Bug 1391405: Part 2 - Speed up base type normalization. r?zombie draft
authorKris Maglione <maglione.k@gmail.com>
Thu, 17 Aug 2017 14:23:15 -0700
changeset 649173 c7f00fb8c35965476e7c7b888b6af36714c1323f
parent 649172 4fe73f4b9bde052a0eadf7d5634f792e16ca1c94
child 649174 5894bc4b9e8d64ac9505f27240ea4fabfcb5f02f
push id74977
push usermaglione.k@gmail.com
push dateFri, 18 Aug 2017 18:50:35 +0000
reviewerszombie
bugs1391405
milestone57.0a1
Bug 1391405: Part 2 - Speed up base type normalization. r?zombie The Array and ArrayBuffer type checks we do in getBaseType add up to a significant amount of overhead given the number of times we call them, especially when X-ray overhead comes into play. These changes allow us to avoid X-ray overhead altogether. MozReview-Commit-ID: KlRuxeElIfp
toolkit/components/extensions/Schemas.jsm
--- a/toolkit/components/extensions/Schemas.jsm
+++ b/toolkit/components/extensions/Schemas.jsm
@@ -123,17 +123,17 @@ async function readJSONAndBlobbify(url) 
  *        The object on which to define the getter.
  * @param {string|Symbol} prop
  *        The property name for which to define the getter.
  * @param {function} getter
  *        The function to call in order to generate the final property
  *        value.
  */
 function exportLazyGetter(object, prop, getter) {
-  object = Cu.waiveXrays(object);
+  object = ChromeUtils.waiveXrays(object);
 
   let redefine = value => {
     if (value === undefined) {
       delete object[prop];
     } else {
       Object.defineProperty(object, prop, {
         enumerable: true,
         configurable: true,
@@ -177,17 +177,17 @@ function exportLazyGetter(object, prop, 
  * @param {function} getter
  *        The function to call in order to generate the final property
  *        descriptor object. This will be called, and the property
  *        descriptor installed on the object, the first time the
  *        property is written or read. The function may return
  *        undefined, which will cause the property to be deleted.
  */
 function exportLazyProperty(object, prop, getter) {
-  object = Cu.waiveXrays(object);
+  object = ChromeUtils.waiveXrays(object);
 
   let redefine = obj => {
     let desc = getter.call(obj);
     getter = null;
 
     delete object[prop];
     if (desc) {
       let defaults = {
@@ -239,31 +239,36 @@ function parsePattern(pattern) {
   let match = /^\(\?([im]*)\)(.*)/.exec(pattern);
   if (match) {
     [, flags, pattern] = match;
   }
   return new RegExp(pattern, flags);
 }
 
 function getValueBaseType(value) {
-  let t = typeof(value);
-  if (t == "object") {
-    if (value === null) {
-      return "null";
-    } else if (Array.isArray(value)) {
-      return "array";
-    } else if (Object.prototype.toString.call(value) == "[object ArrayBuffer]") {
-      return "binary";
-    }
-  } else if (t == "number") {
-    if (value % 1 == 0) {
-      return "integer";
-    }
+  let type = typeof value;
+  switch (type) {
+    case "object":
+      if (value === null) {
+        return "null";
+      }
+      switch (ChromeUtils.getClassName(value, true)) {
+        case "Array":
+          return "array";
+        case "ArrayBuffer":
+          return "binary";
+      }
+      break;
+
+    case "number":
+      if (value % 1 === 0) {
+        return "integer";
+      }
   }
-  return t;
+  return type;
 }
 
 // Methods of Context that are used by Schemas.normalize. These methods can be
 // overridden at the construction of Context.
 const CONTEXT_FOR_VALIDATION = [
   "checkLoadURL",
   "hasPermission",
   "logError",
@@ -598,17 +603,17 @@ class InjectionEntry {
     if (this.lazyInjected) {
       this.lazyInjected = false;
     } else if (this.injected) {
       if (this.injected.revoke) {
         this.injected.revoke();
       }
 
       try {
-        let unwrapped = Cu.waiveXrays(this.parentObj);
+        let unwrapped = ChromeUtils.waiveXrays(this.parentObj);
         delete unwrapped[this.name];
       } catch (e) {
         Cu.reportError(e);
       }
 
       let {value} = this.injected.descriptor;
       if (value) {
         this.context.revokeChildren(value);
@@ -1523,34 +1528,34 @@ class ObjectType extends Type {
     // |value| should be a JS Xray wrapping an object in the
     // extension compartment. This works well except when we need to
     // access callable properties on |value| since JS Xrays don't
     // support those. To work around the problem, we verify that
     // |value| is a plain JS object (i.e., not anything scary like a
     // Proxy). Then we copy the properties out of it into a normal
     // object using a waiver wrapper.
 
-    let klass = Cu.getClassName(value, true);
+    let klass = ChromeUtils.getClassName(value, true);
     if (klass != "Object") {
       throw context.error(`Expected a plain JavaScript object, got a ${klass}`,
                           `be a plain JavaScript object`);
     }
 
     let properties = Object.create(null);
 
-    let waived = Cu.waiveXrays(value);
+    let waived = ChromeUtils.waiveXrays(value);
     for (let prop of Object.getOwnPropertyNames(waived)) {
       let desc = Object.getOwnPropertyDescriptor(waived, prop);
       if (desc.get || desc.set) {
         throw context.error("Objects cannot have getters or setters on properties",
                             "contain no getter or setter properties");
       }
       // Chrome ignores non-enumerable properties.
       if (desc.enumerable) {
-        properties[prop] = Cu.unwaiveXrays(desc.value);
+        properties[prop] = ChromeUtils.unwaiveXrays(desc.value);
       }
     }
 
     return properties;
   }
 
   checkProperty(context, prop, propType, result, properties, remainingProps) {
     let {type, optional, unsupported, onError} = propType;
@@ -2031,17 +2036,17 @@ class SubModuleProperty extends Entry {
       context.injectInto(event, obj, event.name, subpath, ns);
     }
 
     // TODO: Inject this.properties.
 
     return {
       descriptor: {value: obj},
       revoke() {
-        let unwrapped = Cu.waiveXrays(obj);
+        let unwrapped = ChromeUtils.waiveXrays(obj);
         for (let fun of functions) {
           try {
             delete unwrapped[fun.name];
           } catch (e) {
             Cu.reportError(e);
           }
         }
       },
@@ -2323,17 +2328,17 @@ Event = class Event extends CallEntry { 
     Cu.exportFunction(hasStub, obj, {defineAs: "hasListener"});
 
     return {
       descriptor: {value: obj},
       revoke() {
         apiImpl.revoke();
         apiImpl = null;
 
-        let unwrapped = Cu.waiveXrays(obj);
+        let unwrapped = ChromeUtils.waiveXrays(obj);
         delete unwrapped.addListener;
         delete unwrapped.removeListener;
         delete unwrapped.hasListener;
       },
     };
   }
 };