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


Quelle  Environment.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 "debugger/Environment-inl.h"

#include "mozilla/Assertions.h"  // for AssertionConditionType
#include "mozilla/Maybe.h"       // for Maybe, Some, Nothing
#include "mozilla/Vector.h"      // for Vector

#include <string.h>  // for strlen, size_t
#include <utility>   // for move

#include "debugger/Debugger.h"  // for Env, Debugger, ValueToIdentifier
#include "debugger/Object.h"    // for DebuggerObject
#include "debugger/Script.h"    // for DebuggerScript
#include "gc/Tracer.h"    // for TraceManuallyBarrieredCrossCompartmentEdge
#include "js/CallArgs.h"  // for CallArgs
#include "js/friend/ErrorMessages.h"  // for GetErrorMessage, JSMSG_*
#include "js/HeapAPI.h"               // for IsInsideNursery
#include "js/RootingAPI.h"            // for Rooted, MutableHandle
#include "util/Identifier.h"          // for IsIdentifier
#include "vm/Compartment.h"           // for Compartment
#include "vm/JSAtomUtils.h"           // for Atomize
#include "vm/JSContext.h"             // for JSContext
#include "vm/JSFunction.h"            // for JSFunction
#include "vm/JSObject.h"              // for JSObject, RequireObject,
#include "vm/NativeObject.h"          // for NativeObject, JSObject::is
#include "vm/Realm.h"                 // for AutoRealm, ErrorCopier
#include "vm/Scope.h"                 // for ScopeKind, ScopeKindString
#include "vm/StringType.h"            // for JSAtom

#include "gc/StableCellHasher-inl.h"
#include "vm/Compartment-inl.h"        // for Compartment::wrap
#include "vm/EnvironmentObject-inl.h"  // for JSObject::enclosingEnvironment
#include "vm/JSObject-inl.h"  // for IsInternalFunctionObject, NewObjectWithGivenProtoAndKind
#include "vm/ObjectOperations-inl.h"  // for HasProperty, GetProperty
#include "vm/Realm-inl.h"             // for AutoRealm::AutoRealm

namespace js {
class GlobalObject;
}

using namespace js;

using mozilla::Maybe;
using mozilla::Nothing;
using mozilla::Some;

const JSClassOps DebuggerEnvironment::classOps_ = {
    nullptr,                               // addProperty
    nullptr,                               // delProperty
    nullptr,                               // enumerate
    nullptr,                               // newEnumerate
    nullptr,                               // resolve
    nullptr,                               // mayResolve
    nullptr,                               // finalize
    nullptr,                               // call
    nullptr,                               // construct
    CallTraceMethod<DebuggerEnvironment>,  // trace
};

const JSClass DebuggerEnvironment::class_ = {
    "Environment",
    JSCLASS_HAS_RESERVED_SLOTS(DebuggerEnvironment::RESERVED_SLOTS),
    &classOps_,
};

void DebuggerEnvironment::trace(JSTracer* trc) {
  // There is a barrier on private pointers, so the Unbarriered marking
  // is okay.
  if (Env* referent = maybeReferent()) {
    TraceManuallyBarrieredCrossCompartmentEdge(trc, this, &referent,
                                               "Debugger.Environment referent");
    if (referent != maybeReferent()) {
      setReservedSlotGCThingAsPrivateUnbarriered(ENV_SLOT, referent);
    }
  }
}

static DebuggerEnvironment* DebuggerEnvironment_checkThis(
    JSContext* cx, const CallArgs& args) {
  JSObject* thisobj = RequireObject(cx, args.thisv());
  if (!thisobj) {
    return nullptr;
  }
  if (!thisobj->is<DebuggerEnvironment>()) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_INCOMPATIBLE_PROTO, "Debugger.Environment",
                              "method", thisobj->getClass()->name);
    return nullptr;
  }

  return &thisobj->as<DebuggerEnvironment>();
}

struct MOZ_STACK_CLASS DebuggerEnvironment::CallData {
  JSContext* cx;
  const CallArgs& args;

  Handle<DebuggerEnvironment*> environment;

  CallData(JSContext* cx, const CallArgs& args,
           Handle<DebuggerEnvironment*> env)
      : cx(cx), args(args), environment(env) {}

  bool typeGetter();
  bool scopeKindGetter();
  bool parentGetter();
  bool objectGetter();
  bool calleeScriptGetter();
  bool inspectableGetter();
  bool optimizedOutGetter();

  bool namesMethod();
  bool findMethod();
  bool getVariableMethod();
  bool setVariableMethod();

  using Method = bool (CallData::*)();

  template <Method MyMethod>
  static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
};

template <DebuggerEnvironment::CallData::Method MyMethod>
/* static */
bool DebuggerEnvironment::CallData::ToNative(JSContext* cx, unsigned argc,
                                             Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  Rooted<DebuggerEnvironment*> environment(
      cx, DebuggerEnvironment_checkThis(cx, args));
  if (!environment) {
    return false;
  }

  CallData data(cx, args, environment);
  return (data.*MyMethod)();
}

/* static */
bool DebuggerEnvironment::construct(JSContext* cx, unsigned argc, Value* vp) {
  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                            "Debugger.Environment");
  return false;
}

static bool IsDeclarative(Env* env) {
  return env->is<DebugEnvironmentProxy>() &&
         env->as<DebugEnvironmentProxy>().isForDeclarative();
}

template <typename T>
static bool IsDebugEnvironmentWrapper(Env* env) {
  return env->is<DebugEnvironmentProxy>() &&
         env->as<DebugEnvironmentProxy>().environment().is<T>();
}

bool DebuggerEnvironment::CallData::typeGetter() {
  if (!environment->requireDebuggee(cx)) {
    return false;
  }

  DebuggerEnvironmentType type = environment->type();

  const char* s;
  switch (type) {
    case DebuggerEnvironmentType::Declarative:
      s = "declarative";
      break;
    case DebuggerEnvironmentType::With:
      s = "with";
      break;
    case DebuggerEnvironmentType::Object:
      s = "object";
      break;
  }

  JSAtom* str = Atomize(cx, s, strlen(s));
  if (!str) {
    return false;
  }

  args.rval().setString(str);
  return true;
}

bool DebuggerEnvironment::CallData::scopeKindGetter() {
  if (!environment->requireDebuggee(cx)) {
    return false;
  }

  Maybe<ScopeKind> kind = environment->scopeKind();
  if (kind.isSome()) {
    const char* s = ScopeKindString(*kind);
    JSAtom* str = Atomize(cx, s, strlen(s));
    if (!str) {
      return false;
    }
    args.rval().setString(str);
  } else {
    args.rval().setNull();
  }

  return true;
}

bool DebuggerEnvironment::CallData::parentGetter() {
  if (!environment->requireDebuggee(cx)) {
    return false;
  }

  Rooted<DebuggerEnvironment*> result(cx);
  if (!environment->getParent(cx, &result)) {
    return false;
  }

  args.rval().setObjectOrNull(result);
  return true;
}

bool DebuggerEnvironment::CallData::objectGetter() {
  if (!environment->requireDebuggee(cx)) {
    return false;
  }

  if (environment->type() == DebuggerEnvironmentType::Declarative) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_DEBUG_NO_ENV_OBJECT);
    return false;
  }

  Rooted<DebuggerObject*> result(cx);
  if (!environment->getObject(cx, &result)) {
    return false;
  }

  args.rval().setObject(*result);
  return true;
}

bool DebuggerEnvironment::CallData::calleeScriptGetter() {
  if (!environment->requireDebuggee(cx)) {
    return false;
  }

  Rooted<DebuggerScript*> result(cx);
  if (!environment->getCalleeScript(cx, &result)) {
    return false;
  }

  args.rval().setObjectOrNull(result);
  return true;
}

bool DebuggerEnvironment::CallData::inspectableGetter() {
  args.rval().setBoolean(environment->isDebuggee());
  return true;
}

bool DebuggerEnvironment::CallData::optimizedOutGetter() {
  args.rval().setBoolean(environment->isOptimized());
  return true;
}

bool DebuggerEnvironment::CallData::namesMethod() {
  if (!environment->requireDebuggee(cx)) {
    return false;
  }

  RootedIdVector ids(cx);
  if (!DebuggerEnvironment::getNames(cx, environment, &ids)) {
    return false;
  }

  JSObject* obj = IdVectorToArray(cx, ids);
  if (!obj) {
    return false;
  }

  args.rval().setObject(*obj);
  return true;
}

bool DebuggerEnvironment::CallData::findMethod() {
  if (!args.requireAtLeast(cx, "Debugger.Environment.find", 1)) {
    return false;
  }

  RootedId id(cx);
  if (!ValueToIdentifier(cx, args[0], &id)) {
    return false;
  }

  if (!environment->requireDebuggee(cx)) {
    return false;
  }

  Rooted<DebuggerEnvironment*> result(cx);
  if (!DebuggerEnvironment::find(cx, environment, id, &result)) {
    return false;
  }

  args.rval().setObjectOrNull(result);
  return true;
}

bool DebuggerEnvironment::CallData::getVariableMethod() {
  if (!args.requireAtLeast(cx, "Debugger.Environment.getVariable", 1)) {
    return false;
  }

  RootedId id(cx);
  if (!ValueToIdentifier(cx, args[0], &id)) {
    return false;
  }

  if (!environment->requireDebuggee(cx)) {
    return false;
  }

  return DebuggerEnvironment::getVariable(cx, environment, id, args.rval());
}

bool DebuggerEnvironment::CallData::setVariableMethod() {
  if (!args.requireAtLeast(cx, "Debugger.Environment.setVariable", 2)) {
    return false;
  }

  RootedId id(cx);
  if (!ValueToIdentifier(cx, args[0], &id)) {
    return false;
  }

  if (!environment->requireDebuggee(cx)) {
    return false;
  }

  if (!DebuggerEnvironment::setVariable(cx, environment, id, args[1])) {
    return false;
  }

  args.rval().setUndefined();
  return true;
}

bool DebuggerEnvironment::requireDebuggee(JSContext* cx) const {
  if (!isDebuggee()) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_DEBUG_NOT_DEBUGGEE, "Debugger.Environment",
                              "environment");

    return false;
  }

  return true;
}

const JSPropertySpec DebuggerEnvironment::properties_[] = {
    JS_DEBUG_PSG("type", typeGetter),
    JS_DEBUG_PSG("scopeKind", scopeKindGetter),
    JS_DEBUG_PSG("parent", parentGetter),
    JS_DEBUG_PSG("object", objectGetter),
    JS_DEBUG_PSG("calleeScript", calleeScriptGetter),
    JS_DEBUG_PSG("inspectable", inspectableGetter),
    JS_DEBUG_PSG("optimizedOut", optimizedOutGetter),
    JS_PS_END,
};

const JSFunctionSpec DebuggerEnvironment::methods_[] = {
    JS_DEBUG_FN("names", namesMethod, 0),
    JS_DEBUG_FN("find", findMethod, 1),
    JS_DEBUG_FN("getVariable", getVariableMethod, 1),
    JS_DEBUG_FN("setVariable", setVariableMethod, 2),
    JS_FS_END,
};

/* static */
NativeObject* DebuggerEnvironment::initClass(JSContext* cx,
                                             Handle<GlobalObject*> global,
                                             HandleObject dbgCtor) {
  return InitClass(cx, dbgCtor, nullptr, nullptr, "Environment", construct, 0,
                   properties_, methods_, nullptr, nullptr);
}

/* static */
DebuggerEnvironment* DebuggerEnvironment::create(
    JSContext* cx, HandleObject proto, HandleObject referent,
    Handle<NativeObject*> debugger) {
  DebuggerEnvironment* obj =
      IsInsideNursery(referent)
          ? NewObjectWithGivenProto<DebuggerEnvironment>(cx, proto)
          : NewTenuredObjectWithGivenProto<DebuggerEnvironment>(cx, proto);
  if (!obj) {
    return nullptr;
  }

  obj->setReservedSlotGCThingAsPrivate(ENV_SLOT, referent);
  obj->setReservedSlot(OWNER_SLOT, ObjectValue(*debugger));

  return obj;
}

/* static */
DebuggerEnvironmentType DebuggerEnvironment::type() const {
  // Don't bother switching compartments just to check env's type.
  if (IsDeclarative(referent())) {
    return DebuggerEnvironmentType::Declarative;
  }
  if (IsDebugEnvironmentWrapper<WithEnvironmentObject>(referent())) {
    return DebuggerEnvironmentType::With;
  }
  return DebuggerEnvironmentType::Object;
}

mozilla::Maybe<ScopeKind> DebuggerEnvironment::scopeKind() const {
  if (!referent()->is<DebugEnvironmentProxy>()) {
    return Nothing();
  }
  EnvironmentObject& env =
      referent()->as<DebugEnvironmentProxy>().environment();
  Scope* scope = GetEnvironmentScope(env);
  return scope ? Some(scope->kind()) : Nothing();
}

bool DebuggerEnvironment::getParent(
    JSContext* cx, MutableHandle<DebuggerEnvironment*> result) const {
  // Don't bother switching compartments just to get env's parent.
  Rooted<Env*> parent(cx, referent()->enclosingEnvironment());
  if (!parent) {
    result.set(nullptr);
    return true;
  }

  return owner()->wrapEnvironment(cx, parent, result);
}

bool DebuggerEnvironment::getObject(
    JSContext* cx, MutableHandle<DebuggerObject*> result) const {
  MOZ_ASSERT(type() != DebuggerEnvironmentType::Declarative);

  // Don't bother switching compartments just to get env's object.
  RootedObject object(cx);
  if (IsDebugEnvironmentWrapper<WithEnvironmentObject>(referent())) {
    object.set(&referent()
                    ->as<DebugEnvironmentProxy>()
                    .environment()
                    .as<WithEnvironmentObject>()
                    .object());
  } else if (IsDebugEnvironmentWrapper<NonSyntacticVariablesObject>(
                 referent())) {
    object.set(&referent()
                    ->as<DebugEnvironmentProxy>()
                    .environment()
                    .as<NonSyntacticVariablesObject>());
  } else {
    object.set(referent());
    MOZ_ASSERT(!object->is<DebugEnvironmentProxy>());
  }

  return owner()->wrapDebuggeeObject(cx, object, result);
}

bool DebuggerEnvironment::getCalleeScript(
    JSContext* cx, MutableHandle<DebuggerScript*> result) const {
  if (!referent()->is<DebugEnvironmentProxy>()) {
    result.set(nullptr);
    return true;
  }

  JSObject& scope = referent()->as<DebugEnvironmentProxy>().environment();
  if (!scope.is<CallObject>()) {
    result.set(nullptr);
    return true;
  }

  Rooted<BaseScript*> script(cx, scope.as<CallObject>().callee().baseScript());

  DebuggerScript* scriptObject = owner()->wrapScript(cx, script);
  if (!scriptObject) {
    return false;
  }

  result.set(scriptObject);
  return true;
}

bool DebuggerEnvironment::isDebuggee() const {
  MOZ_ASSERT(referent());
  MOZ_ASSERT(!referent()->is<EnvironmentObject>());

  return owner()->observesGlobal(&referent()->nonCCWGlobal());
}

bool DebuggerEnvironment::isOptimized() const {
  return referent()->is<DebugEnvironmentProxy>() &&
         referent()->as<DebugEnvironmentProxy>().isOptimizedOut();
}

/* static */
bool DebuggerEnvironment::getNames(JSContext* cx,
                                   Handle<DebuggerEnvironment*> environment,
                                   MutableHandleIdVector result) {
  MOZ_ASSERT(environment->isDebuggee());
  MOZ_ASSERT(result.empty());

  Rooted<Env*> referent(cx, environment->referent());
  {
    Maybe<AutoRealm> ar;
    ar.emplace(cx, referent);

    ErrorCopier ec(ar);
    if (!GetPropertyKeys(cx, referent, JSITER_HIDDEN, result)) {
      return false;
    }
  }

  result.eraseIf([](PropertyKey key) {
    return !key.isAtom() || !IsIdentifier(key.toAtom());
  });

  for (size_t i = 0; i < result.length(); ++i) {
    cx->markAtom(result[i].toAtom());
  }

  return true;
}

/* static */
bool DebuggerEnvironment::find(JSContext* cx,
                               Handle<DebuggerEnvironment*> environment,
                               HandleId id,
                               MutableHandle<DebuggerEnvironment*> result) {
  MOZ_ASSERT(environment->isDebuggee());

  Rooted<Env*> env(cx, environment->referent());
  Debugger* dbg = environment->owner();

  {
    Maybe<AutoRealm> ar;
    ar.emplace(cx, env);

    cx->markId(id);

    // This can trigger resolve hooks.
    ErrorCopier ec(ar);
    for (; env; env = env->enclosingEnvironment()) {
      bool found;
      if (!HasProperty(cx, env, id, &found)) {
        return false;
      }
      if (found) {
        break;
      }
    }
  }

  if (!env) {
    result.set(nullptr);
    return true;
  }

  return dbg->wrapEnvironment(cx, env, result);
}

/* static */
bool DebuggerEnvironment::getVariable(JSContext* cx,
                                      Handle<DebuggerEnvironment*> environment,
                                      HandleId id, MutableHandleValue result) {
  MOZ_ASSERT(environment->isDebuggee());

  Rooted<Env*> referent(cx, environment->referent());
  Debugger* dbg = environment->owner();

  {
    Maybe<AutoRealm> ar;
    ar.emplace(cx, referent);

    cx->markId(id);

    // This can trigger getters.
    ErrorCopier ec(ar);

    bool found;
    if (!HasProperty(cx, referent, id, &found)) {
      return false;
    }
    if (!found) {
      result.setUndefined();
      return true;
    }

    // For DebugEnvironmentProxys, we get sentinel values for optimized out
    // slots and arguments instead of throwing (the default behavior).
    //
    // See wrapDebuggeeValue for how the sentinel values are wrapped.
    if (referent->is<DebugEnvironmentProxy>()) {
      Rooted<DebugEnvironmentProxy*> env(
          cx, &referent->as<DebugEnvironmentProxy>());
      if (!DebugEnvironmentProxy::getMaybeSentinelValue(cx, env, id, result)) {
        return false;
      }
    } else {
      if (!GetProperty(cx, referent, referent, id, result)) {
        return false;
      }
    }
  }

  // When we've faked up scope chain objects for optimized-out scopes,
  // declarative environments may contain internal JSFunction objects, which
  // we shouldn't expose to the user.
  if (result.isObject()) {
    RootedObject obj(cx, &result.toObject());
    if (obj->is<JSFunction>() &&
        IsInternalFunctionObject(obj->as<JSFunction>()))
      result.setMagic(JS_OPTIMIZED_OUT);
  }

  return dbg->wrapDebuggeeValue(cx, result);
}

/* static */
bool DebuggerEnvironment::setVariable(JSContext* cx,
                                      Handle<DebuggerEnvironment*> environment,
                                      HandleId id, HandleValue value_) {
  MOZ_ASSERT(environment->isDebuggee());

  Rooted<Env*> referent(cx, environment->referent());
  Debugger* dbg = environment->owner();

  RootedValue value(cx, value_);
  if (!dbg->unwrapDebuggeeValue(cx, &value)) {
    return false;
  }

  {
    Maybe<AutoRealm> ar;
    ar.emplace(cx, referent);
    if (!cx->compartment()->wrap(cx, &value)) {
      return false;
    }
    cx->markId(id);

    // This can trigger setters.
    ErrorCopier ec(ar);

    // Make sure the environment actually has the specified binding.
    bool found;
    if (!HasProperty(cx, referent, id, &found)) {
      return false;
    }
    if (!found) {
      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                JSMSG_DEBUG_VARIABLE_NOT_FOUND);
      return false;
    }

    // Just set the property.
    if (!SetProperty(cx, referent, id, value)) {
      return false;
    }
  }

  return true;
}

Messung V0.5
C=94 H=93 G=93

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