Bug 1313767: Deobfuscate core/heritage.js r=rpl draft
authorKris Maglione <maglione.k@gmail.com>
Sun, 30 Oct 2016 16:47:27 -0700
changeset 551373 6209308005f1d36dfbd95a0976c6712ccfecf53c
parent 551372 26e8bd522e086b0e089f2bc1885ef932c9e1f249
child 621534 ef6a8eb7b960873aab8e83d188b04df5ad3a5e1b
push id51040
push usermaglione.k@gmail.com
push dateSat, 25 Mar 2017 22:06:50 +0000
reviewersrpl
bugs1313767
milestone55.0a1
Bug 1313767: Deobfuscate core/heritage.js r=rpl MozReview-Commit-ID: 5raSFnOflxq
addon-sdk/source/lib/sdk/core/heritage.js
--- a/addon-sdk/source/lib/sdk/core/heritage.js
+++ b/addon-sdk/source/lib/sdk/core/heritage.js
@@ -3,182 +3,171 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 module.metadata = {
   "stability": "unstable"
 };
 
 var getPrototypeOf = Object.getPrototypeOf;
-var getNames = x => [...Object.getOwnPropertyNames(x),
-                     ...Object.getOwnPropertySymbols(x)];
+function* getNames(x) {
+  yield* Object.getOwnPropertyNames(x);
+  yield* Object.getOwnPropertySymbols(x);
+}
 var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
-var create = Object.create;
 var freeze = Object.freeze;
-var unbind = Function.call.bind(Function.bind, Function.call);
 
 // This shortcut makes sure that we do perform desired operations, even if
 // associated methods have being overridden on the used object.
-var owns = unbind(Object.prototype.hasOwnProperty);
-var apply = unbind(Function.prototype.apply);
-var slice = Array.slice || unbind(Array.prototype.slice);
-var reduce = Array.reduce || unbind(Array.prototype.reduce);
-var map = Array.map || unbind(Array.prototype.map);
-var concat = Array.concat || unbind(Array.prototype.concat);
+var hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);
 
 // Utility function to get own properties descriptor map.
-function getOwnPropertyDescriptors(object) {
-  return reduce(getNames(object), function(descriptor, name) {
-    descriptor[name] = getOwnPropertyDescriptor(object, name);
-    return descriptor;
-  }, {});
+function getOwnPropertyDescriptors(...objects) {
+  let descriptors = {};
+  for (let object of objects)
+    for (let name of getNames(object))
+      descriptors[name] = getOwnPropertyDescriptor(object, name);
+  return descriptors;
 }
 
 function isDataProperty(property) {
-  var value = property.value;
   var type = typeof(property.value);
   return "value" in property &&
-         (type !== "object" || value === null) &&
-         type !== "function";
+         type !== "function" &&
+         (type !== "object" || property.value === null);
 }
 
 function getDataProperties(object) {
   var properties = getOwnPropertyDescriptors(object);
-  return getNames(properties).reduce(function(result, name) {
+  let result = {};
+  for (let name of getNames(properties)) {
     var property = properties[name];
     if (isDataProperty(property)) {
       result[name] = {
         value: property.value,
         writable: true,
         configurable: true,
         enumerable: false
       };
     }
-    return result;
-  }, {})
+  }
+  return result;
 }
 
 /**
  * Takes `source` object as an argument and returns identical object
  * with the difference that all own properties will be non-enumerable
  */
-function obscure(source) {
-  var descriptor = reduce(getNames(source), function(descriptor, name) {
-    var property = getOwnPropertyDescriptor(source, name);
+function obscure(source, prototype = getPrototypeOf(source)) {
+  let descriptors = {};
+  for (let name of getNames(source)) {
+    let property = getOwnPropertyDescriptor(source, name);
     property.enumerable = false;
-    descriptor[name] = property;
-    return descriptor;
-  }, {});
-  return create(getPrototypeOf(source), descriptor);
+    descriptors[name] = property;
+  }
+  return Object.create(prototype, descriptors);
 }
 exports.obscure = obscure;
 
 /**
  * Takes arbitrary number of source objects and returns fresh one, that
  * inherits from the same prototype as a first argument and implements all
  * own properties of all argument objects. If two or more argument objects
  * have own properties with the same name, the property is overridden, with
  * precedence from right to left, implying, that properties of the object on
  * the left are overridden by a same named property of the object on the right.
  */
-var mix = function(source) {
-  var descriptor = reduce(slice(arguments), function(descriptor, source) {
-    return reduce(getNames(source), function(descriptor, name) {
-      descriptor[name] = getOwnPropertyDescriptor(source, name);
-      return descriptor;
-    }, descriptor);
-  }, {});
-
-  return create(getPrototypeOf(source), descriptor);
+var mix = function(...sources) {
+  return Object.create(getPrototypeOf(sources[0]),
+                       getOwnPropertyDescriptors(...sources));
 };
 exports.mix = mix;
 
 /**
  * Returns a frozen object with that inherits from the given `prototype` and
  * implements all own properties of the given `properties` object.
  */
 function extend(prototype, properties) {
-  return create(prototype, getOwnPropertyDescriptors(properties));
+  return Object.create(prototype,
+                       getOwnPropertyDescriptors(properties));
 }
 exports.extend = extend;
 
+function prototypeOf(input) {
+  return typeof(input) === 'function' ? input.prototype : input;
+}
+
 /**
  * Returns a constructor function with a proper `prototype` setup. Returned
  * constructor's `prototype` inherits from a given `options.extends` or
  * `Class.prototype` if omitted and implements all the properties of the
  * given `option`. If `options.implemens` array is passed, it's elements
  * will be mixed into prototype as well. Also, `options.extends` can be
  * a function or a prototype. If function than it's prototype is used as
  * an ancestor of the prototype, if it's an object that it's used directly.
  * Also `options.implements` may contain functions or objects, in case of
  * functions their prototypes are used for mixing.
  */
-var Class = new function() {
-  function prototypeOf(input) {
-    return typeof(input) === 'function' ? input.prototype : input;
-  }
-  var none = freeze([]);
+function Class(options) {
+  // Create descriptor with normalized `options.extends` and
+  // `options.implements`.
+  var descriptor = {
+    // Normalize extends property of `options.extends` to a prototype object
+    // in case it's constructor. If property is missing that fallback to
+    // `Type.prototype`.
+    extends: hasOwnProperty(options, 'extends') ?
+             prototypeOf(options.extends) : Class.prototype,
 
-  return function Class(options) {
-    // Create descriptor with normalized `options.extends` and
-    // `options.implements`.
-    var descriptor = {
-      // Normalize extends property of `options.extends` to a prototype object
-      // in case it's constructor. If property is missing that fallback to
-      // `Type.prototype`.
-      extends: owns(options, 'extends') ?
-               prototypeOf(options.extends) : Class.prototype,
-      // Normalize `options.implements` to make sure that it's array of
-      // prototype objects instead of constructor functions.
-      implements: owns(options, 'implements') ?
-                  freeze(map(options.implements, prototypeOf)) : none
-    };
+    // Normalize `options.implements` to make sure that it's array of
+    // prototype objects instead of constructor functions.
+    implements: freeze(hasOwnProperty(options, 'implements') ?
+                       options.implements.map(prototypeOf) : []),
+  };
 
-    // Create array of property descriptors who's properties will be defined
-    // on the resulting prototype. Note: Using reflection `concat` instead of
-    // method as it may be overridden.
-    var descriptors = concat(descriptor.implements, options, descriptor, {
-      constructor: constructor
-    });
+  // Create array of property descriptors who's properties will be defined
+  // on the resulting prototype.
+  var descriptors = [].concat(descriptor.implements, options, descriptor,
+                              { constructor });
 
-    // Note: we use reflection `apply` in the constructor instead of method
-    // call since later may be overridden.
-    function constructor() {
-      var instance = create(prototype, attributes);
-      if (initialize) apply(initialize, instance, arguments);
-      return instance;
-    }
-    // Create `prototype` that inherits from given ancestor passed as
-    // `options.extends`, falling back to `Type.prototype`, implementing all
-    // properties of given `options.implements` and `options` itself.
-    var prototype = extend(descriptor.extends, mix.apply(mix, descriptors));
-    var initialize = prototype.initialize;
+  // Note: we use reflection `apply` in the constructor instead of method
+  // call since later may be overridden.
+  function constructor() {
+    var instance = Object.create(prototype, attributes);
+    if (initialize)
+      Reflect.apply(initialize, instance, arguments);
+    return instance;
+  }
+  // Create `prototype` that inherits from given ancestor passed as
+  // `options.extends`, falling back to `Type.prototype`, implementing all
+  // properties of given `options.implements` and `options` itself.
+  var prototype = Object.create(descriptor.extends,
+                                getOwnPropertyDescriptors(...descriptors));
+  var initialize = prototype.initialize;
 
-    // Combine ancestor attributes with prototype's attributes so that
-    // ancestors attributes also become initializeable.
-    var attributes = mix(descriptor.extends.constructor.attributes || {},
-                         getDataProperties(prototype));
+  // Combine ancestor attributes with prototype's attributes so that
+  // ancestors attributes also become initializeable.
+  var attributes = mix(descriptor.extends.constructor.attributes || {},
+                       getDataProperties(prototype));
 
-    constructor.attributes = attributes;
-    Object.defineProperty(constructor, 'prototype', {
-      configurable: false,
-      writable: false,
-      value: prototype
-    });
-    return constructor;
-  };
+  constructor.attributes = attributes;
+  Object.defineProperty(constructor, 'prototype', {
+    configurable: false,
+    writable: false,
+    value: prototype
+  });
+  return constructor;
 }
-Class.prototype = extend(null, obscure({
+Class.prototype = obscure({
   constructor: function constructor() {
     this.initialize.apply(this, arguments);
     return this;
   },
   initialize: function initialize() {
     // Do your initialization logic here
   },
   // Copy useful properties from `Object.prototype`.
   toString: Object.prototype.toString,
   toLocaleString: Object.prototype.toLocaleString,
   toSource: Object.prototype.toSource,
   valueOf: Object.prototype.valueOf,
   isPrototypeOf: Object.prototype.isPrototypeOf
-}));
+}, null);
 exports.Class = freeze(Class);