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


Quelle  BoundFunctionObject.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 "vm/BoundFunctionObject.h"

#include <string_view>

#include "util/StringBuilder.h"
#include "vm/Interpreter.h"
#include "vm/Shape.h"
#include "vm/Stack.h"

#include "gc/ObjectKind-inl.h"
#include "vm/JSFunction-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/Shape-inl.h"

using namespace js;

// Helper function to initialize `args` with all bound arguments + the arguments
// supplied in `callArgs`.
template <typename Args>
static MOZ_ALWAYS_INLINE void FillArguments(Args& args,
                                            BoundFunctionObject* bound,
                                            size_t numBoundArgs,
                                            const CallArgs& callArgs) {
  MOZ_ASSERT(args.length() == numBoundArgs + callArgs.length());

  if (numBoundArgs <= BoundFunctionObject::MaxInlineBoundArgs) {
    for (size_t i = 0; i < numBoundArgs; i++) {
      args[i].set(bound->getInlineBoundArg(i));
    }
  } else {
    ArrayObject* boundArgs = bound->getBoundArgsArray();
    for (size_t i = 0; i < numBoundArgs; i++) {
      args[i].set(boundArgs->getDenseElement(i));
    }
  }

  for (size_t i = 0; i < callArgs.length(); i++) {
    args[numBoundArgs + i].set(callArgs[i]);
  }
}

// ES2023 10.4.1.1 [[Call]]
// https://tc39.es/ecma262/#sec-bound-function-exotic-objects-call-thisargument-argumentslist
// static
bool BoundFunctionObject::call(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  Rooted<BoundFunctionObject*> bound(cx,
                                     &args.callee().as<BoundFunctionObject>());

  // Step 1.
  Rooted<Value> target(cx, bound->getTargetVal());

  // Step 2.
  Rooted<Value> boundThis(cx, bound->getBoundThis());

  // Steps 3-4.
  size_t numBoundArgs = bound->numBoundArgs();
  InvokeArgs args2(cx);
  if (!args2.init(cx, uint64_t(numBoundArgs) + args.length())) {
    return false;
  }
  FillArguments(args2, bound, numBoundArgs, args);

  // Step 5.
  return Call(cx, target, boundThis, args2, args.rval());
}

// ES2023 10.4.1.2 [[Construct]]
// https://tc39.es/ecma262/#sec-bound-function-exotic-objects-construct-argumentslist-newtarget
// static
bool BoundFunctionObject::construct(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  Rooted<BoundFunctionObject*> bound(cx,
                                     &args.callee().as<BoundFunctionObject>());

  MOZ_ASSERT(bound->isConstructor(),
             "shouldn't have called this hook if not a constructor");

  // Step 1.
  Rooted<Value> target(cx, bound->getTargetVal());

  // Step 2.
  MOZ_ASSERT(IsConstructor(target));

  // Steps 3-4.
  size_t numBoundArgs = bound->numBoundArgs();
  ConstructArgs args2(cx);
  if (!args2.init(cx, uint64_t(numBoundArgs) + args.length())) {
    return false;
  }
  FillArguments(args2, bound, numBoundArgs, args);

  // Step 5.
  Rooted<Value> newTarget(cx, args.newTarget());
  if (newTarget == ObjectValue(*bound)) {
    newTarget = target;
  }

  // Step 6.
  Rooted<JSObject*> res(cx);
  if (!Construct(cx, target, args2, newTarget, &res)) {
    return false;
  }
  args.rval().setObject(*res);
  return true;
}

// static
JSString* BoundFunctionObject::funToString(JSContext* cx, Handle<JSObject*> obj,
                                           bool isToSource) {
  // Implementation of the funToString hook used by Function.prototype.toString.

  // For the non-standard toSource extension, we include "bound" to indicate
  // it's a bound function.
  if (isToSource) {
    static constexpr std::string_view nativeCodeBound =
        "function bound() {\n [native code]\n}";
    return NewStringCopy<CanGC>(cx, nativeCodeBound);
  }

  static constexpr std::string_view nativeCode =
      "function() {\n [native code]\n}";
  return NewStringCopy<CanGC>(cx, nativeCode);
}

// static
SharedShape* BoundFunctionObject::assignInitialShape(
    JSContext* cx, Handle<BoundFunctionObject*> obj) {
  MOZ_ASSERT(obj->empty());

  constexpr PropertyFlags propFlags = {PropertyFlag::Configurable};
  if (!NativeObject::addPropertyInReservedSlot(cx, obj, cx->names().length,
                                               LengthSlot, propFlags)) {
    return nullptr;
  }
  if (!NativeObject::addPropertyInReservedSlot(cx, obj, cx->names().name,
                                               NameSlot, propFlags)) {
    return nullptr;
  }

  SharedShape* shape = obj->sharedShape();
  if (shape->proto() == TaggedProto(&cx->global()->getFunctionPrototype())) {
    cx->global()->setBoundFunctionShapeWithDefaultProto(shape);
  }
  return shape;
}

static MOZ_ALWAYS_INLINE bool ComputeLengthValue(
    JSContext* cx, Handle<BoundFunctionObject*> bound, Handle<JSObject*> target,
    size_t numBoundArgs, double* length) {
  *length = 0.0;

  // Try to avoid invoking the JSFunction resolve hook.
  if (target->is<JSFunction>() &&
      !target->as<JSFunction>().hasResolvedLength()) {
    uint16_t targetLength;
    if (!JSFunction::getUnresolvedLength(cx, target.as<JSFunction>(),
                                         &targetLength)) {
      return false;
    }

    if (size_t(targetLength) > numBoundArgs) {
      *length = size_t(targetLength) - numBoundArgs;
    }
    return true;
  }

  // Use a fast path for getting the .length value if the target is a bound
  // function with its initial shape.
  Value targetLength;
  if (target->is<BoundFunctionObject>() && target->shape() == bound->shape()) {
    BoundFunctionObject* targetFn = &target->as<BoundFunctionObject>();
    targetLength = targetFn->getLengthForInitialShape();
  } else {
    bool hasLength;
    Rooted<PropertyKey> key(cx, NameToId(cx->names().length));
    if (!HasOwnProperty(cx, target, key, &hasLength)) {
      return false;
    }

    if (!hasLength) {
      return true;
    }

    Rooted<Value> targetLengthRoot(cx);
    if (!GetProperty(cx, target, target, key, &targetLengthRoot)) {
      return false;
    }
    targetLength = targetLengthRoot;
  }

  if (targetLength.isNumber()) {
    *length = std::max(
        0.0, JS::ToInteger(targetLength.toNumber()) - double(numBoundArgs));
  }
  return true;
}

static MOZ_ALWAYS_INLINE JSAtom* AppendBoundFunctionPrefix(JSContext* cx,
                                                           JSString* str) {
  auto& cache = cx->zone()->boundPrefixCache();

  JSAtom* strAtom = str->isAtom() ? &str->asAtom() : nullptr;
  if (strAtom) {
    if (auto p = cache.lookup(strAtom)) {
      return p->value();
    }
  }

  StringBuilder sb(cx);
  if (!sb.append("bound ") || !sb.append(str)) {
    return nullptr;
  }
  JSAtom* atom = sb.finishAtom();
  if (!atom) {
    return nullptr;
  }

  if (strAtom) {
    (void)cache.putNew(strAtom, atom);
  }
  return atom;
}

static MOZ_ALWAYS_INLINE JSAtom* ComputeNameValue(
    JSContext* cx, Handle<BoundFunctionObject*> bound,
    Handle<JSObject*> target) {
  // Try to avoid invoking the JSFunction resolve hook.
  JSString* name = nullptr;
  if (target->is<JSFunction>() && !target->as<JSFunction>().hasResolvedName()) {
    JSFunction* targetFn = &target->as<JSFunction>();
    name = targetFn->getUnresolvedName(cx);
    if (!name) {
      return nullptr;
    }
  } else {
    // Use a fast path for getting the .name value if the target is a bound
    // function with its initial shape.
    Value targetName;
    if (target->is<BoundFunctionObject>() &&
        target->shape() == bound->shape()) {
      BoundFunctionObject* targetFn = &target->as<BoundFunctionObject>();
      targetName = targetFn->getNameForInitialShape();
    } else {
      Rooted<Value> targetNameRoot(cx);
      if (!GetProperty(cx, target, target, cx->names().name, &targetNameRoot)) {
        return nullptr;
      }
      targetName = targetNameRoot;
    }
    if (!targetName.isString()) {
      return cx->names().boundWithSpace_;
    }
    name = targetName.toString();
  }

  return AppendBoundFunctionPrefix(cx, name);
}

// ES2023 20.2.3.2 Function.prototype.bind
// https://tc39.es/ecma262/#sec-function.prototype.bind
// static
bool BoundFunctionObject::functionBind(JSContext* cx, unsigned argc,
                                       Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // Steps 1-2.
  if (!IsCallable(args.thisv())) {
    ReportIncompatibleMethod(cx, args, &FunctionClass);
    return false;
  }

  if (MOZ_UNLIKELY(args.length() > ARGS_LENGTH_MAX)) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_TOO_MANY_ARGUMENTS);
    return false;
  }

  Rooted<JSObject*> target(cx, &args.thisv().toObject());

  BoundFunctionObject* bound =
      functionBindImpl(cx, target, args.array(), args.length(), nullptr);
  if (!bound) {
    return false;
  }

  // Step 11.
  args.rval().setObject(*bound);
  return true;
}

// ES2023 20.2.3.2 Function.prototype.bind
// https://tc39.es/ecma262/#sec-function.prototype.bind
//
// ES2023 10.4.1.3 BoundFunctionCreate
// https://tc39.es/ecma262/#sec-boundfunctioncreate
//
// BoundFunctionCreate has been inlined in Function.prototype.bind for
// performance reasons.
//
// static
BoundFunctionObject* BoundFunctionObject::functionBindImpl(
    JSContext* cx, Handle<JSObject*> target, Value* args, uint32_t argc,
    Handle<BoundFunctionObject*> maybeBound) {
  MOZ_ASSERT(target->isCallable());

  // Make sure the arguments on the stack are rooted when we're called directly
  // from JIT code.
  RootedExternalValueArray argsRoot(cx, argc, args);

  size_t numBoundArgs = argc > 0 ? argc - 1 : 0;
  MOZ_ASSERT(numBoundArgs <= ARGS_LENGTH_MAX, "ensured by callers");

  // If this assertion fails, make sure we use the correct AllocKind and that we
  // use all of its slots (consider increasing MaxInlineBoundArgs).
  static_assert(gc::GetGCKindSlots(allocKind) == SlotCount);

  // ES2023 10.4.1.3 BoundFunctionCreate
  // Steps 1-5.
  Rooted<BoundFunctionObject*> bound(cx);
  if (maybeBound) {
    // We allocated a bound function in JIT code. In the uncommon case of the
    // target not having Function.prototype as proto, we have to set the right
    // proto here.
    bound = maybeBound;
    if (MOZ_UNLIKELY(bound->staticPrototype() != target->staticPrototype())) {
      Rooted<JSObject*> proto(cx, target->staticPrototype());
      if (!SetPrototype(cx, bound, proto)) {
        return nullptr;
      }
    }
  } else {
    // Step 1.
    Rooted<JSObject*> proto(cx);
    if (!GetPrototype(cx, target, &proto)) {
      return nullptr;
    }

    // Steps 2-5.
    if (proto == &cx->global()->getFunctionPrototype() &&
        cx->global()->maybeBoundFunctionShapeWithDefaultProto()) {
      Rooted<SharedShape*> shape(
          cx, cx->global()->maybeBoundFunctionShapeWithDefaultProto());
      bound = NativeObject::create<BoundFunctionObject>(
          cx, allocKind, gc::Heap::Default, shape);
      if (!bound) {
        return nullptr;
      }
    } else {
      bound = NewObjectWithGivenProto<BoundFunctionObject>(cx, proto);
      if (!bound) {
        return nullptr;
      }
      if (!SharedShape::ensureInitialCustomShape<BoundFunctionObject>(cx,
                                                                      bound)) {
        return nullptr;
      }
    }
  }

  MOZ_ASSERT(bound->lookupPure(cx->names().length)->slot() == LengthSlot);
  MOZ_ASSERT(bound->lookupPure(cx->names().name)->slot() == NameSlot);

  // Steps 6 and 9.
  bound->initFlags(numBoundArgs, target->isConstructor());

  // Step 7.
  bound->initReservedSlot(TargetSlot, ObjectValue(*target));

  // Step 8.
  if (argc > 0) {
    bound->initReservedSlot(BoundThisSlot, args[0]);
  }

  if (numBoundArgs <= MaxInlineBoundArgs) {
    for (size_t i = 0; i < numBoundArgs; i++) {
      bound->initReservedSlot(BoundArg0Slot + i, args[i + 1]);
    }
  } else {
    ArrayObject* arr = NewDenseCopiedArray(cx, numBoundArgs, args + 1);
    if (!arr) {
      return nullptr;
    }
    bound->initReservedSlot(BoundArg0Slot, ObjectValue(*arr));
  }

  // ES2023 20.2.3.2 Function.prototype.bind
  // Step 4.
  double length = 0.0;

  // Steps 5-6.
  if (!ComputeLengthValue(cx, bound, target, numBoundArgs, &length)) {
    return nullptr;
  }

  // Step 7.
  bound->initLength(length);

  // Steps 8-9.
  JSAtom* name = ComputeNameValue(cx, bound, target);
  if (!name) {
    return nullptr;
  }

  // Step 10.
  bound->initName(name);

  // Step 11.
  return bound;
}

// static
BoundFunctionObject* BoundFunctionObject::createWithTemplate(
    JSContext* cx, Handle<BoundFunctionObject*> templateObj) {
  Rooted<SharedShape*> shape(cx, templateObj->sharedShape());
  auto* bound = NativeObject::create<BoundFunctionObject>(
      cx, allocKind, gc::Heap::Default, shape);
  if (!bound) {
    return nullptr;
  }
  bound->initFlags(templateObj->numBoundArgs(), templateObj->isConstructor());
  bound->initLength(templateObj->getLengthForInitialShape().toInt32());
  bound->initName(&templateObj->getNameForInitialShape().toString()->asAtom());
  return bound;
}

// static
BoundFunctionObject* BoundFunctionObject::functionBindSpecializedBaseline(
    JSContext* cx, Handle<JSObject*> target, Value* args, uint32_t argc,
    Handle<BoundFunctionObject*> templateObj) {
  // Root the Values on the stack.
  RootedExternalValueArray argsRoot(cx, argc, args);

  MOZ_ASSERT(target->is<JSFunction>() || target->is<BoundFunctionObject>());
  MOZ_ASSERT(target->isCallable());
  MOZ_ASSERT(target->isConstructor() == templateObj->isConstructor());
  MOZ_ASSERT(target->staticPrototype() == templateObj->staticPrototype());

  size_t numBoundArgs = argc > 0 ? argc - 1 : 0;
  MOZ_ASSERT(numBoundArgs <= MaxInlineBoundArgs);

  BoundFunctionObject* bound = createWithTemplate(cx, templateObj);
  if (!bound) {
    return nullptr;
  }

  MOZ_ASSERT(bound->lookupPure(cx->names().length)->slot() == LengthSlot);
  MOZ_ASSERT(bound->lookupPure(cx->names().name)->slot() == NameSlot);

  bound->initReservedSlot(TargetSlot, ObjectValue(*target));
  if (argc > 0) {
    bound->initReservedSlot(BoundThisSlot, args[0]);
  }
  for (size_t i = 0; i < numBoundArgs; i++) {
    bound->initReservedSlot(BoundArg0Slot + i, args[i + 1]);
  }
  return bound;
}

// static
BoundFunctionObject* BoundFunctionObject::createTemplateObject(JSContext* cx) {
  Rooted<JSObject*> proto(cx, &cx->global()->getFunctionPrototype());
  Rooted<BoundFunctionObject*> bound(
      cx, NewTenuredObjectWithGivenProto<BoundFunctionObject>(cx, proto));
  if (!bound) {
    return nullptr;
  }
  if (!SharedShape::ensureInitialCustomShape<BoundFunctionObject>(cx, bound)) {
    return nullptr;
  }
  return bound;
}

bool BoundFunctionObject::initTemplateSlotsForSpecializedBind(
    JSContext* cx, uint32_t numBoundArgs, bool targetIsConstructor,
    uint32_t targetLength, JSAtom* targetName) {
  size_t len = 0;
  if (targetLength > numBoundArgs) {
    len = targetLength - numBoundArgs;
  }

  JSAtom* name = AppendBoundFunctionPrefix(cx, targetName);
  if (!name) {
    return false;
  }

  initFlags(numBoundArgs, targetIsConstructor);
  initLength(len);
  initName(name);
  return true;
}

static const JSClassOps classOps = {
    nullptr,                         // addProperty
    nullptr,                         // delProperty
    nullptr,                         // enumerate
    nullptr,                         // newEnumerate
    nullptr,                         // resolve
    nullptr,                         // mayResolve
    nullptr,                         // finalize
    BoundFunctionObject::call,       // call
    BoundFunctionObject::construct,  // construct
    nullptr,                         // trace
};

static const ObjectOps objOps = {
    nullptr,                           // lookupProperty
    nullptr,                           // qdefineProperty
    nullptr,                           // hasProperty
    nullptr,                           // getProperty
    nullptr,                           // setProperty
    nullptr,                           // getOwnPropertyDescriptor
    nullptr,                           // deleteProperty
    nullptr,                           // getElements
    BoundFunctionObject::funToString,  // funToString
};

const JSClass BoundFunctionObject::class_ = {
    "BoundFunctionObject",
    // Note: bound functions don't have their own constructor or prototype (they
    // use the prototype of the target object), but we give them a JSProtoKey
    // because that's what Xray wrappers use to identify builtin objects.
    JSCLASS_HAS_CACHED_PROTO(JSProto_BoundFunction) |
        JSCLASS_HAS_RESERVED_SLOTS(BoundFunctionObject::SlotCount),
    &classOps,
    JS_NULL_CLASS_SPEC,
    JS_NULL_CLASS_EXT,
    &objOps,
};

Messung V0.5
C=89 H=96 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