text:
/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=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/. */

"use strict";

const { Cu, Ci } = require("chrome");
const { GeneratedLocation } = require("devtools/server/actors/common");
const { DebuggerServer } = require("devtools/server/main")
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { assert, dumpn } = DevToolsUtils;
const PromiseDebugging = require("PromiseDebugging");

loader.lazyRequireGetter(this, "ThreadSafeChromeUtils");

const TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
      "Uint32Array", "Int8Array", "Int16Array", "Int32Array", "Float32Array",
      "Float64Array"];

// Number of items to preview in objects, arrays, maps, sets, lists,
// collections, etc.
const OBJECT_PREVIEW_MAX_ITEMS = 10;

/**
 * Creates an actor for the specified object.
 *
 * @param obj Debugger.Object
 *        The debuggee object.
 * @param hooks Object
 *        A collection of abstract methods that are implemented by the caller.
 *        ObjectActor requires the following functions to be implemented by
 *        the caller:
 *          - createValueGrip
 *              Creates a value grip for the given object
 *          - sources
 *              TabSources getter that manages the sources of a thread
 *          - createEnvironmentActor
 *              Creates and return an environment actor
 *          - getGripDepth
 *              An actor's grip depth getter
 *          - incrementGripDepth
 *              Increment the actor's grip depth
 *          - decrementGripDepth
 *              Decrement the actor's grip depth
 *          - globalDebugObject
 *              The Debuggee Global Object as given by the ThreadActor
 */
function ObjectActor(obj, {
  createValueGrip,
  sources,
  createEnvironmentActor,
  getGripDepth,
  incrementGripDepth,
  decrementGripDepth,
  getGlobalDebugObject
}) {
  assert(!obj.optimizedOut,
         "Should not create object actors for optimized out values!");
  this.obj = obj;
  this.hooks = {
    createValueGrip,
    sources,
    createEnvironmentActor,
    getGripDepth,
    incrementGripDepth,
    decrementGripDepth,
    getGlobalDebugObject
  };
  this.iterators = new Set();
}

ObjectActor.prototype = {
  actorPrefix: "obj",

  /**
   * Returns a grip for this actor for returning in a protocol message.
   */
  grip: function() {
    this.hooks.incrementGripDepth();

    let g = {
      "type": "object",
      "class": this.obj.class,
      "actor": this.actorID,
      "extensible": this.obj.isExtensible(),
      "frozen": this.obj.isFrozen(),
      "sealed": this.obj.isSealed()
    };

    if (this.obj.class != "DeadObject") {
      if (this.obj.class == "Promise") {
        g.promiseState = this._createPromiseState();
      }

      // FF40+: Allow to know how many properties an object has
      // to lazily display them when there is a bunch.
      // Throws on some MouseEvent object in tests.
      try {
        // Bug 1163520: Assert on internal functions
        if (this.obj.class != "Function") {
          g.ownPropertyLength = this.obj.getOwnPropertyNames().length;
        }
      } catch(e) {}

      let raw = this.obj.unsafeDereference();

      // If Cu is not defined, we are running on a worker thread, where xrays
      // don't exist.
      if (Cu) {
        raw = Cu.unwaiveXrays(raw);
      }

      if (!DevToolsUtils.isSafeJSObject(raw)) {
        raw = null;
      }

      let previewers = DebuggerServer.ObjectActorPreviewers[this.obj.class] ||
                       DebuggerServer.ObjectActorPreviewers.Object;
      for (let fn of previewers) {
        try {
          if (fn(this, g, raw)) {
            break;
          }
        } catch (e) {
          let msg = "ObjectActor.prototype.grip previewer function";
          DevToolsUtils.reportException(msg, e);
        }
      }
    }

    this.hooks.decrementGripDepth();
    return g;
  },

  /**
   * Returns an object exposing the internal Promise state.
   */
  _createPromiseState: function() {
    const { state, value, reason } = getPromiseState(this.obj);
    let promiseState = { state };
    let rawPromise = this.obj.unsafeDereference();

    if (state == "fulfilled") {
      promiseState.value = this.hooks.createValueGrip(value);
    } else if (state == "rejected") {
      promiseState.reason = this.hooks.createValueGrip(reason);
    }

    promiseState.creationTimestamp = Date.now() -
      PromiseDebugging.getPromiseLifetime(rawPromise);

    // If the promise is not settled, avoid adding the timeToSettle property
    // and catch the error thrown by PromiseDebugging.getTimeToSettle.
    try {
      promiseState.timeToSettle = PromiseDebugging.getTimeToSettle(rawPromise);
    } catch(e) {}

    return promiseState;
  },

  /**
   * Releases this actor from the pool.
   */
  release: function() {
    if (this.registeredPool.objectActors) {
      this.registeredPool.objectActors.delete(this.obj);
    }
    this.iterators.forEach(actor => this.registeredPool.removeActor(actor));
    this.iterators.clear();
    this.registeredPool.removeActor(this);
  },

  /**
   * Handle a protocol request to provide the definition site of this function
   * object.
   */
  onDefinitionSite: function() {
    if (this.obj.class != "Function") {
      return {
        from: this.actorID,
        error: "objectNotFunction",
        message: this.actorID + " is not a function."
      };
    }

    if (!this.obj.script) {
      return {
        from: this.actorID,
        error: "noScript",
        message: this.actorID + " has no Debugger.Script"
      };
    }

    return this.hooks.sources().getOriginalLocation(new GeneratedLocation(
      this.hooks.sources().createNonSourceMappedActor(this.obj.script.source),
      this.obj.script.startLine,
      0 // TODO bug 901138: use Debugger.Script.prototype.startColumn
    )).then((originalLocation) => {
      return {
        source: originalLocation.originalSourceActor.form(),
        line: originalLocation.originalLine,
        column: originalLocation.originalColumn
      };
    });
  },

  /**
   * Handle a protocol request to provide the names of the properties defined on
   * the object and not its prototype.
   */
  onOwnPropertyNames: function() {
    return { from: this.actorID,
             ownPropertyNames: this.obj.getOwnPropertyNames() };
  },

  /**
   * Creates an actor to iterate over an object property names and values.
   * See PropertyIteratorActor constructor for more info about options param.
   *
   * @param request object
   *        The protocol request object.
   */
  onEnumProperties: function(request) {
    let actor = new PropertyIteratorActor(this, request.options);
    this.registeredPool.addActor(actor);
    this.iterators.add(actor);
    return { iterator: actor.grip() };
  },

  /**
   * Handle a protocol request to provide the prototype and own properties of
   * the object.
   */
  onPrototypeAndProperties: function() {
    let ownProperties = Object.create(null);
    let names;
    try {
      names = this.obj.getOwnPropertyNames();
    } catch (ex) {
      // The above can throw if this.obj points to a dead object.
      // TODO: we should use Cu.isDeadWrapper() - see bug 885800.
      return { from: this.actorID,
               prototype: this.hooks.createValueGrip(null),
               ownProperties: ownProperties,
               safeGetterValues: Object.create(null) };
    }
    for (let name of names) {
      ownProperties[name] = this._propertyDescriptor(name);
    }
    return { from: this.actorID,
             prototype: this.hooks.createValueGrip(this.obj.proto),
             ownProperties: ownProperties,
             safeGetterValues: this._findSafeGetterValues(names) };
  },

  /**
   * Find the safe getter values for the current Debugger.Object, |this.obj|.
   *
   * @private
   * @param array ownProperties
   *        The array that holds the list of known ownProperties names for
   *        |this.obj|.
   * @param number [limit=0]
   *        Optional limit of getter values to find.
   * @return object
   *         An object that maps property names to safe getter descriptors as
   *         defined by the remote debugging protocol.
   */
  _findSafeGetterValues: function(ownProperties, limit = 0) {
    let safeGetterValues = Object.create(null);
    let obj = this.obj;
    let level = 0, i = 0;

    while (obj) {
      let getters = this._findSafeGetters(obj);
      for (let name of getters) {
        // Avoid overwriting properties from prototypes closer to this.obj. Also
        // avoid providing safeGetterValues from prototypes if property |name|
        // is already defined as an own property.
        if (name in safeGetterValues ||
            (obj != this.obj && ownProperties.indexOf(name) !== -1)) {
          continue;
        }

        // Ignore __proto__ on Object.prototye.
        if (!obj.proto && name == "__proto__") {
          continue;
        }

        let desc = null, getter = null;
        try {
          desc = obj.getOwnPropertyDescriptor(name);
          getter = desc.get;
        } catch (ex) {
          // The above can throw if the cache becomes stale.
        }
        if (!getter) {
          obj._safeGetters = null;
          continue;
        }

        let result = getter.call(this.obj);
        if (result && !("throw" in result)) {
          let getterValue = undefined;
          if ("return" in result) {
            getterValue = result.return;
          } else if ("yield" in result) {
            getterValue = result.yield;
          }
          // WebIDL attributes specified with the LenientThis extended attribute
          // return undefined and should be ignored.
          if (getterValue !== undefined) {
            safeGetterValues[name] = {
              getterValue: this.hooks.createValueGrip(getterValue),
              getterPrototypeLevel: level,
              enumerable: desc.enumerable,
              writable: level == 0 ? desc.writable : true,
            };
            if (limit && ++i == limit) {
              break;
            }
          }
        }
      }
      if (limit && i == limit) {
        break;
      }

      obj = obj.proto;
      level++;
    }

    return safeGetterValues;
  },

  /**
   * Find the safe getters for a given Debugger.Object. Safe getters are native
   * getters which are safe to execute.
   *
   * @private
   * @param Debugger.Object object
   *        The Debugger.Object where you want to find safe getters.
   * @return Set
   *         A Set of names of safe getters. This result is cached for each
   *         Debugger.Object.
   */
  _findSafeGetters: function(object) {
    if (object._safeGetters) {
      return object._safeGetters;
    }

    let getters = new Set();
    let names = [];
    try {
      names = object.getOwnPropertyNames()
    } catch (ex) {
      // Calling getOwnPropertyNames() on some wrapped native prototypes is not
      // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
    }

    for (let name of names) {
      let desc = null;
      try {
        desc = object.getOwnPropertyDescriptor(name);
      } catch (e) {
        // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
        // allowed (bug 560072).
      }
      if (!desc || desc.value !== undefined || !("get" in desc)) {
        continue;
      }

      if (DevToolsUtils.hasSafeGetter(desc)) {
        getters.add(name);
      }
    }

    object._safeGetters = getters;
    return getters;
  },

  /**
   * Handle a protocol request to provide the prototype of the object.
   */
  onPrototype: function() {
    return { from: this.actorID,
             prototype: this.hooks.createValueGrip(this.obj.proto) };
  },

  /**
   * Handle a protocol request to provide the property descriptor of the
   * object's specified property.
   *
   * @param request object
   *        The protocol request object.
   */
  onProperty: function(request) {
    if (!request.name) {
      return { error: "missingParameter",
               message: "no property name was specified" };
    }

    return { from: this.actorID,
             descriptor: this._propertyDescriptor(request.name) };
  },

  /**
   * Handle a protocol request to provide the display string for the object.
   */
  onDisplayString: function() {
    const string = stringify(this.obj);
    return { from: this.actorID,
             displayString: this.hooks.createValueGrip(string) };
  },

  /**
   * A helper method that creates a property descriptor for the provided object,
   * properly formatted for sending in a protocol response.
   *
   * @private
   * @param string name
   *        The property that the descriptor is generated for.
   * @param boolean [onlyEnumerable]
   *        Optional: true if you want a descriptor only for an enumerable
   *        property, false otherwise.
   * @return object|undefined
   *         The property descriptor, or undefined if this is not an enumerable
   *         property and onlyEnumerable=true.
   */
  _propertyDescriptor: function(name, onlyEnumerable) {
    let desc;
    try {
      desc = this.obj.getOwnPropertyDescriptor(name);
    } catch (e) {
      // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
      // allowed (bug 560072). Inform the user with a bogus, but hopefully
      // explanatory, descriptor.
      return {
        configurable: false,
        writable: false,
        enumerable: false,
        value: e.name
      };
    }

    if (!desc || onlyEnumerable && !desc.enumerable) {
      return undefined;
    }

    let retval = {
      configurable: desc.configurable,
      enumerable: desc.enumerable
    };

    if ("value" in desc) {
      retval.writable = desc.writable;
      retval.value = this.hooks.createValueGrip(desc.value);
    } else {
      if ("get" in desc) {
        retval.get = this.hooks.createValueGrip(desc.get);
      }
      if ("set" in desc) {
        retval.set = this.hooks.createValueGrip(desc.set);
      }
    }
    return retval;
  },

  /**
   * Handle a protocol request to provide the source code of a function.
   *
   * @param request object
   *        The protocol request object.
   */
  onDecompile: function(request) {
    if (this.obj.class !== "Function") {
      return { error: "objectNotFunction",
               message: "decompile request is only valid for object grips " +
                        "with a 'Function' class." };
    }

    return { from: this.actorID,
             decompiledCode: this.obj.decompile(!!request.pretty) };
  },

  /**
   * Handle a protocol request to provide the parameters of a function.
   */
  onParameterNames: function() {
    if (this.obj.class !== "Function") {
      return { error: "objectNotFunction",
               message: "'parameterNames' request is only valid for object " +
                        "grips with a 'Function' class." };
    }

    return { parameterNames: this.obj.parameterNames };
  },

  /**
   * Handle a protocol request to release a thread-lifetime grip.
   */
  onRelease: function() {
    this.release();
    return {};
  },

  /**
   * Handle a protocol request to provide the lexical scope of a function.
   */
  onScope: function() {
    if (this.obj.class !== "Function") {
      return { error: "objectNotFunction",
               message: "scope request is only valid for object grips with a" +
                        " 'Function' class." };
    }

    let envActor = this.hooks.createEnvironmentActor(this.obj.environment,
                                                     this.registeredPool);
    if (!envActor) {
      return { error: "notDebuggee",
               message: "cannot access the environment of this function." };
    }

    return { from: this.actorID, scope: envActor.form() };
  },

  /**
   * Handle a protocol request to get the list of dependent promises of a
   * promise.
   *
   * @return object
   *         Returns an object containing an array of object grips of the
   *         dependent promises
   */
  onDependentPromises: function() {
    if (this.obj.class != "Promise") {
      return { error: "objectNotPromise",
               message: "'dependentPromises' request is only valid for " +
                        "object grips with a 'Promise' class." };
    }

    let rawPromise = this.obj.unsafeDereference();
    let promises = PromiseDebugging.getDependentPromises(rawPromise).map(p =>
      this.hooks.createValueGrip(this.obj.makeDebuggeeValue(p)));

    return { promises };
  },

  /**
   * Handle a protocol request to get the allocation stack of a promise.
   */
  onAllocationStack: function() {
    if (this.obj.class != "Promise") {
      return { error: "objectNotPromise",
               message: "'allocationStack' request is only valid for " +
                        "object grips with a 'Promise' class." };
    }

    let rawPromise = this.obj.unsafeDereference();
    let stack = PromiseDebugging.getAllocationStack(rawPromise);
    let allocationStacks = [];

    while (stack) {
      if (stack.source) {
        let source = this._getSourceOriginalLocation(stack);

        if (source) {
          allocationStacks.push(source);
        }
      }
      stack = stack.parent;
    }

    return Promise.all(allocationStacks).then(stacks => {
      return { allocationStack: stacks };
    });
  },

  /**
   * Handle a protocol request to get the fulfillment stack of a promise.
   */
  onFulfillmentStack: function() {
    if (this.obj.class != "Promise") {
      return { error: "objectNotPromise",
               message: "'fulfillmentStack' request is only valid for " +
                        "object grips with a 'Promise' class." };
    }

    let rawPromise = this.obj.unsafeDereference();
    let stack = PromiseDebugging.getFullfillmentStack(rawPromise);
    let fulfillmentStacks = [];

    while (stack) {
      if (stack.source) {
        let source = this._getSourceOriginalLocation(stack);

        if (source) {
          fulfillmentStacks.push(source);
        }
      }
      stack = stack.parent;
    }

    return Promise.all(fulfillmentStacks).then(stacks => {
      return { fulfillmentStack: stacks };
    });
  },

  /**
   * Handle a protocol request to get the rejection stack of a promise.
   */
  onRejectionStack: function() {
    if (this.obj.class != "Promise") {
      return { error: "objectNotPromise",
               message: "'rejectionStack' request is only valid for " +
                        "object grips with a 'Promise' class." };
    }

    let rawPromise = this.obj.unsafeDereference();
    let stack = PromiseDebugging.getRejectionStack(rawPromise);
    let rejectionStacks = [];

    while (stack) {
      if (stack.source) {
        let source = this._getSourceOriginalLocation(stack);

        if (source) {
          rejectionStacks.push(source);
        }
      }
      stack = stack.parent;
    }

    return Promise.all(rejectionStacks).then(stacks => {
      return { rejectionStack: stacks };
    });
  },

  /**
   * Helper function for fetching the source location of a SavedFrame stack.
   *
   * @param SavedFrame stack
   *        The promise allocation stack frame
   * @return object
   *         Returns an object containing the source location of the SavedFrame
   *         stack.
   */
  _getSourceOriginalLocation: function(stack) {
    let source;

    // Catch any errors if the source actor cannot be found
    try {
      source = this.hooks.sources().getSourceActorByURL(stack.source);
    } catch(e) {}

    if (!source) {
      return null;
    }

    return this.hooks.sources().getOriginalLocation(new GeneratedLocation(
      source,
      stack.line,
      stack.column
    )).then((originalLocation) => {
      return {
        source: originalLocation.originalSourceActor.form(),
        line: originalLocation.originalLine,
        column: originalLocation.originalColumn,
        functionDisplayName: stack.functionDisplayName
      };
    });
  }
};

ObjectActor.prototype.requestTypes = {
  "definitionSite": ObjectActor.prototype.onDefinitionSite,
  "parameterNames": ObjectActor.prototype.onParameterNames,
  "prototypeAndProperties": ObjectActor.prototype.onPrototypeAndProperties,
  "enumProperties": ObjectActor.prototype.onEnumProperties,
  "prototype": ObjectActor.prototype.onPrototype,
  "property": ObjectActor.prototype.onProperty,
  "displayString": ObjectActor.prototype.onDisplayString,
  "ownPropertyNames": ObjectActor.prototype.onOwnPropertyNames,
  "decompile": ObjectActor.prototype.onDecompile,
  "release": ObjectActor.prototype.onRelease,
  "scope": ObjectActor.prototype.onScope,
  "dependentPromises": ObjectActor.prototype.onDependentPromises,
  "allocationStack": ObjectActor.prototype.onAllocationStack,
  "fulfillmentStack": ObjectActor.prototype.onFulfillmentStack,
  "rejectionStack": ObjectActor.prototype.onRejectionStack
};

/**
 * Creates an actor to iterate over an object's property names and values.
 *
 * @param objectActor ObjectActor
 *        The object actor.
 * @param options Object
 *        A dictionary object with various boolean attributes:
 *        - ignoreSafeGetters Boolean
 *          If true, do not iterate over safe getters.
 *        - ignoreIndexedProperties Boolean
 *          If true, filters out Array items.
 *          e.g. properties names between `0` and `object.length`.
 *        - ignoreNonIndexedProperties Boolean
 *          If true, filters out items that aren't array items
 *          e.g. properties names that are not a number between `0`
 *          and `object.length`.
 *        - sort Boolean
 *          If true, the iterator will sort the properties by name
 *          before dispatching them.
 *        - query String
 *          If non-empty, will filter the properties by names and values
 *          containing this query string. The match is not case-sensitive.
 *          Regarding value filtering it just compare to the stringification
 *          of the property value.
 */
function PropertyIteratorActor(objectActor, options){
  this.objectActor = objectActor;

  let ownProperties = Object.create(null);
  let names = [];
  try {
    names = this.objectActor.obj.getOwnPropertyNames();
  } catch (ex) {}

  let safeGetterValues = {};
  let safeGetterNames = [];
  if (!options.ignoreSafeGetters) {
    // Merge the safe getter values into the existing properties list.
    safeGetterValues = this.objectActor._findSafeGetterValues(names);
    safeGetterNames = Object.keys(safeGetterValues);
    for (let name of safeGetterNames) {
      if (names.indexOf(name) === -1) {
        names.push(name);
      }
    }
  }

  if (options.ignoreIndexedProperties || options.ignoreNonIndexedProperties) {
    let length = DevToolsUtils.getProperty(this.objectActor.obj, "length");
    if (typeof(length) !== "number") {
      // Pseudo arrays are flagged as ArrayLike if they have
      // subsequent indexed properties without having any length attribute.
      length = 0;
      for (let key of names) {
        if (isNaN(key) || key != length++) {
          break;
        }
      }
    }

    if (options.ignoreIndexedProperties) {
      names = names.filter(i => {
        // Use parseFloat in order to reject floats...
        // (parseInt converts floats to integer)
        // (Number(str) converts spaces to 0)
        i = parseFloat(i);
        return !Number.isInteger(i) || i < 0 || i >= length;
      });
    }

    if (options.ignoreNonIndexedProperties) {
      names = names.filter(i => {
        i = parseFloat(i);
        return Number.isInteger(i) && i >= 0 && i < length;
      });
    }
  }

  if (options.query) {
    let { query } = options;
    query = query.toLowerCase();
    names = names.filter(name => {
      // Filter on attribute names
      if (name.toLowerCase().includes(query)) {
        return true;
      }
      // and then on attribute values
      let desc;
      try {
        desc = this.obj.getOwnPropertyDescriptor(name);
      } catch(e) {}
      if (desc && desc.value &&
          String(desc.value).includes(query)) {
        return true;
      }
      return false;
    });
  }

  if (options.sort) {
    names.sort();
  }

  // Now build the descriptor list
  for (let name of names) {
    let desc = this.objectActor._propertyDescriptor(name);
    if (!desc) {
      desc = safeGetterValues[name];
    }
    else if (name in safeGetterValues) {
      // Merge the safe getter values into the existing properties list.
      let { getterValue, getterPrototypeLevel } = safeGetterValues[name];
      desc.getterValue = getterValue;
      desc.getterPrototypeLevel = getterPrototypeLevel;
    }
    ownProperties[name] = desc;
  }

  this.names = names;
  this.ownProperties = ownProperties;
}

PropertyIteratorActor.prototype = {
  actorPrefix: "propertyIterator",

  grip: function() {
    return {
      type: "propertyIterator",
      actor: this.actorID,
      count: this.names.length
    };
  },

  names: function({ indexes }) {
    let list = [];
    for (let idx of indexes) {
      list.push(this.names[idx]);
    }
    return {
      names: list
    };
  },

  slice: function({ start, count }) {
    let names = this.names.slice(start, start + count);
    let props = Object.create(null);
    for (let name of names) {
      props[name] = this.ownProperties[name];
    }
    return {
      ownProperties: props
    };
  },

  all: function() {
    return {
      ownProperties: this.ownProperties
    };
  }
};

PropertyIteratorActor.prototype.requestTypes = {
  "names": PropertyIteratorActor.prototype.names,
  "slice": PropertyIteratorActor.prototype.slice,
  "all": PropertyIteratorActor.prototype.all,
};

/**
 * Functions for adding information to ObjectActor grips for the purpose of
 * having customized output. This object holds arrays mapped by
 * Debugger.Object.prototype.class.
 *
 * In each array you can add functions that take two
 * arguments:
 *   - the ObjectActor instance and its hooks to make a preview for,
 *   - the grip object being prepared for the client,
 *   - the raw JS object after calling Debugger.Object.unsafeDereference(). This
 *   argument is only provided if the object is safe for reading properties and
 *   executing methods. See DevToolsUtils.isSafeJSObject().
 *
 * Functions must return false if they cannot provide preview
 * information for the debugger object, or true otherwise.
 */
DebuggerServer.ObjectActorPreviewers = {
  String: [function({obj, hooks}, grip) {
    let result = genericObjectPreviewer("String", String, obj, hooks);
    let length = DevToolsUtils.getProperty(obj, "length");

    if (!result || typeof length != "number") {
      return false;
    }

    grip.preview = {
      kind: "ArrayLike",
      length: length
    };

    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let items = grip.preview.items = [];

    const max = Math.min(result.value.length, OBJECT_PREVIEW_MAX_ITEMS);
    for (let i = 0; i < max; i++) {
      let value = hooks.createValueGrip(result.value[i]);
      items.push(value);
    }

    return true;
  }],

  Boolean: [function({obj, hooks}, grip) {
    let result = genericObjectPreviewer("Boolean", Boolean, obj, hooks);
    if (result) {
      grip.preview = result;
      return true;
    }

    return false;
  }],

  Number: [function({obj, hooks}, grip) {
    let result = genericObjectPreviewer("Number", Number, obj, hooks);
    if (result) {
      grip.preview = result;
      return true;
    }

    return false;
  }],

  Function: [function({obj, hooks}, grip) {
    if (obj.name) {
      grip.name = obj.name;
    }

    if (obj.displayName) {
      grip.displayName = obj.displayName.substr(0, 500);
    }

    if (obj.parameterNames) {
      grip.parameterNames = obj.parameterNames;
    }

    // Check if the developer has added a de-facto standard displayName
    // property for us to use.
    let userDisplayName;
    try {
      userDisplayName = obj.getOwnPropertyDescriptor("displayName");
    } catch (e) {
      // Calling getOwnPropertyDescriptor with displayName might throw
      // with "permission denied" errors for some functions.
      dumpn(e);
    }

    if (userDisplayName && typeof userDisplayName.value == "string" &&
        userDisplayName.value) {
      grip.userDisplayName = hooks.createValueGrip(userDisplayName.value);
    }

    let dbgGlobal = hooks.getGlobalDebugObject();
    if (dbgGlobal) {
      let script = dbgGlobal.makeDebuggeeValue(obj.unsafeDereference()).script;
      if (script) {
        grip.location = {
          url: script.url,
          line: script.startLine
        };
      }
    }

    return true;
  }],

  RegExp: [function({obj, hooks}, grip) {
    // Avoid having any special preview for the RegExp.prototype itself.
    if (!obj.proto || obj.proto.class != "RegExp") {
      return false;
    }

    let str = RegExp.prototype.toString.call(obj.unsafeDereference());
    grip.displayString = hooks.createValueGrip(str);
    return true;
  }],

  Date: [function({obj, hooks}, grip) {
    let time = Date.prototype.getTime.call(obj.unsafeDereference());

    grip.preview = {
      timestamp: hooks.createValueGrip(time),
    };
    return true;
  }],

  Array: [function({obj, hooks}, grip) {
    let length = DevToolsUtils.getProperty(obj, "length");
    if (typeof length != "number") {
      return false;
    }

    grip.preview = {
      kind: "ArrayLike",
      length: length,
    };

    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let raw = obj.unsafeDereference();
    let items = grip.preview.items = [];

    for (let i = 0; i < length; ++i) {
      // Array Xrays filter out various possibly-unsafe properties (like
      // functions, and claim that the value is undefined instead. This
      // is generally the right thing for privileged code accessing untrusted
      // objects, but quite confusing for Object previews. So we manually
      // override this protection by waiving Xrays on the array, and re-applying
      // Xrays on any indexed value props that we pull off of it.
      let desc = Object.getOwnPropertyDescriptor(Cu.waiveXrays(raw), i);
      if (desc && !desc.get && !desc.set) {
        let value = Cu.unwaiveXrays(desc.value);
        value = makeDebuggeeValueIfNeeded(obj, value);
        items.push(hooks.createValueGrip(value));
      } else {
        items.push(null);
      }

      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    return true;
  }],

  Set: [function({obj, hooks}, grip) {
    let size = DevToolsUtils.getProperty(obj, "size");
    if (typeof size != "number") {
      return false;
    }

    grip.preview = {
      kind: "ArrayLike",
      length: size,
    };

    // Avoid recursive object grips.
    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let raw = obj.unsafeDereference();
    let items = grip.preview.items = [];
    // We currently lack XrayWrappers for Set, so when we iterate over
    // the values, the temporary iterator objects get created in the target
    // compartment. However, we _do_ have Xrays to Object now, so we end up
    // Xraying those temporary objects, and filtering access to |it.value|
    // based on whether or not it's Xrayable and/or callable, which breaks
    // the for/of iteration.
    //
    // This code is designed to handle untrusted objects, so we can safely
    // waive Xrays on the iterable, and relying on the Debugger machinery to
    // make sure we handle the resulting objects carefully.
    for (let item of Cu.waiveXrays(Set.prototype.values.call(raw))) {
      item = Cu.unwaiveXrays(item);
      item = makeDebuggeeValueIfNeeded(obj, item);
      items.push(hooks.createValueGrip(item));
      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    return true;
  }],

  WeakSet: [function({obj, hooks}, grip) {
    let raw = obj.unsafeDereference();

    // We currently lack XrayWrappers for WeakSet, so when we iterate over
    // the values, the temporary iterator objects get created in the target
    // compartment. However, we _do_ have Xrays to Object now, so we end up
    // Xraying those temporary objects, and filtering access to |it.value|
    // based on whether or not it's Xrayable and/or callable, which breaks
    // the for/of iteration.
    //
    // This code is designed to handle untrusted objects, so we can safely
    // waive Xrays on the iterable, and relying on the Debugger machinery to
    // make sure we handle the resulting objects carefully.
    let keys = Cu.waiveXrays(ThreadSafeChromeUtils.nondeterministicGetWeakSetKeys(raw));
    grip.preview = {
      kind: "ArrayLike",
      length: keys.length,
    };

    // Avoid recursive object grips.
    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let items = grip.preview.items = [];
    for (let item of keys) {
      item = Cu.unwaiveXrays(item);
      item = makeDebuggeeValueIfNeeded(obj, item);
      items.push(hooks.createValueGrip(item));
      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    return true;
  }],

  Map: [function({obj, hooks}, grip) {
    let size = DevToolsUtils.getProperty(obj, "size");
    if (typeof size != "number") {
      return false;
    }

    grip.preview = {
      kind: "MapLike",
      size: size,
    };

    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let raw = obj.unsafeDereference();
    let entries = grip.preview.entries = [];
    // Iterating over a Map via .entries goes through various intermediate
    // objects - an Iterator object, then a 2-element Array object, then the
    // actual values we care about. We don't have Xrays to Iterator objects,
    // so we get Opaque wrappers for them. And even though we have Xrays to
    // Arrays, the semantics often deny access to the entires based on the
    // nature of the values. So we need waive Xrays for the iterator object
    // and the tupes, and then re-apply them on the underlying values until
    // we fix bug 1023984.
    //
    // Even then though, we might want to continue waiving Xrays here for the
    // same reason we do so for Arrays above - this filtering behavior is likely
    // to be more confusing than beneficial in the case of Object previews.
    for (let keyValuePair of Cu.waiveXrays(Map.prototype.entries.call(raw))) {
      let key = Cu.unwaiveXrays(keyValuePair[0]);
      let value = Cu.unwaiveXrays(keyValuePair[1]);
      key = makeDebuggeeValueIfNeeded(obj, key);
      value = makeDebuggeeValueIfNeeded(obj, value);
      entries.push([hooks.createValueGrip(key),
                    hooks.createValueGrip(value)]);
      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    return true;
  }],

  WeakMap: [function({obj, hooks}, grip) {
    let raw = obj.unsafeDereference();
    // We currently lack XrayWrappers for WeakMap, so when we iterate over
    // the values, the temporary iterator objects get created in the target
    // compartment. However, we _do_ have Xrays to Object now, so we end up
    // Xraying those temporary objects, and filtering access to |it.value|
    // based on whether or not it's Xrayable and/or callable, which breaks
    // the for/of iteration.
    //
    // This code is designed to handle untrusted objects, so we can safely
    // waive Xrays on the iterable, and relying on the Debugger machinery to
    // make sure we handle the resulting objects carefully.
    let rawEntries = Cu.waiveXrays(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(raw));

    grip.preview = {
      kind: "MapLike",
      size: rawEntries.length,
    };

    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let entries = grip.preview.entries = [];
    for (let key of rawEntries) {
      let value = Cu.unwaiveXrays(WeakMap.prototype.get.call(raw, key));
      key = Cu.unwaiveXrays(key);
      key = makeDebuggeeValueIfNeeded(obj, key);
      value = makeDebuggeeValueIfNeeded(obj, value);
      entries.push([hooks.createValueGrip(key),
                    hooks.createValueGrip(value)]);
      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    return true;
  }],

  DOMStringMap: [function({obj, hooks}, grip, rawObj) {
    if (!rawObj) {
      return false;
    }

    let keys = obj.getOwnPropertyNames();
    grip.preview = {
      kind: "MapLike",
      size: keys.length,
    };

    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let entries = grip.preview.entries = [];
    for (let key of keys) {
      let value = makeDebuggeeValueIfNeeded(obj, rawObj[key]);
      entries.push([key, hooks.createValueGrip(value)]);
      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    return true;
  }],
};

/**
 * Generic previewer for "simple" classes like String, Number and Boolean.
 *
 * @param string className
 *        Class name to expect.
 * @param object classObj
 *        The class to expect, eg. String. The valueOf() method of the class is
 *        invoked on the given object.
 * @param Debugger.Object obj
 *        The debugger object we need to preview.
 * @param object hooks
 *        The thread actor to use to create a value grip.
 * @return object|null
 *         An object with one property, "value", which holds the value grip that
 *         represents the given object. Null is returned if we cant preview the
 *         object.
 */
function genericObjectPreviewer(className, classObj, obj, hooks) {
  if (!obj.proto || obj.proto.class != className) {
    return null;
  }

  let raw = obj.unsafeDereference();
  let v = null;
  try {
    v = classObj.prototype.valueOf.call(raw);
  } catch (ex) {
    // valueOf() can throw if the raw JS object is "misbehaved".
    return null;
  }

  if (v !== null) {
    v = hooks.createValueGrip(makeDebuggeeValueIfNeeded(obj, v));
    return { value: v };
  }

  return null;
}

// Preview functions that do not rely on the object class.
DebuggerServer.ObjectActorPreviewers.Object = [
  function TypedArray({obj, hooks}, grip) {
    if (TYPED_ARRAY_CLASSES.indexOf(obj.class) == -1) {
      return false;
    }

    let length = DevToolsUtils.getProperty(obj, "length");
    if (typeof length != "number") {
      return false;
    }

    grip.preview = {
      kind: "ArrayLike",
      length: length,
    };

    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let raw = obj.unsafeDereference();
    let global = Cu.getGlobalForObject(DebuggerServer);
    let classProto = global[obj.class].prototype;
    // The Xray machinery for TypedArrays denies indexed access on the grounds
    // that it's slow, and advises callers to do a structured clone instead.
    let safeView = Cu.cloneInto(classProto.subarray.call(raw, 0,
      OBJECT_PREVIEW_MAX_ITEMS), global);
    let items = grip.preview.items = [];
    for (let i = 0; i < safeView.length; i++) {
      items.push(safeView[i]);
    }

    return true;
  },

  function Error({obj, hooks}, grip) {
    switch (obj.class) {
      case "Error":
      case "EvalError":
      case "RangeError":
      case "ReferenceError":
      case "SyntaxError":
      case "TypeError":
      case "URIError":
        let name = DevToolsUtils.getProperty(obj, "name");
        let msg = DevToolsUtils.getProperty(obj, "message");
        let stack = DevToolsUtils.getProperty(obj, "stack");
        let fileName = DevToolsUtils.getProperty(obj, "fileName");
        let lineNumber = DevToolsUtils.getProperty(obj, "lineNumber");
        let columnNumber = DevToolsUtils.getProperty(obj, "columnNumber");
        grip.preview = {
          kind: "Error",
          name: hooks.createValueGrip(name),
          message: hooks.createValueGrip(msg),
          stack: hooks.createValueGrip(stack),
          fileName: hooks.createValueGrip(fileName),
          lineNumber: hooks.createValueGrip(lineNumber),
          columnNumber: hooks.createValueGrip(columnNumber),
        };
        return true;
      default:
        return false;
    }
  },

  function CSSMediaRule({obj, hooks}, grip, rawObj) {
    if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMCSSMediaRule)) {
      return false;
    }
    grip.preview = {
      kind: "ObjectWithText",
      text: hooks.createValueGrip(rawObj.conditionText),
    };
    return true;
  },

  function CSSStyleRule({obj, hooks}, grip, rawObj) {
    if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMCSSStyleRule)) {
      return false;
    }
    grip.preview = {
      kind: "ObjectWithText",
      text: hooks.createValueGrip(rawObj.selectorText),
    };
    return true;
  },

  function ObjectWithURL({obj, hooks}, grip, rawObj) {
    if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMCSSImportRule ||
                                 rawObj instanceof Ci.nsIDOMCSSStyleSheet ||
                                 rawObj instanceof Ci.nsIDOMLocation ||
                                 rawObj instanceof Ci.nsIDOMWindow)) {
      return false;
    }

    let url;
    if (rawObj instanceof Ci.nsIDOMWindow && rawObj.location) {
      url = rawObj.location.href;
    } else if (rawObj.href) {
      url = rawObj.href;
    } else {
      return false;
    }

    grip.preview = {
      kind: "ObjectWithURL",
      url: hooks.createValueGrip(url),
    };

    return true;
  },

  function ArrayLike({obj, hooks}, grip, rawObj) {
    if (isWorker || !rawObj ||
        obj.class != "DOMStringList" &&
        obj.class != "DOMTokenList" &&
        !(rawObj instanceof Ci.nsIDOMMozNamedAttrMap ||
          rawObj instanceof Ci.nsIDOMCSSRuleList ||
          rawObj instanceof Ci.nsIDOMCSSValueList ||
          rawObj instanceof Ci.nsIDOMFileList ||
          rawObj instanceof Ci.nsIDOMFontFaceList ||
          rawObj instanceof Ci.nsIDOMMediaList ||
          rawObj instanceof Ci.nsIDOMNodeList ||
          rawObj instanceof Ci.nsIDOMStyleSheetList)) {
      return false;
    }

    if (typeof rawObj.length != "number") {
      return false;
    }

    grip.preview = {
      kind: "ArrayLike",
      length: rawObj.length,
    };

    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let items = grip.preview.items = [];

    for (let i = 0; i < rawObj.length &&
                    items.length < OBJECT_PREVIEW_MAX_ITEMS; i++) {
      let value = makeDebuggeeValueIfNeeded(obj, rawObj[i]);
      items.push(hooks.createValueGrip(value));
    }

    return true;
  },

  function CSSStyleDeclaration({obj, hooks}, grip, rawObj) {
    if (isWorker || !rawObj ||
        !(rawObj instanceof Ci.nsIDOMCSSStyleDeclaration)) {
      return false;
    }

    grip.preview = {
      kind: "MapLike",
      size: rawObj.length,
    };

    let entries = grip.preview.entries = [];

    for (let i = 0; i < OBJECT_PREVIEW_MAX_ITEMS &&
                    i < rawObj.length; i++) {
      let prop = rawObj[i];
      let value = rawObj.getPropertyValue(prop);
      entries.push([prop, hooks.createValueGrip(value)]);
    }

    return true;
  },

  function DOMNode({obj, hooks}, grip, rawObj) {
    if (isWorker || obj.class == "Object" || !rawObj ||
        !(rawObj instanceof Ci.nsIDOMNode)) {
      return false;
    }

    let preview = grip.preview = {
      kind: "DOMNode",
      nodeType: rawObj.nodeType,
      nodeName: rawObj.nodeName,
    };

    if (rawObj instanceof Ci.nsIDOMDocument && rawObj.location) {
      preview.location = hooks.createValueGrip(rawObj.location.href);
    } else if (rawObj instanceof Ci.nsIDOMDocumentFragment) {
      preview.childNodesLength = rawObj.childNodes.length;

      if (hooks.getGripDepth() < 2) {
        preview.childNodes = [];
        for (let node of rawObj.childNodes) {
          let actor = hooks.createValueGrip(obj.makeDebuggeeValue(node));
          preview.childNodes.push(actor);
          if (preview.childNodes.length == OBJECT_PREVIEW_MAX_ITEMS) {
            break;
          }
        }
      }
    } else if (rawObj instanceof Ci.nsIDOMElement) {
      // Add preview for DOM element attributes.
      if (rawObj instanceof Ci.nsIDOMHTMLElement) {
        preview.nodeName = preview.nodeName.toLowerCase();
      }

      let i = 0;
      preview.attributes = {};
      preview.attributesLength = rawObj.attributes.length;
      for (let attr of rawObj.attributes) {
        preview.attributes[attr.nodeName] = hooks.createValueGrip(attr.value);
        if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
          break;
        }
      }
    } else if (rawObj instanceof Ci.nsIDOMAttr) {
      preview.value = hooks.createValueGrip(rawObj.value);
    } else if (rawObj instanceof Ci.nsIDOMText ||
               rawObj instanceof Ci.nsIDOMComment) {
      preview.textContent = hooks.createValueGrip(rawObj.textContent);
    }

    return true;
  },

  function DOMEvent({obj, hooks}, grip, rawObj) {
    if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMEvent)) {
      return false;
    }

    let preview = grip.preview = {
      kind: "DOMEvent",
      type: rawObj.type,
      properties: Object.create(null),
    };

    if (hooks.getGripDepth() < 2) {
      let target = obj.makeDebuggeeValue(rawObj.target);
      preview.target = hooks.createValueGrip(target);
    }

    let props = [];
    if (rawObj instanceof Ci.nsIDOMMouseEvent) {
      props.push("buttons", "clientX", "clientY", "layerX", "layerY");
    } else if (rawObj instanceof Ci.nsIDOMKeyEvent) {
      let modifiers = [];
      if (rawObj.altKey) {
        modifiers.push("Alt");
      }
      if (rawObj.ctrlKey) {
        modifiers.push("Control");
      }
      if (rawObj.metaKey) {
        modifiers.push("Meta");
      }
      if (rawObj.shiftKey) {
        modifiers.push("Shift");
      }
      preview.eventKind = "key";
      preview.modifiers = modifiers;

      props.push("key", "charCode", "keyCode");
    } else if (rawObj instanceof Ci.nsIDOMTransitionEvent) {
      props.push("propertyName", "pseudoElement");
    } else if (rawObj instanceof Ci.nsIDOMAnimationEvent) {
      props.push("animationName", "pseudoElement");
    } else if (rawObj instanceof Ci.nsIDOMClipboardEvent) {
      props.push("clipboardData");
    }

    // Add event-specific properties.
    for (let prop of props) {
      let value = rawObj[prop];
      if (value && (typeof value == "object" || typeof value == "function")) {
        // Skip properties pointing to objects.
        if (hooks.getGripDepth() > 1) {
          continue;
        }
        value = obj.makeDebuggeeValue(value);
      }
      preview.properties[prop] = hooks.createValueGrip(value);
    }

    // Add any properties we find on the event object.
    if (!props.length) {
      let i = 0;
      for (let prop in rawObj) {
        let value = rawObj[prop];
        if (prop == "target" || prop == "type" || value === null ||
            typeof value == "function") {
          continue;
        }
        if (value && typeof value == "object") {
          if (hooks.getGripDepth() > 1) {
            continue;
          }
          value = obj.makeDebuggeeValue(value);
        }
        preview.properties[prop] = hooks.createValueGrip(value);
        if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
          break;
        }
      }
    }

    return true;
  },

  function DOMException({obj, hooks}, grip, rawObj) {
    if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMDOMException)) {
      return false;
    }

    grip.preview = {
      kind: "DOMException",
      name: hooks.createValueGrip(rawObj.name),
      message: hooks.createValueGrip(rawObj.message),
      code: hooks.createValueGrip(rawObj.code),
      result: hooks.createValueGrip(rawObj.result),
      filename: hooks.createValueGrip(rawObj.filename),
      lineNumber: hooks.createValueGrip(rawObj.lineNumber),
      columnNumber: hooks.createValueGrip(rawObj.columnNumber),
    };

    return true;
  },

  function PseudoArray({obj, hooks}, grip, rawObj) {
    let length = 0;

    // Making sure all keys are numbers from 0 to length-1
    let keys = obj.getOwnPropertyNames();
    if (keys.length == 0) {
      return false;
    }
    for (let key of keys) {
      if (isNaN(key) || key != length++) {
        return false;
      }
    }

    grip.preview = {
      kind: "ArrayLike",
      length: length,
    };

    // Avoid recursive object grips.
    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let items = grip.preview.items = [];

    let i = 0;
    for (let key of keys) {
      if (rawObj.hasOwnProperty(key) && i++ < OBJECT_PREVIEW_MAX_ITEMS) {
        let value = makeDebuggeeValueIfNeeded(obj, rawObj[key]);
        items.push(hooks.createValueGrip(value));
      }
    }

    return true;
  },

  function GenericObject(objectActor, grip) {
    let {obj, hooks} = objectActor;
    if (grip.preview || grip.displayString || hooks.getGripDepth() > 1) {
      return false;
    }

    let i = 0, names = [];
    let preview = grip.preview = {
      kind: "Object",
      ownProperties: Object.create(null),
    };

    try {
      names = obj.getOwnPropertyNames();
    } catch (ex) {
      // Calling getOwnPropertyNames() on some wrapped native prototypes is not
      // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
    }

    preview.ownPropertiesLength = names.length;

    for (let name of names) {
      let desc = objectActor._propertyDescriptor(name, true);
      if (!desc) {
        continue;
      }

      preview.ownProperties[name] = desc;
      if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    if (i < OBJECT_PREVIEW_MAX_ITEMS) {
      preview.safeGetterValues = objectActor._findSafeGetterValues(
                                    Object.keys(preview.ownProperties),
                                    OBJECT_PREVIEW_MAX_ITEMS - i);
    }

    return true;
  },
];

/**
 * Call PromiseDebugging.getState on this Debugger.Object's referent and wrap
 * the resulting `value` or `reason` properties in a Debugger.Object instance.
 *
 * See dom/webidl/PromiseDebugging.webidl
 *
 * @returns Object
 *          An object of one of the following forms:
 *          - { state: "pending" }
 *          - { state: "fulfilled", value }
 *          - { state: "rejected", reason }
 */
function getPromiseState(obj) {
  if (obj.class != "Promise") {
    throw new Error(
      "Can't call `getPromiseState` on `Debugger.Object`s that don't " +
      "refer to Promise objects.");
  }

  const state = PromiseDebugging.getState(obj.unsafeDereference());
  return {
    state: state.state,
    value: obj.makeDebuggeeValue(state.value),
    reason: obj.makeDebuggeeValue(state.reason)
  };
};

/**
 * Determine if a given value is non-primitive.
 *
 * @param Any value
 *        The value to test.
 * @return Boolean
 *         Whether the value is non-primitive.
 */
function isObject(value) {
  const type = typeof value;
  return type == "object" ? value !== null : type == "function";
}

/**
 * Create a function that can safely stringify Debugger.Objects of a given
 * builtin type.
 *
 * @param Function ctor
 *        The builtin class constructor.
 * @return Function
 *         The stringifier for the class.
 */
function createBuiltinStringifier(ctor) {
  return obj => ctor.prototype.toString.call(obj.unsafeDereference());
}

/**
 * Stringify a Debugger.Object-wrapped Error instance.
 *
 * @param Debugger.Object obj
 *        The object to stringify.
 * @return String
 *         The stringification of the object.
 */
function errorStringify(obj) {
  let name = DevToolsUtils.getProperty(obj, "name");
  if (name === "" || name === undefined) {
    name = obj.class;
  } else if (isObject(name)) {
    name = stringify(name);
  }

  let message = DevToolsUtils.getProperty(obj, "message");
  if (isObject(message)) {
    message = stringify(message);
  }

  if (message === "" || message === undefined) {
    return name;
  }
  return name + ": " + message;
}

/**
 * Stringify a Debugger.Object based on its class.
 *
 * @param Debugger.Object obj
 *        The object to stringify.
 * @return String
 *         The stringification for the object.
 */
function stringify(obj) {
  if (obj.class == "DeadObject") {
    const error = new Error("Dead object encountered.");
    DevToolsUtils.reportException("stringify", error);
    return "<dead object>";
  }

  const stringifier = stringifiers[obj.class] || stringifiers.Object;

  try {
    return stringifier(obj);
  } catch (e) {
    DevToolsUtils.reportException("stringify", e);
    return "<failed to stringify object>";
  }
}

// Used to prevent infinite recursion when an array is found inside itself.
var seen = null;

var stringifiers = {
  Error: errorStringify,
  EvalError: errorStringify,
  RangeError: errorStringify,
  ReferenceError: errorStringify,
  SyntaxError: errorStringify,
  TypeError: errorStringify,
  URIError: errorStringify,
  Boolean: createBuiltinStringifier(Boolean),
  Function: createBuiltinStringifier(Function),
  Number: createBuiltinStringifier(Number),
  RegExp: createBuiltinStringifier(RegExp),
  String: createBuiltinStringifier(String),
  Object: obj => "[object " + obj.class + "]",
  Array: obj => {
    // If we're at the top level then we need to create the Set for tracking
    // previously stringified arrays.
    const topLevel = !seen;
    if (topLevel) {
      seen = new Set();
    } else if (seen.has(obj)) {
      return "";
    }

    seen.add(obj);

    const len = DevToolsUtils.getProperty(obj, "length");
    let string = "";

    // The following check is only required because the debuggee could possibly
    // be a Proxy and return any value. For normal objects, array.length is
    // always a non-negative integer.
    if (typeof len == "number" && len > 0) {
      for (let i = 0; i < len; i++) {
        const desc = obj.getOwnPropertyDescriptor(i);
        if (desc) {
          const { value } = desc;
          if (value != null) {
            string += isObject(value) ? stringify(value) : value;
          }
        }

        if (i < len - 1) {
          string += ",";
        }
      }
    }

    if (topLevel) {
      seen = null;
    }

    return string;
  },
  DOMException: obj => {
    const message = DevToolsUtils.getProperty(obj, "message") || "<no message>";
    const result = (+DevToolsUtils.getProperty(obj, "result")).toString(16);
    const code = DevToolsUtils.getProperty(obj, "code");
    const name = DevToolsUtils.getProperty(obj, "name") || "<unknown>";

    return '[Exception... "' + message + '" ' +
           'code: "' + code +'" ' +
           'nsresult: "0x' + result + ' (' + name + ')"]';
  },
  Promise: obj => {
    const { state, value, reason } = getPromiseState(obj);
    let statePreview = state;
    if (state != "pending") {
      const settledValue = state === "fulfilled" ? value : reason;
      statePreview += ": " + (typeof settledValue === "object" && settledValue !== null
                                ? stringify(settledValue)
                                : settledValue);
    }
    return "Promise (" + statePreview + ")";
  },
};

/**
 * Make a debuggee value for the given object, if needed. Primitive values
 * are left the same.
 *
 * Use case: you have a raw JS object (after unsafe dereference) and you want to
 * send it to the client. In that case you need to use an ObjectActor which
 * requires a debuggee value. The Debugger.Object.prototype.makeDebuggeeValue()
 * method works only for JS objects and functions.
 *
 * @param Debugger.Object obj
 * @param any value
 * @return object
 */
function makeDebuggeeValueIfNeeded(obj, value) {
  if (value && (typeof value == "object" || typeof value == "function")) {
    return obj.makeDebuggeeValue(value);
  }
  return value;
}

/**
 * Creates an actor for the specied "very long" string. "Very long" is specified
 * at the server's discretion.
 *
 * @param string String
 *        The string.
 */
function LongStringActor(string) {
  this.string = string;
  this.stringLength = string.length;
}

LongStringActor.prototype = {
  actorPrefix: "longString",

  disconnect: function() {
    // Because longStringActors is not a weak map, we won't automatically leave
    // it so we need to manually leave on disconnect so that we don't leak
    // memory.
    this._releaseActor();
  },

  /**
   * Returns a grip for this actor for returning in a protocol message.
   */
  grip: function() {
    return {
      "type": "longString",
      "initial": this.string.substring(
        0, DebuggerServer.LONG_STRING_INITIAL_LENGTH),
      "length": this.stringLength,
      "actor": this.actorID
    };
  },

  /**
   * Handle a request to extract part of this actor's string.
   *
   * @param request object
   *        The protocol request object.
   */
  onSubstring: function(request) {
    return {
      "from": this.actorID,
      "substring": this.string.substring(request.start, request.end)
    };
  },

  /**
   * Handle a request to release this LongStringActor instance.
   */
  onRelease: function () {
    // TODO: also check if registeredPool === threadActor.threadLifetimePool
    // when the web console moves aray from manually releasing pause-scoped
    // actors.
    this._releaseActor();
    this.registeredPool.removeActor(this);
    return {};
  },

  _releaseActor: function() {
    if (this.registeredPool && this.registeredPool.longStringActors) {
      delete this.registeredPool.longStringActors[this.string];
    }
  }
};

LongStringActor.prototype.requestTypes = {
  "substring": LongStringActor.prototype.onSubstring,
  "release": LongStringActor.prototype.onRelease
};

/**
 * Create a grip for the given debuggee value.  If the value is an
 * object, will create an actor with the given lifetime.
 */
function createValueGrip(value, pool, makeObjectGrip) {
  switch (typeof value) {
    case "boolean":
      return value;

    case "string":
      if (stringIsLong(value)) {
        return longStringGrip(value, pool);
      }
      return value;

    case "number":
      if (value === Infinity) {
        return { type: "Infinity" };
      } else if (value === -Infinity) {
        return { type: "-Infinity" };
      } else if (Number.isNaN(value)) {
        return { type: "NaN" };
      } else if (!value && 1 / value === -Infinity) {
        return { type: "-0" };
      }
      return value;

    case "undefined":
      return { type: "undefined" };

    case "object":
      if (value === null) {
        return { type: "null" };
      }
    else if (value.optimizedOut ||
             value.uninitialized ||
             value.missingArguments) {
        // The slot is optimized out, an uninitialized binding, or
        // arguments on a dead scope
        return {
          type: "null",
          optimizedOut: value.optimizedOut,
          uninitialized: value.uninitialized,
          missingArguments: value.missingArguments
        };
      }
      return makeObjectGrip(value, pool);

    case "symbol":
      let form = {
        type: "symbol"
      };
      let name = getSymbolName(value);
      if (name !== undefined) {
        form.name = createValueGrip(name, pool, makeObjectGrip);
      }
      return form;

    default:
      assert(false, "Failed to provide a grip for: " + value);
      return null;
  }
}

const symbolProtoToString = Symbol.prototype.toString;

function getSymbolName(symbol) {
  const name = symbolProtoToString.call(symbol).slice("Symbol(".length, -1);
  return name || undefined;
}

/**
 * Returns true if the string is long enough to use a LongStringActor instead
 * of passing the value directly over the protocol.
 *
 * @param str String
 *        The string we are checking the length of.
 */
function stringIsLong(str) {
  return str.length >= DebuggerServer.LONG_STRING_LENGTH;
}

/**
 * Create a grip for the given string.
 *
 * @param str String
 *        The string we are creating a grip for.
 * @param pool ActorPool
 *        The actor pool where the new actor will be added.
 */
function longStringGrip(str, pool) {
  if (!pool.longStringActors) {
    pool.longStringActors = {};
  }

  if (pool.longStringActors.hasOwnProperty(str)) {
    return pool.longStringActors[str].grip();
  }

  let actor = new LongStringActor(str);
  pool.addActor(actor);
  pool.longStringActors[str] = actor;
  return actor.grip();
}

exports.ObjectActor = ObjectActor;
exports.PropertyIteratorActor = PropertyIteratorActor;
exports.LongStringActor = LongStringActor;
exports.createValueGrip = createValueGrip;
exports.stringIsLong = stringIsLong;
exports.longStringGrip = longStringGrip;


text:
import json
import os
import urllib.parse
from typing import Tuple

import responses
from django.test import SimpleTestCase
from django.utils.http import urlencode
from requests import PreparedRequest

from importer.cli import Cli
from importer.utils import Utils
from mainapp.models import Body

class TestCli(SimpleTestCase):
    def test_warning_body_url_mismatch(self):
        body = {
            "id": "http://ratsinfo-online.net/landkreis-mol-bi/oparl/1.0/bodies.asp?id=1",
            "type": "https://schema.oparl.org/1.0/Body",
            "system": "http://ratsinfo-online.net/landkreis-mol-bi/oparl/1.0/system.asp",
            "name": "Landkreis Märkisch-Oderland",
            "shortName": "KT",
            "organization": "http://ratsinfo-online.net/landkreis-mol-bi/oparl/1.0/organizations.asp?body=1",
            "person": "http://ratsinfo-online.net/landkreis-mol-bi/oparl/1.0/persons.asp?body=1",
            "meeting": "http://ratsinfo-online.net/landkreis-mol-bi/oparl/1.0/meetings.asp?body=1",
            "paper": "http://ratsinfo-online.net/landkreis-mol-bi/oparl/1.0/papers.asp?body=1",
            "legislativeTerm": [],
            "created": "2008-01-01T12:00:00+01:00",
            "modified": "2019-02-12T15:04:33+01:00",
        }

        with responses.RequestsMock() as requests_mock:
            body_url = (
                "https://ratsinfo-online.net/landkreis-mol-bi/oparl/1.0/bodies.asp?id=1"
            )
            requests_mock.add(requests_mock.GET, body_url, json=body)

            cli = Cli()
            # Typeshed is wrong for assertLogs
            # noinspection PyTypeChecker
            with self.assertLogs(cli.__module__, level="WARNING") as cm:
                endpoint_system, endpoint_id = cli.get_endpoint_from_body_url(body_url)
                self.assertEqual(endpoint_system, body["system"])
                self.assertEqual(endpoint_id, body["id"])
                self.assertEqual(
                    cm.output,
                    [
                        "WARNING:{}:The body's url '{}' doesn't match the body's id '{}'".format(
                            cli.__module__, body_url, body["id"]
                        )
                    ],
                )

    def request_callback(self, request: PreparedRequest) -> Tuple[int, dict, str]:
        bedburg = {
            "head": {"vars": ["city", "cityLabel", "ags"]},
            "results": {
                "bindings": [
                    {
                        "city": {
                            "type": "uri",
                            "value": "http://www.wikidata.org/entity/Q245292",
                        },
                        "ags": {"type": "literal", "value": "05362004"},
                        "cityLabel": {
                            "xml:lang": "de",
                            "type": "literal",
                            "value": "Bedburg",
                        },
                    }
                ]
            },
        }

        empty = {
            "head": {"vars": ["city", "cityLabel", "ags"]},
            "results": {"bindings": []},
        }

        payload = urllib.parse.parse_qs(urllib.parse.urlparse(request.url).query)
        if '"Bedburg"' in payload["query"][0]:
            return 200, {}, json.dumps(bedburg)

        return 200, {}, json.dumps(empty)

    def test_city_to_ags(self):
        with responses.RequestsMock() as requests_mock:
            requests_mock.add_callback(
                requests_mock.GET,
                "https://query.wikidata.org/sparql",
                callback=self.request_callback,
                content_type="application/json",
            )

            expected = ("05362004", "Bedburg")

            body = Body()
            # Example: https://www.lwl-pch.sitzung-online.de/oi/oparl/1.0/bodies.asp?id=2
            body.short_name = "KT"
            body.name = "Unknown"

            cli = Cli()
            with self.assertRaisesMessage(
                RuntimeError, "Could not determine the Amtliche Gemeindeschlüssel"
            ):
                cli.get_ags(body, {}, "Unknown")

            # System name
            ags = cli.get_ags(body, {"name": "Stadt Bedburg"}, "Unknown")
            self.assertEqual(ags, expected)

            # Userinput
            ags = cli.get_ags(body, {}, "Bedburg")
            self.assertEqual(ags, expected)

            # Body name
            body.name = "Bedburg"
            ags = cli.get_ags(body, {}, "Unknown")
            self.assertEqual(ags, expected)

    def test_ags_and_short_name(self):
        with open("testdata/body_to_ags.json") as fp:
            expected = json.load(fp)

        actual = {}

        with responses.RequestsMock() as requests_mock:
            # Prerecorded responses from wikidata
            with open("testdata/wikidata_sparql_responses.json") as fp:
                saved_responses = json.load(fp)
            for saved_response in saved_responses:
                requests_mock.add(
                    requests_mock.GET,
                    "https://query.wikidata.org/sparql?"
                    + urlencode(saved_response["params"]),
                    json=saved_response["response"],
                )

            cli = Cli()
            for file in sorted(os.listdir("testdata/bodies")):
                with open(os.path.join("testdata/bodies", file)) as fp:
                    data = json.load(fp)
                with open(os.path.join("testdata/systems", file)) as fp:
                    system = json.load(fp)
                body = Body()
                body.ags = data.get("ags")
                body.name = data["name"]
                body.short_name = data.get("shortName") or data["name"]
                body.short_name = Utils().normalize_body_name(body.short_name)

                if body.ags:
                    if len(body.ags) == 7:
                        body.ags = "0" + body.ags
                else:
                    ags, match_name = cli.get_ags(body, system, data["id"])
                    body.ags = ags
                    # Sometimes there's a bad short name (e.g. "Rat" for Erkelenz),
                    # so we use the name that's in wikidata instead
                    body.short_name = match_name

                actual[file] = {"ags": body.ags, "short_name": body.short_name}
            self.assertEqual(expected, actual)

redpajama_set_name: RedPajamaGithub


text:
<module xmlns="urn:jboss:module:1.3" name="org.wildfly.swarm.datasources" slot="runtime">
  <resources>
    <artifact name="org.wildfly.swarm:wildfly-swarm-datasources-runtime:${project.version}"/>
  </resources>

  <dependencies>
    <module name="org.wildfly.swarm.datasources"/>
    <module name="org.wildfly.swarm.container"/>
    <module name="org.wildfly.swarm.container" slot="runtime"/>
    <module name="org.wildfly.swarm.configuration"/>

    <module name="org.jboss.as.connector"/>
  </dependencies>
</module>


text:
/**
 * Created by Lzientek on 01-10-2016
 */

import {Injectable} from '@angular/core';

import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import Room from "../models/Room";

@Injectable()
export class RoomService {

    private roomsUrl = 'api/rooms';  // URL to web api

    constructor(private http: Http) { }

    getRooms(): Promise<Room[]> {
        return this.http.get(this.roomsUrl)
            .toPromise()
            .then(response => response.json())
            .catch(this.handleError);
    }

    getRoom(id: string) {
        return this.http.get(this.roomsUrl + '/' + id)
            .toPromise()
            .then(response => response.json())
            .catch(this.handleError);
    }

    save(room: Room): Promise<Room>  {
        if (room._id) {
            return this.put(room);
        }
        return this.post(room);
    }

    private post(room: Room): Promise<Room> {
        let headers = new Headers({
            'Content-Type': 'application/json'});

        return this.http
            .post(this.roomsUrl, JSON.stringify(room), {headers: headers})
            .toPromise()
            .then(response =>  response.json())
            .catch(this.handleError);
    }

    private put(room: Room): Promise<Room> {
        let headers = new Headers();
        headers.append('Content-Type', 'application/json');

        let url = `${this.roomsUrl}/${room._id}`;

        return this.http
            .put(url, JSON.stringify(room), {headers: headers})
            .toPromise()
            .then(() => room)
            .catch(this.handleError);
    }

    delete(room: Room) {
        let headers = new Headers();
        headers.append('Content-Type', 'application/json');

        let url = `${this.roomsUrl}/${room._id}`;

        return this.http
            .delete(url, headers)
            .toPromise()
            .catch(this.handleError);
    }

    private handleError(error: any) {
        console.error('An error occurred', error);
        return Promise.reject(error.message || error);
    }
}


text:
package com.googlecode.lanterna.terminal;

import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.screen.TerminalScreen;
import com.googlecode.lanterna.terminal.ansi.CygwinTerminal;
import com.googlecode.lanterna.terminal.ansi.TelnetTerminal;
import com.googlecode.lanterna.terminal.ansi.TelnetTerminalServer;
import com.googlecode.lanterna.terminal.ansi.UnixLikeTTYTerminal;
import com.googlecode.lanterna.terminal.ansi.UnixTerminal;
import com.googlecode.lanterna.terminal.swing.*;

import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.EnumSet;

import org.vash.vate.nativeutils.VTNativeUtils;

/**
 * This TerminalFactory implementation uses a simple auto-detection mechanism for figuring out which terminal
 * implementation to create based on characteristics of the system the program is running on.
 * <p>
 * Note that for all systems with a graphical environment present, the SwingTerminalFrame will be chosen. You can
 * suppress this by calling setForceTextTerminal(true) on this factory.
 * @author martin
 */
public class DefaultTerminalFactory implements TerminalFactory {
    private static final OutputStream DEFAULT_OUTPUT_STREAM = System.out;
    private static final InputStream DEFAULT_INPUT_STREAM = System.in;
    private static final Charset DEFAULT_CHARSET = Charset.defaultCharset();

    private final OutputStream outputStream;
    private final InputStream inputStream;
    private final Charset charset;

    private TerminalSize initialTerminalSize;
    private boolean forceTextTerminal;
    private boolean preferTerminalEmulator;
    private boolean forceAWTOverSwing;
    private int telnetPort;
    private int inputTimeout;
    private String title;
    private boolean autoOpenTerminalFrame;
    private final EnumSet<TerminalEmulatorAutoCloseTrigger> autoCloseTriggers;
    private TerminalEmulatorColorConfiguration colorConfiguration;
    private TerminalEmulatorDeviceConfiguration deviceConfiguration;
    private AWTTerminalFontConfiguration fontConfiguration;
    private MouseCaptureMode mouseCaptureMode;
    private UnixTerminal.CtrlCBehaviour unixTerminalCtrlCBehaviour;

    /**
     * Creates a new DefaultTerminalFactory with all properties set to their defaults
     */
    public DefaultTerminalFactory() {
        this(DEFAULT_OUTPUT_STREAM, DEFAULT_INPUT_STREAM, DEFAULT_CHARSET);
    }

    /**
     * Creates a new DefaultTerminalFactory with I/O and character set options customisable.
     * @param outputStream Output stream to use for text-based Terminal implementations
     * @param inputStream Input stream to use for text-based Terminal implementations
     * @param charset Character set to assume the client is using
     */
    @SuppressWarnings({"SameParameterValue", "WeakerAccess"})
    public DefaultTerminalFactory(OutputStream outputStream, InputStream inputStream, Charset charset) {
        this.outputStream = outputStream;
        this.inputStream = inputStream;
        this.charset = charset;

        this.forceTextTerminal = false;
        this.preferTerminalEmulator = false;
        this.forceAWTOverSwing = false;

        this.telnetPort = -1;
        this.inputTimeout = -1;
        this.autoOpenTerminalFrame = true;
        this.title = null;
        this.autoCloseTriggers = EnumSet.of(TerminalEmulatorAutoCloseTrigger.CloseOnExitPrivateMode);
        this.mouseCaptureMode = null;
        this.unixTerminalCtrlCBehaviour = UnixTerminal.CtrlCBehaviour.CTRL_C_KILLS_APPLICATION;

        //SwingTerminal will replace these null values for the default implementation if they are unchanged
        this.colorConfiguration = null;
        this.deviceConfiguration = null;
        this.fontConfiguration = null;
    }

    public Terminal createTerminal() throws IOException {
        // 3 different reasons for tty-based terminal:
        //   "explicit preference", "no alternative",
        //       ("because we can" - unless "rather not")

    	if (checkConsoleIsatty())
    	{
	        if (forceTextTerminal || isAwtHeadless() ||
	                !preferTerminalEmulator) {
	            // if tty but have no tty, but do have a port, then go telnet:
	            if( telnetPort > 0) {
	                return createTelnetTerminal();
	            }
	            if(isOperatingSystemWindows()) {
	            	//System.out.println("createWindowsTerminal()");
	                return createWindowsTerminal();
	            	//return createUnixTerminal(outputStream, inputStream, charset);
	            }
	            else {
	                return createUnixTerminal(outputStream, inputStream, charset);
	            }
	        }
	        else {
	            // while Lanterna's TerminalEmulator lacks mouse support:
	            // if user wanted mouse AND set a telnetPort, and didn't
	            //   explicitly ask for a graphical Terminal, then go telnet:
	            if (!preferTerminalEmulator && mouseCaptureMode != null && telnetPort > 0) {
	                return createTelnetTerminal();
	            } else {
	                return createTerminalEmulator();
	            }
	        }
    	}
    	else
    	{
    		if ((forceTextTerminal || isAwtHeadless()) && !preferTerminalEmulator) {
	            // if tty but have no tty, but do have a port, then go telnet:
	            if( telnetPort > 0) {
	                return createTelnetTerminal();
	            }
	            if(isOperatingSystemWindows()) {
	            	return createWindowsTerminal();
	                //return createWindowsTerminal(outputStream, inputStream, charset);
	            	//return createUnixTerminal(outputStream, inputStream, charset);
	            }
	            else {
	                return createUnixTerminal(outputStream, inputStream, charset);
	            }
	        }
	        else {
	            // while Lanterna's TerminalEmulator lacks mouse support:
	            // if user wanted mouse AND set a telnetPort, and didn't
	            //   explicitly ask for a graphical Terminal, then go telnet:
	            if (!preferTerminalEmulator && mouseCaptureMode != null && telnetPort > 0) {
	                return createTelnetTerminal();
	            } else {
	                return createTerminalEmulator();
	            }
	        }
    	}
    }

    /**
     * Creates a new terminal emulator window which will be either Swing-based or AWT-based depending on what is
     * available on the system
     * @return New terminal emulator exposed as a {@link Terminal} interface
     */
    public Terminal createTerminalEmulator() {
        Terminal terminal;
        if (!forceAWTOverSwing && hasSwing()) {
            terminal = createSwingTerminal();
        } else {
            terminal = createAWTTerminal();
        }

        if (autoOpenTerminalFrame) {
            makeWindowVisible(terminal);
        }
        return terminal;
    }

    public AWTTerminalFrame createAWTTerminal() {
        return new AWTTerminalFrame(
                title,
                initialTerminalSize,
                deviceConfiguration,
                fontConfiguration,
                colorConfiguration,
                autoCloseTriggers.toArray(new TerminalEmulatorAutoCloseTrigger[autoCloseTriggers.size()]));
    }

    public SwingTerminalFrame createSwingTerminal() {
        return new SwingTerminalFrame(
                title,
                initialTerminalSize,
                deviceConfiguration,
                fontConfiguration instanceof SwingTerminalFontConfiguration ? (SwingTerminalFontConfiguration)fontConfiguration : null,
                colorConfiguration,
                autoCloseTriggers.toArray(new TerminalEmulatorAutoCloseTrigger[autoCloseTriggers.size()]));
    }

    /**
     * Creates a new TelnetTerminal
     *
     * Note: a telnetPort should have been set with setTelnetPort(),
     * otherwise creation of TelnetTerminal will most likely fail.
     *
     * @return New terminal emulator exposed as a {@link Terminal} interface
     */
    public TelnetTerminal createTelnetTerminal() {
        try {
            System.err.print("Waiting for incoming telnet connection on port "+telnetPort+" ... ");
            System.err.flush();

            TelnetTerminalServer tts = new TelnetTerminalServer(telnetPort);
            TelnetTerminal rawTerminal = tts.acceptConnection();
            tts.close(); // Just for single-shot: free up the port!

            System.err.println("Ok, got it!");

            if(mouseCaptureMode != null) {
                rawTerminal.setMouseCaptureMode(mouseCaptureMode);
            }
            if(inputTimeout >= 0) {
                rawTerminal.getInputDecoder().setTimeoutUnits(inputTimeout);
            }
            return rawTerminal;
        } catch(IOException ioe) {
            throw new RuntimeException(ioe);
        }
    }

    /**
     * Instantiates a Terminal according to the factory implementation with the exception that
     * {@link DefaultTerminalFactory#preferTerminalEmulator} is always ignored. You may want to use this method when
     * using tools that rely on AOT compilation such as Graal native-image to ensure AWT/Swing code paths are not hit.
     * @return Terminal implementation
     * @throws IOException If there was an I/O error with the underlying input/output system
     */
    public Terminal createHeadlessTerminal() throws Throwable {
        // if tty but have no tty, but do have a port, then go telnet:
        if( telnetPort > 0 && !checkConsoleIsatty()) {
            return createTelnetTerminal();
        }
        if(isOperatingSystemWindows()) {
            return createWindowsTerminal();
        }

        return createUnixTerminal(outputStream, inputStream, charset);
    }

    private boolean isAwtHeadless() {
        try {
            Class cls = Class.forName("java.awt.GraphicsEnvironment");
            Method method = cls.getDeclaredMethod("isHeadless");
            return (Boolean) method.invoke(null);
        } catch (Exception ignore) {
            // Most likely cause is that the java.desktop module is not available in the runtime image.
            return true;
        }
    }

    private boolean hasSwing() {
        try {
            Class.forName("javax.swing.JComponent");
            return true;
        }
        catch(Exception ignore) {
            return false;
        }
    }

    private void makeWindowVisible(Terminal terminal) {
        try {
            Class cls = Class.forName("java.awt.Window");
            Method method = cls.getDeclaredMethod("setVisible", boolean.class);
            method.invoke(terminal, true);
        } catch (Exception ex) {
            throw new RuntimeException("Failed to make terminal emulator window visible.", ex);
        }
    }

    /**
     * Sets a hint to the TerminalFactory of what size to use when creating the terminal. Most terminals are not created
     * on request but for example the SwingTerminal and SwingTerminalFrame are and this value will be passed down on
     * creation.
     * @param initialTerminalSize Size (in rows and columns) of the newly created terminal
     * @return Reference to itself, so multiple .set-calls can be chained
     */
    public DefaultTerminalFactory setInitialTerminalSize(TerminalSize initialTerminalSize) {
        this.initialTerminalSize = initialTerminalSize;
        return this;
    }

    /**
     * Controls whether a text-based Terminal shall be created even if the system
     *    supports a graphical environment
     * @param forceTextTerminal If true, will always create a text-based Terminal
     * @return Reference to itself, so multiple .set-calls can be chained
     */
    public DefaultTerminalFactory setForceTextTerminal(boolean forceTextTerminal) {
        this.forceTextTerminal = forceTextTerminal;
        return this;
    }

    /**
     * Controls whether a Swing or AWT TerminalFrame shall be preferred if the system
     *    has both a Console and a graphical environment
     * @param preferTerminalEmulator If true, will prefer creating a graphical terminal emulator
     * @return Reference to itself, so multiple .set-calls can be chained
     */
    public DefaultTerminalFactory setPreferTerminalEmulator(boolean preferTerminalEmulator) {
        this.preferTerminalEmulator = preferTerminalEmulator;
        return this;
    }

    /**
     * Sets the default CTRL-C behavior to use for all {@link UnixTerminal} objects created by this factory. You can
     * use this to tell Lanterna to trap CTRL-C instead of exiting the application. Non-UNIX terminals are not affected
     * by this.
     * @param unixTerminalCtrlCBehaviour CTRL-C behavior to use for {@link UnixTerminal}:s
     */
    public DefaultTerminalFactory setUnixTerminalCtrlCBehaviour(UnixTerminal.CtrlCBehaviour unixTerminalCtrlCBehaviour) {
        this.unixTerminalCtrlCBehaviour = unixTerminalCtrlCBehaviour;
        return this;
    }

    /**
     * Primarily for debugging applications with mouse interactions:
     * If no Console is available (e.g. from within an IDE), then fall
     * back to TelnetTerminal on specified port.
     *
     * If both a non-null mouseCapture mode and a positive telnetPort
     * are specified, then as long as Swing/AWT Terminal emulators do
     * not support MouseCapturing, a TelnetTerminal will be preferred
     * over the graphical Emulators.
     *
     * @param telnetPort the TCP/IP port on which to eventually wait for a connection.
     *         A value less or equal 0 disables creation of a TelnetTerminal.
     *         Note, that ports less than 1024 typically require system
     *         privileges to listen on.
     * @return Reference to itself, so multiple .set-calls can be chained
     */
    public DefaultTerminalFactory setTelnetPort(int telnetPort) {
        this.telnetPort = telnetPort;
        return this;
    }

    /**
     * Only for StreamBasedTerminals: After seeing e.g. an Escape (but nothing
     *         else yet), wait up to the specified number of time units for more
     *         bytes to make up a complete sequence. This may be necessary on
     *         slow channels, or if some client terminal sends each byte of a
     *         sequence in its own TCP packet.
     *
     * @param inputTimeout how long to wait for possible completions of sequences.
     *         units are of a 1/4 second, so e.g. 12 would wait up to 3 seconds.
     * @return Reference to itself, so multiple .set-calls can be chained
     */
    public DefaultTerminalFactory setInputTimeout(int inputTimeout) {
        this.inputTimeout = inputTimeout;
        return this;
    }

    /**
     * Normally when a graphical terminal emulator is created by the factory, it will create a
     * {@link SwingTerminalFrame} unless Swing is not present in the system. Setting this property to {@code true} will
     * make it create an {@link AWTTerminalFrame} even if Swing is present
     * @param forceAWTOverSwing If {@code true}, will always create an {@link AWTTerminalFrame} over a
     * {@link SwingTerminalFrame} if asked to create a graphical terminal emulator
     * @return Reference to itself, so multiple .set-calls can be chained
     */
    public DefaultTerminalFactory setForceAWTOverSwing(boolean forceAWTOverSwing) {
        this.forceAWTOverSwing = forceAWTOverSwing;
        return this;
    }

    /**
     * Controls whether a SwingTerminalFrame shall be automatically shown (.setVisible(true)) immediately after
     * creation. If {@code false}, you will manually need to call {@code .setVisible(true)} on the JFrame to actually
     * see the terminal window. Default for this value is {@code true}.
     * @param autoOpenTerminalFrame Automatically open SwingTerminalFrame after creation
     * @return Itself
     */
    public DefaultTerminalFactory setAutoOpenTerminalEmulatorWindow(boolean autoOpenTerminalFrame) {
        this.autoOpenTerminalFrame = autoOpenTerminalFrame;
        return this;
    }

    /**
     * Sets the title to use on created SwingTerminalFrames created by this factory
     * @param title Title to use on created SwingTerminalFrames created by this factory
     * @return Reference to itself, so multiple .set-calls can be chained
     */
    public DefaultTerminalFactory setTerminalEmulatorTitle(String title) {
        this.title = title;
        return this;
    }

    /**
     * Sets the auto-close trigger to use on created SwingTerminalFrames created by this factory. This will reset any
     * previous triggers. If called with {@code null}, all triggers are cleared.
     * @param autoCloseTrigger Auto-close trigger to use on created SwingTerminalFrames created by this factory, or {@code null} to clear all existing triggers
     * @return Reference to itself, so multiple .set-calls can be chained
     */
    public DefaultTerminalFactory setTerminalEmulatorFrameAutoCloseTrigger(TerminalEmulatorAutoCloseTrigger autoCloseTrigger) {
        this.autoCloseTriggers.clear();
        if(autoCloseTrigger != null) {
            this.autoCloseTriggers.add(autoCloseTrigger);
        }
        return this;
    }

    /**
     * Adds an auto-close trigger to use on created SwingTerminalFrames created by this factory
     * @param autoCloseTrigger Auto-close trigger to add to the created SwingTerminalFrames created by this factory
     * @return Reference to itself, so multiple calls can be chained
     */
    public DefaultTerminalFactory addTerminalEmulatorFrameAutoCloseTrigger(TerminalEmulatorAutoCloseTrigger autoCloseTrigger) {
        if(autoCloseTrigger != null) {
            this.autoCloseTriggers.add(autoCloseTrigger);
        }
        return this;
    }

    /**
     * Sets the color configuration to use on created SwingTerminalFrames created by this factory
     * @param colorConfiguration Color configuration to use on created SwingTerminalFrames created by this factory
     * @return Reference to itself, so multiple .set-calls can be chained
     */
    public DefaultTerminalFactory setTerminalEmulatorColorConfiguration(TerminalEmulatorColorConfiguration colorConfiguration) {
        this.colorConfiguration = colorConfiguration;
        return this;
    }

    /**
     * Sets the device configuration to use on created SwingTerminalFrames created by this factory
     * @param deviceConfiguration Device configuration to use on created SwingTerminalFrames created by this factory
     * @return Reference to itself, so multiple .set-calls can be chained
     */
    public DefaultTerminalFactory setTerminalEmulatorDeviceConfiguration(TerminalEmulatorDeviceConfiguration deviceConfiguration) {
        this.deviceConfiguration = deviceConfiguration;
        return this;
    }

    /**
     * Sets the font configuration to use on created SwingTerminalFrames created by this factory
     * @param fontConfiguration Font configuration to use on created SwingTerminalFrames created by this factory
     * @return Reference to itself, so multiple .set-calls can be chained
     */
    public DefaultTerminalFactory setTerminalEmulatorFontConfiguration(AWTTerminalFontConfiguration fontConfiguration) {
        this.fontConfiguration = fontConfiguration;
        return this;
    }

    /**
     * Sets the mouse capture mode the terminal should use. Please note that this is an extension which isn't widely
     * supported!
     *
     * If both a non-null mouseCapture mode and a positive telnetPort
     * are specified, then as long as Swing/AWT Terminal emulators do
     * not support MouseCapturing, a TelnetTerminal will be preferred
     * over the graphical Emulators.
     *
     * @param mouseCaptureMode Capture mode for mouse interactions
     * @return Itself
     */
    public DefaultTerminalFactory setMouseCaptureMode(MouseCaptureMode mouseCaptureMode) {
        this.mouseCaptureMode = mouseCaptureMode;
        return this;
    }

    /**
     * Create a {@link Terminal} and immediately wrap it up in a {@link TerminalScreen}
     * @return New {@link TerminalScreen} created with a terminal from {@link #createTerminal()}
     * @throws IOException In case there was an I/O error
     */
    public TerminalScreen createScreen() throws IOException {
        return new TerminalScreen(createTerminal());
    }

    private Terminal createWindowsTerminal() throws IOException {
        try {
            Class<?> nativeImplementation = Class.forName("com.googlecode.lanterna.terminal.win32.WindowsTerminal");
            Constructor<?> constructor = nativeImplementation.getConstructor(InputStream.class, OutputStream.class, Charset.class, UnixLikeTTYTerminal.CtrlCBehaviour.class);
            return (Terminal)constructor.newInstance(inputStream, outputStream, charset, UnixLikeTTYTerminal.CtrlCBehaviour.CTRL_C_KILLS_APPLICATION);
        }
        catch(Throwable t) {
            try {
                return createCygwinTerminal(outputStream, inputStream, charset);
            } catch(IOException e) {
                throw new IOException("To use Lanterna on Windows, either add JNA (and jna-platform) to the classpath or use javaw! (see https://github.com/mabe02/lanterna/issues/335)");
            }
        }
    }

//    private Terminal createWindowsTerminal(OutputStream outputStream, InputStream inputStream, Charset charset) throws IOException {
//        try {
//            Class<?> nativeImplementation = Class.forName("com.googlecode.lanterna.terminal.WindowsTerminal");
//            Constructor<?> constructor = nativeImplementation.getConstructor(InputStream.class, OutputStream.class, Charset.class, UnixLikeTTYTerminal.CtrlCBehaviour.class);
//            return (Terminal)constructor.newInstance(inputStream, outputStream, charset, UnixLikeTTYTerminal.CtrlCBehaviour.CTRL_C_KILLS_APPLICATION);
//        }
//        catch(Exception ignore) {
//            try {
//                return createCygwinTerminal(outputStream, inputStream, charset);
//            } catch(IOException e) {
//            	throw e;
//                //throw new IOException("To start java on Windows, use javaw! (see https://github.com/mabe02/lanterna/issues/335 )", e);
//            }
//        }
//    }

    private Terminal createCygwinTerminal(OutputStream outputStream, InputStream inputStream, Charset charset) throws IOException {
        CygwinTerminal cygTerminal = new CygwinTerminal(inputStream, outputStream, charset);
        if(inputTimeout >= 0) {
            cygTerminal.getInputDecoder().setTimeoutUnits(inputTimeout);
        }
        return cygTerminal;
    }

    private Terminal createUnixTerminal(OutputStream outputStream, InputStream inputStream, Charset charset) throws IOException {
        UnixTerminal unixTerminal;
//        try {
//            Class<?> nativeImplementation = Class.forName("com.googlecode.lanterna.terminal.NativeGNULinuxTerminal");
//            Constructor<?> constructor = nativeImplementation.getConstructor(InputStream.class, OutputStream.class, Charset.class, UnixLikeTTYTerminal.CtrlCBehaviour.class);
//            unixTerminal = (UnixTerminal)constructor.newInstance(inputStream, outputStream, charset, unixTerminalCtrlCBehaviour);
//        }
//        catch(Exception ignore) {
//            unixTerminal = new UnixTerminal(inputStream, outputStream, charset, unixTerminalCtrlCBehaviour);
//        }
        unixTerminal = new UnixTerminal(inputStream, outputStream, charset, unixTerminalCtrlCBehaviour);
        if(mouseCaptureMode != null) {
            unixTerminal.setMouseCaptureMode(mouseCaptureMode);
        }
        if(inputTimeout >= 0) {
            unixTerminal.getInputDecoder().setTimeoutUnits(inputTimeout);
        }
        return unixTerminal;
    }

    /**
     * Detects whether the running platform is Windows* by looking at the
     * operating system name system property
     */
    private static boolean isOperatingSystemWindows() {
        return System.getProperty("os.name", "").toLowerCase().contains("windows");
    }

	public static boolean checkConsoleIsatty()
	{
		try
		{
			Class.forName("java.io.Console");
			Class<?> systemClass = Class.forName("java.lang.System");
			Method consoleMethod = systemClass.getDeclaredMethod("console");
			//consoleMethod.setAccessible(true);
			Object consoleResult = consoleMethod.invoke(null);
			if (consoleResult != null)
			{
				return VTNativeUtils.isatty(0) != 0 && VTNativeUtils.isatty(1) != 0;
			}
			try
			{
				if (FileDescriptor.in.valid())
				{
					FileDescriptor.in.sync();
					return VTNativeUtils.isatty(0) != 0 && VTNativeUtils.isatty(1) != 0;
				}
				else
				{

				}
			}
			catch (Throwable e)
			{

			}
		}
		catch (Throwable e)
		{
		  try
      {
        if (FileDescriptor.in.valid())
        {
          FileDescriptor.in.sync();
          return VTNativeUtils.isatty(0) != 0 && VTNativeUtils.isatty(1) != 0;
        }
        else
        {

        }
      }
      catch (Throwable ex)
      {

      }
		}
		return false;
	}
}