https://github.com/mozilla/gecko-dev
Raw File
Tip revision: 717b1cdc7109767ac16cf4aaa0e6c4803352a9e8 authored by Ted Campbell on 07 November 2020, 05:36:31 UTC
Bug 1675905 - Simplify IonBuilder::createThisScripted. r=jandem,iain a=RyanVM
Tip revision: 717b1cd
environment.js
/* 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/. */
"use strict";

/* global Debugger */

const { ActorClassWithSpec, Actor } = require("devtools/shared/protocol");
const { createValueGrip } = require("devtools/server/actors/object/utils");
const { environmentSpec } = require("devtools/shared/specs/environment");

/**
 * Creates an EnvironmentActor. EnvironmentActors are responsible for listing
 * the bindings introduced by a lexical environment and assigning new values to
 * those identifier bindings.
 *
 * @param Debugger.Environment aEnvironment
 *        The lexical environment that will be used to create the actor.
 * @param ThreadActor aThreadActor
 *        The parent thread actor that contains this environment.
 */
const EnvironmentActor = ActorClassWithSpec(environmentSpec, {
  initialize: function(environment, threadActor) {
    Actor.prototype.initialize.call(this, threadActor.conn);

    this.obj = environment;
    this.threadActor = threadActor;
  },

  /**
   * When the Environment Actor is destroyed it removes the
   * Debugger.Environment.actor field so that environment does not
   * reference a destroyed actor.
   */
  destroy: function() {
    this.obj.actor = null;
    Actor.prototype.destroy.call(this);
  },

  /**
   * Return an environment form for use in a protocol message.
   */
  form: function() {
    const form = { actor: this.actorID };

    // What is this environment's type?
    if (this.obj.type == "declarative") {
      form.type = this.obj.callee ? "function" : "block";
    } else {
      form.type = this.obj.type;
    }

    form.scopeKind = this.obj.scopeKind;

    // Does this environment have a parent?
    if (this.obj.parent) {
      form.parent = this.threadActor
        .createEnvironmentActor(this.obj.parent, this.getParent())
        .form();
    }

    // Does this environment reflect the properties of an object as variables?
    if (this.obj.type == "object" || this.obj.type == "with") {
      form.object = createValueGrip(
        this.obj.object,
        this.getParent(),
        this.threadActor.objectGrip
      );
    }

    // Is this the environment created for a function call?
    if (this.obj.callee) {
      form.function = createValueGrip(
        this.obj.callee,
        this.getParent(),
        this.threadActor.objectGrip
      );
    }

    // Shall we list this environment's bindings?
    if (this.obj.type == "declarative") {
      form.bindings = this.bindings();
    }

    return form;
  },

  /**
   * Handle a protocol request to change the value of a variable bound in this
   * lexical environment.
   *
   * @param string name
   *        The name of the variable to be changed.
   * @param any value
   *        The value to be assigned.
   */
  assign: function(name, value) {
    // TODO: enable the commented-out part when getVariableDescriptor lands
    // (bug 725815).
    /* let desc = this.obj.getVariableDescriptor(name);

    if (!desc.writable) {
      return { error: "immutableBinding",
               message: "Changing the value of an immutable binding is not " +
                        "allowed" };
    }*/

    try {
      this.obj.setVariable(name, value);
    } catch (e) {
      if (e instanceof Debugger.DebuggeeWouldRun) {
        const errorObject = {
          error: "threadWouldRun",
          message: "Assigning a value would cause the debuggee to run",
        };
        throw errorObject;
      } else {
        throw e;
      }
    }
    return { from: this.actorID };
  },

  /**
   * Handle a protocol request to fully enumerate the bindings introduced by the
   * lexical environment.
   */
  bindings: function() {
    const bindings = { arguments: [], variables: {} };

    // TODO: this part should be removed in favor of the commented-out part
    // below when getVariableDescriptor lands (bug 725815).
    if (typeof this.obj.getVariable != "function") {
      // if (typeof this.obj.getVariableDescriptor != "function") {
      return bindings;
    }

    let parameterNames;
    if (this.obj.callee) {
      parameterNames = this.obj.callee.parameterNames;
    } else {
      parameterNames = [];
    }
    for (const name of parameterNames) {
      const arg = {};
      const value = this.obj.getVariable(name);

      // TODO: this part should be removed in favor of the commented-out part
      // below when getVariableDescriptor lands (bug 725815).
      const desc = {
        value: value,
        configurable: false,
        writable: !value?.optimizedOut,
        enumerable: true,
      };

      // let desc = this.obj.getVariableDescriptor(name);
      const descForm = {
        enumerable: true,
        configurable: desc.configurable,
      };
      if ("value" in desc) {
        descForm.value = createValueGrip(
          desc.value,
          this.getParent(),
          this.threadActor.objectGrip
        );
        descForm.writable = desc.writable;
      } else {
        descForm.get = createValueGrip(
          desc.get,
          this.getParent(),
          this.threadActor.objectGrip
        );
        descForm.set = createValueGrip(
          desc.set,
          this.getParent(),
          this.threadActor.objectGrip
        );
      }
      arg[name] = descForm;
      bindings.arguments.push(arg);
    }

    for (const name of this.obj.names()) {
      if (
        bindings.arguments.some(function exists(element) {
          return !!element[name];
        })
      ) {
        continue;
      }

      const value = this.obj.getVariable(name);

      // TODO: this part should be removed in favor of the commented-out part
      // below when getVariableDescriptor lands.
      const desc = {
        value: value,
        configurable: false,
        writable: !(
          value &&
          (value.optimizedOut || value.uninitialized || value.missingArguments)
        ),
        enumerable: true,
      };

      // let desc = this.obj.getVariableDescriptor(name);
      const descForm = {
        enumerable: true,
        configurable: desc.configurable,
      };
      if ("value" in desc) {
        descForm.value = createValueGrip(
          desc.value,
          this.getParent(),
          this.threadActor.objectGrip
        );
        descForm.writable = desc.writable;
      } else {
        descForm.get = createValueGrip(
          desc.get || undefined,
          this.getParent(),
          this.threadActor.objectGrip
        );
        descForm.set = createValueGrip(
          desc.set || undefined,
          this.getParent(),
          this.threadActor.objectGrip
        );
      }
      bindings.variables[name] = descForm;
    }

    return bindings;
  },
});

exports.EnvironmentActor = EnvironmentActor;
back to top