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


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


#include "builtin/FinalizationRegistryObject.h"
#include "gc/GC.h"
#include "gc/PublicIterators.h"
#include "js/friend/WindowProxy.h"  // js::IsWindow, js::IsWindowProxy
#include "js/Wrapper.h"
#include "proxy/DeadObjectProxy.h"
#include "proxy/DOMProxy.h"
#include "vm/Iteration.h"
#include "vm/Runtime.h"
#include "vm/WrapperObject.h"

#include "gc/Nursery-inl.h"
#include "vm/Compartment-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/Realm-inl.h"

using namespace js;

#define PIERCE(cx, wrapper, pre, op, post)        \
  JS_BEGIN_MACRO                                  \
    bool ok;                                      \
    {                                             \
      AutoRealm call(cx, wrappedObject(wrapper)); \
      ok = (pre) && (op);                         \
    }                                             \
    return ok && (post);                          \
  JS_END_MACRO

#define NOTHING (true)

static bool MarkAtoms(JSContext* cx, jsid id) {
  cx->markId(id);
  return true;
}

static bool MarkAtoms(JSContext* cx, HandleIdVector ids) {
  for (size_t i = 0; i < ids.length(); i++) {
    cx->markId(ids[i]);
  }
  return true;
}

bool CrossCompartmentWrapper::getOwnPropertyDescriptor(
    JSContext* cx, HandleObject wrapper, HandleId id,
    MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) const {
  PIERCE(cx, wrapper, MarkAtoms(cx, id),
         Wrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc),
         cx->compartment()->wrap(cx, desc));
}

bool CrossCompartmentWrapper::defineProperty(JSContext* cx,
                                             HandleObject wrapper, HandleId id,
                                             Handle<PropertyDescriptor> desc,
                                             ObjectOpResult& result) const {
  Rooted<PropertyDescriptor> desc2(cx, desc);
  PIERCE(cx, wrapper, MarkAtoms(cx, id) && cx->compartment()->wrap(cx, &desc2),
         Wrapper::defineProperty(cx, wrapper, id, desc2, result), NOTHING);
}

bool CrossCompartmentWrapper::ownPropertyKeys(
    JSContext* cx, HandleObject wrapper, MutableHandleIdVector props) const {
  PIERCE(cx, wrapper, NOTHING, Wrapper::ownPropertyKeys(cx, wrapper, props),
         MarkAtoms(cx, props));
}

bool CrossCompartmentWrapper::delete_(JSContext* cx, HandleObject wrapper,
                                      HandleId id,
                                      ObjectOpResult& result) const {
  PIERCE(cx, wrapper, MarkAtoms(cx, id),
         Wrapper::delete_(cx, wrapper, id, result), NOTHING);
}

bool CrossCompartmentWrapper::getPrototype(JSContext* cx, HandleObject wrapper,
                                           MutableHandleObject protop) const {
  {
    RootedObject wrapped(cx, wrappedObject(wrapper));
    AutoRealm call(cx, wrapped);
    if (!GetPrototype(cx, wrapped, protop)) {
      return false;
    }
  }

  return cx->compartment()->wrap(cx, protop);
}

bool CrossCompartmentWrapper::setPrototype(JSContext* cx, HandleObject wrapper,
                                           HandleObject proto,
                                           ObjectOpResult& result) const {
  RootedObject protoCopy(cx, proto);
  PIERCE(cx, wrapper, cx->compartment()->wrap(cx, &protoCopy),
         Wrapper::setPrototype(cx, wrapper, protoCopy, result), NOTHING);
}

bool CrossCompartmentWrapper::getPrototypeIfOrdinary(
    JSContext* cx, HandleObject wrapper, bool* isOrdinary,
    MutableHandleObject protop) const {
  {
    RootedObject wrapped(cx, wrappedObject(wrapper));
    AutoRealm call(cx, wrapped);
    if (!GetPrototypeIfOrdinary(cx, wrapped, isOrdinary, protop)) {
      return false;
    }

    if (!*isOrdinary) {
      return true;
    }
  }

  return cx->compartment()->wrap(cx, protop);
}

bool CrossCompartmentWrapper::setImmutablePrototype(JSContext* cx,
                                                    HandleObject wrapper,
                                                    bool* succeeded) const {
  PIERCE(cx, wrapper, NOTHING,
         Wrapper::setImmutablePrototype(cx, wrapper, succeeded), NOTHING);
}

bool CrossCompartmentWrapper::preventExtensions(JSContext* cx,
                                                HandleObject wrapper,
                                                ObjectOpResult& result) const {
  PIERCE(cx, wrapper, NOTHING, Wrapper::preventExtensions(cx, wrapper, result),
         NOTHING);
}

bool CrossCompartmentWrapper::isExtensible(JSContext* cx, HandleObject wrapper,
                                           bool* extensible) const {
  PIERCE(cx, wrapper, NOTHING, Wrapper::isExtensible(cx, wrapper, extensible),
         NOTHING);
}

bool CrossCompartmentWrapper::has(JSContext* cx, HandleObject wrapper,
                                  HandleId id, bool* bp) const {
  PIERCE(cx, wrapper, MarkAtoms(cx, id), Wrapper::has(cx, wrapper, id, bp),
         NOTHING);
}

bool CrossCompartmentWrapper::hasOwn(JSContext* cx, HandleObject wrapper,
                                     HandleId id, bool* bp) const {
  PIERCE(cx, wrapper, MarkAtoms(cx, id), Wrapper::hasOwn(cx, wrapper, id, bp),
         NOTHING);
}

static bool WrapReceiver(JSContext* cx, HandleObject wrapper,
                         MutableHandleValue receiver) {
  // Usually the receiver is the wrapper and we can just unwrap it. If the
  // wrapped object is also a wrapper, things are more complicated and we
  // fall back to the slow path (it calls UncheckedUnwrap to unwrap all
  // wrappers).
  if (ObjectValue(*wrapper) == receiver) {
    JSObject* wrapped = Wrapper::wrappedObject(wrapper);
    if (!IsWrapper(wrapped)) {
      MOZ_ASSERT(wrapped->compartment() == cx->compartment());
      MOZ_ASSERT(!IsWindow(wrapped));
      receiver.setObject(*wrapped);
      return true;
    }
  }

  return cx->compartment()->wrap(cx, receiver);
}

bool CrossCompartmentWrapper::get(JSContext* cx, HandleObject wrapper,
                                  HandleValue receiver, HandleId id,
                                  MutableHandleValue vp) const {
  RootedValue receiverCopy(cx, receiver);
  {
    AutoRealm call(cx, wrappedObject(wrapper));
    if (!MarkAtoms(cx, id) || !WrapReceiver(cx, wrapper, &receiverCopy)) {
      return false;
    }

    if (!Wrapper::get(cx, wrapper, receiverCopy, id, vp)) {
      return false;
    }
  }
  return cx->compartment()->wrap(cx, vp);
}

bool CrossCompartmentWrapper::set(JSContext* cx, HandleObject wrapper,
                                  HandleId id, HandleValue v,
                                  HandleValue receiver,
                                  ObjectOpResult& result) const {
  RootedValue valCopy(cx, v);
  RootedValue receiverCopy(cx, receiver);
  PIERCE(cx, wrapper,
         MarkAtoms(cx, id) && cx->compartment()->wrap(cx, &valCopy) &&
             WrapReceiver(cx, wrapper, &receiverCopy),
         Wrapper::set(cx, wrapper, id, valCopy, receiverCopy, result), NOTHING);
}

bool CrossCompartmentWrapper::getOwnEnumerablePropertyKeys(
    JSContext* cx, HandleObject wrapper, MutableHandleIdVector props) const {
  PIERCE(cx, wrapper, NOTHING,
         Wrapper::getOwnEnumerablePropertyKeys(cx, wrapper, props),
         MarkAtoms(cx, props));
}

bool CrossCompartmentWrapper::enumerate(JSContext* cx, HandleObject wrapper,
                                        MutableHandleIdVector props) const {
  PIERCE(cx, wrapper, NOTHING, Wrapper::enumerate(cx, wrapper, props),
         MarkAtoms(cx, props));
}

bool CrossCompartmentWrapper::call(JSContext* cx, HandleObject wrapper,
                                   const CallArgs& args) const {
  RootedObject wrapped(cx, wrappedObject(wrapper));

  {
    AutoRealm call(cx, wrapped);

    args.setCallee(ObjectValue(*wrapped));
    if (!cx->compartment()->wrap(cx, args.mutableThisv())) {
      return false;
    }

    for (size_t n = 0; n < args.length(); ++n) {
      if (!cx->compartment()->wrap(cx, args[n])) {
        return false;
      }
    }

    if (!Wrapper::call(cx, wrapper, args)) {
      return false;
    }
  }

  return cx->compartment()->wrap(cx, args.rval());
}

bool CrossCompartmentWrapper::construct(JSContext* cx, HandleObject wrapper,
                                        const CallArgs& args) const {
  RootedObject wrapped(cx, wrappedObject(wrapper));
  {
    AutoRealm call(cx, wrapped);

    for (size_t n = 0; n < args.length(); ++n) {
      if (!cx->compartment()->wrap(cx, args[n])) {
        return false;
      }
    }
    if (!cx->compartment()->wrap(cx, args.newTarget())) {
      return false;
    }
    if (!Wrapper::construct(cx, wrapper, args)) {
      return false;
    }
  }
  return cx->compartment()->wrap(cx, args.rval());
}

bool CrossCompartmentWrapper::nativeCall(JSContext* cx, IsAcceptableThis test,
                                         NativeImpl impl,
                                         const CallArgs& srcArgs) const {
  RootedObject wrapper(cx, &srcArgs.thisv().toObject());
  MOZ_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) ||
             !UncheckedUnwrap(wrapper)->is<CrossCompartmentWrapperObject>());

  RootedObject wrapped(cx, wrappedObject(wrapper));
  {
    AutoRealm call(cx, wrapped);
    InvokeArgs dstArgs(cx);
    if (!dstArgs.init(cx, srcArgs.length())) {
      return false;
    }

    Value* src = srcArgs.base();
    Value* srcend = srcArgs.array() + srcArgs.length();
    Value* dst = dstArgs.base();

    RootedValue source(cx);
    for (; src < srcend; ++src, ++dst) {
      source = *src;
      if (!cx->compartment()->wrap(cx, &source)) {
        return false;
      }
      *dst = source.get();

      // Handle |this| specially. When we rewrap on the other side of the
      // membrane, we might apply a same-compartment security wrapper that
      // will stymie this whole process. If that happens, unwrap the wrapper.
      // This logic can go away when same-compartment security wrappers go away.
      if ((src == srcArgs.base() + 1) && dst->isObject()) {
        RootedObject thisObj(cx, &dst->toObject());
        if (thisObj->is<WrapperObject>() &&
            Wrapper::wrapperHandler(thisObj)->hasSecurityPolicy()) {
          MOZ_ASSERT(!thisObj->is<CrossCompartmentWrapperObject>());
          *dst = ObjectValue(*Wrapper::wrappedObject(thisObj));
        }
      }
    }

    if (!CallNonGenericMethod(cx, test, impl, dstArgs)) {
      return false;
    }

    srcArgs.rval().set(dstArgs.rval());
  }
  return cx->compartment()->wrap(cx, srcArgs.rval());
}

const char* CrossCompartmentWrapper::className(JSContext* cx,
                                               HandleObject wrapper) const {
  AutoRealm call(cx, wrappedObject(wrapper));
  return Wrapper::className(cx, wrapper);
}

JSString* CrossCompartmentWrapper::fun_toString(JSContext* cx,
                                                HandleObject wrapper,
                                                bool isToSource) const {
  RootedString str(cx);
  {
    AutoRealm call(cx, wrappedObject(wrapper));
    str = Wrapper::fun_toString(cx, wrapper, isToSource);
    if (!str) {
      return nullptr;
    }
  }
  if (!cx->compartment()->wrap(cx, &str)) {
    return nullptr;
  }
  return str;
}

RegExpShared* CrossCompartmentWrapper::regexp_toShared(
    JSContext* cx, HandleObject wrapper) const {
  RootedRegExpShared re(cx);
  {
    AutoRealm call(cx, wrappedObject(wrapper));
    re = Wrapper::regexp_toShared(cx, wrapper);
    if (!re) {
      return nullptr;
    }
  }

  // Get an equivalent RegExpShared associated with the current compartment.
  Rooted<JSAtom*> source(cx, re->getSource());
  cx->markAtom(source);
  return cx->zone()->regExps().get(cx, source, re->getFlags());
}

bool CrossCompartmentWrapper::boxedValue_unbox(JSContext* cx,
                                               HandleObject wrapper,
                                               MutableHandleValue vp) const {
  PIERCE(cx, wrapper, NOTHING, Wrapper::boxedValue_unbox(cx, wrapper, vp),
         cx->compartment()->wrap(cx, vp));
}

const CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);

JS_PUBLIC_API void js::NukeCrossCompartmentWrapper(JSContext* cx,
                                                   JSObject* wrapper) {
  JS::Compartment* comp = wrapper->compartment();
  auto ptr = comp->lookupWrapper(Wrapper::wrappedObject(wrapper));
  if (ptr) {
    comp->removeWrapper(ptr);
  }
  NukeRemovedCrossCompartmentWrapper(cx, wrapper);
}

JS_PUBLIC_API void js::NukeCrossCompartmentWrapperIfExists(
    JSContext* cx, JS::Compartment* source, JSObject* target) {
  MOZ_ASSERT(source != target->compartment());
  MOZ_ASSERT(!target->is<CrossCompartmentWrapperObject>());
  auto ptr = source->lookupWrapper(target);
  if (ptr) {
    JSObject* wrapper = ptr->value().get();
    NukeCrossCompartmentWrapper(cx, wrapper);
  }
}

// Returns true iff all realms in the compartment have been nuked.
static bool NukedAllRealms(JS::Compartment* comp) {
  for (RealmsInCompartmentIter realm(comp); !realm.done(); realm.next()) {
    if (!realm->nukedIncomingWrappers) {
      return false;
    }
  }
  return true;
}

/*
 * NukeCrossCompartmentWrappers invalidates all cross-compartment wrappers
 * that point to objects in the |target| realm.
 *
 * There is some complexity in targeting to preserve semantics which requires
 * the filtering and behavioural options:
 *
 * - |sourceFilter| limits the compartments searched for source pointers
 * - |nukeReferencesToWindow| will, if set to DontNukeWindowReferences skip
 *   wrappers whose target is the window proxy of the target realm.
 * - |nukeReferencesFromTarget| will, when set to NukeAllReferences, disallow
 *   the creation of new wrappers to the target realm. This option can also
 *   allow more wrappers to be cleaned up transitively.
 */

JS_PUBLIC_API bool js::NukeCrossCompartmentWrappers(
    JSContext* cx, const CompartmentFilter& sourceFilter, JS::Realm* target,
    js::NukeReferencesToWindow nukeReferencesToWindow,
    js::NukeReferencesFromTarget nukeReferencesFromTarget) {
  CHECK_THREAD(cx);
  JSRuntime* rt = cx->runtime();

  // If we're nuking all wrappers into the target realm, prevent us from
  // creating new wrappers for it in the future.
  if (nukeReferencesFromTarget == NukeAllReferences) {
    target->nukedIncomingWrappers = true;
  }

  for (CompartmentsIter c(rt); !c.done(); c.next()) {
    if (!sourceFilter.match(c)) {
      continue;
    }

    // If the realm matches both the source and target filter, we may want to
    // cut outgoing wrappers too, if we nuked all realms in the compartment.
    bool nukeAll =
        (nukeReferencesFromTarget == NukeAllReferences &&
         target->compartment() == c.get() && NukedAllRealms(c.get()));

    // Iterate only the wrappers that have target compartment matched unless
    // |nukeAll| is true. Use Maybe to avoid copying from conditionally
    // initializing ObjectWrapperEnum.
    mozilla::Maybe<Compartment::ObjectWrapperEnum> e;
    if (MOZ_LIKELY(!nukeAll)) {
      e.emplace(c, target->compartment());
    } else {
      e.emplace(c);
      c.get()->nukedOutgoingWrappers = true;
    }
    for (; !e->empty(); e->popFront()) {
      JSObject* key = e->front().key();

      AutoWrapperRooter wobj(cx, WrapperValue(*e));

      // Unwrap from the wrapped object in key instead of the wrapper, this
      // could save us a bit of time.
      JSObject* wrapped = UncheckedUnwrap(key);

      // Don't nuke wrappers for objects in other realms in the target
      // compartment unless nukeAll is set because in that case we want to nuke
      // all outgoing wrappers for the current compartment.
      if (!nukeAll && wrapped->nonCCWRealm() != target) {
        continue;
      }

      // We only skip nuking window references that point to a target
      // compartment, not the ones that belong to it.
      if (nukeReferencesToWindow == DontNukeWindowReferences &&
          MOZ_LIKELY(!nukeAll) && IsWindowProxy(wrapped)) {
        continue;
      }

      // Now this is the wrapper we want to nuke.
      e->removeFront();
      NukeRemovedCrossCompartmentWrapper(cx, wobj);
    }
  }

  return true;
}

JS_PUBLIC_API bool js::AllowNewWrapper(JS::Compartment* target, JSObject* obj) {
  // Disallow creating new wrappers if we nuked the object realm or target
  // compartment.

  MOZ_ASSERT(obj->compartment() != target);

  if (target->nukedOutgoingWrappers ||
      obj->nonCCWRealm()->nukedIncomingWrappers) {
    return false;
  }

  return true;
}

JS_PUBLIC_API bool js::NukedObjectRealm(JSObject* obj) {
  return obj->nonCCWRealm()->nukedIncomingWrappers;
}

// Given a cross-compartment wrapper |wobj|, update it to point to
// |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
// useful even if wrapper already points to newTarget.
// This operation crashes on failure rather than leaving the heap in an
// inconsistent state.
void js::RemapWrapper(JSContext* cx, JSObject* wobjArg,
                      JSObject* newTargetArg) {
  RootedObject wobj(cx, wobjArg);
  RootedObject newTarget(cx, newTargetArg);
  MOZ_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
  MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
  JSObject* origTarget = Wrapper::wrappedObject(wobj);
  MOZ_ASSERT(origTarget);
  JS::Compartment* wcompartment = wobj->compartment();
  MOZ_ASSERT(wcompartment != newTarget->compartment());

  AutoDisableProxyCheck adpc;

  // If we're mapping to a different target (as opposed to just recomputing
  // for the same target), we must not have an existing wrapper for the new
  // target, otherwise this will break.
  MOZ_ASSERT_IF(origTarget != newTarget,
                !wcompartment->lookupWrapper(newTarget));

  // The old value should still be in the cross-compartment wrapper map, and
  // the lookup should return wobj.
  ObjectWrapperMap::Ptr p = wcompartment->lookupWrapper(origTarget);
  MOZ_ASSERT(*p->value().unsafeGet() == wobj);
  wcompartment->removeWrapper(p);

  // When we remove origv from the wrapper map, its wrapper, wobj, must
  // immediately cease to be a cross-compartment wrapper. Nuke it.
  NukeCrossCompartmentWrapper(cx, wobj);

  // If the target is a dead wrapper, and we're just fixing wrappers for
  // it, then we're done now that the CCW is a dead wrapper.
  if (JS_IsDeadWrapper(origTarget)) {
    MOZ_RELEASE_ASSERT(origTarget == newTarget);
    return;
  }

  js::RemapDeadWrapper(cx, wobj, newTarget);
}

// Given a dead proxy object |wobj|, turn it into a cross-compartment wrapper
// pointing at |newTarget|.
// This operation crashes on failure rather than leaving the heap in an
// inconsistent state.
void js::RemapDeadWrapper(JSContext* cx, HandleObject wobj,
                          HandleObject newTarget) {
  MOZ_ASSERT(IsDeadProxyObject(wobj));
  MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());

  // These are not exposed. Doing this would require updating the
  // FinalizationObservers data structures.
  MOZ_ASSERT(!newTarget->is<FinalizationRecordObject>());

  AutoDisableProxyCheck adpc;

  // wobj is not a cross-compartment wrapper, so we can use nonCCWRealm.
  Realm* wrealm = wobj->nonCCWRealm();

  // First, we wrap it in the new compartment. We try to use the existing
  // wrapper, |wobj|, since it's been nuked anyway. The rewrap() function has
  // the choice to reuse |wobj| or not.
  RootedObject tobj(cx, newTarget);
  AutoRealmUnchecked ar(cx, wrealm);
  AutoEnterOOMUnsafeRegion oomUnsafe;
  JS::Compartment* wcompartment = wobj->compartment();
  if (!wcompartment->rewrap(cx, &tobj, wobj)) {
    oomUnsafe.crash("js::RemapWrapper");
  }

  // If rewrap() reused |wobj|, it will have overwritten it and returned with
  // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj|
  // will still be nuked. In the latter case, we replace |wobj| with the
  // contents of the new wrapper in |tobj|.
  if (tobj != wobj) {
    // Now, because we need to maintain object identity, we do a brain
    // transplant on the old object so that it contains the contents of the
    // new one.
    JSObject::swap(cx, wobj, tobj, oomUnsafe);
  }

  if (!wobj->is<WrapperObject>()) {
    MOZ_ASSERT(js::IsDOMRemoteProxyObject(wobj) || IsDeadProxyObject(wobj));
    return;
  }

  // Before swapping, this wrapper came out of rewrap(), which enforces the
  // invariant that the wrapper in the map points directly to the key.
  MOZ_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);

  // Update the entry in the compartment's wrapper map to point to the old
  // wrapper, which has now been updated (via reuse or swap).
  if (!wcompartment->putWrapper(cx, newTarget, wobj)) {
    oomUnsafe.crash("js::RemapWrapper");
  }
}

// Remap all cross-compartment wrappers pointing to |oldTarget| to point to
// |newTarget|. All wrappers are recomputed.
JS_PUBLIC_API bool js::RemapAllWrappersForObject(JSContext* cx,
                                                 HandleObject oldTarget,
                                                 HandleObject newTarget) {
  AutoWrapperVector toTransplant(cx);

  for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
    if (ObjectWrapperMap::Ptr wp = c->lookupWrapper(oldTarget)) {
      // We found a wrapper. Remember and root it.
      if (!toTransplant.append(WrapperValue(wp))) {
        return false;
      }
    }
  }

  for (const WrapperValue& v : toTransplant) {
    RemapWrapper(cx, v, newTarget);
  }

  return true;
}

JS_PUBLIC_API bool js::RecomputeWrappers(
    JSContext* cx, const CompartmentFilter& sourceFilter,
    const CompartmentFilter& targetFilter) {
  bool evictedNursery = false;

  AutoWrapperVector toRecompute(cx);
  for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
    // Filter by source compartment.
    if (!sourceFilter.match(c)) {
      continue;
    }

    if (!evictedNursery &&
        c->hasNurseryAllocatedObjectWrapperEntries(targetFilter)) {
      cx->runtime()->gc.evictNursery();
      evictedNursery = true;
    }

    // Iterate over object wrappers, filtering appropriately.
    for (Compartment::ObjectWrapperEnum e(c, targetFilter); !e.empty();
         e.popFront()) {
      // Don't remap wrappers to finalization record objects. These are used
      // internally and are not exposed.
      JSObject* wrapper = *e.front().value().unsafeGet();
      if (Wrapper::wrappedObject(wrapper)->is<FinalizationRecordObject>()) {
        continue;
      }

      // Add the wrapper to the list.
      if (!toRecompute.append(WrapperValue(e))) {
        return false;
      }
    }
  }

  // Recompute all the wrappers in the list.
  for (const WrapperValue& wrapper : toRecompute) {
    JSObject* wrapped = Wrapper::wrappedObject(wrapper);
    RemapWrapper(cx, wrapper, wrapped);
  }

  return true;
}

91%


¤ Dauer der Verarbeitung: 0.9 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


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