toolkit/components/osfile/modules/osfile_shared_allthreads.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 "use strict";
     7 /**
     8  * OS.File utilities used by all threads.
     9  *
    10  * This module defines:
    11  * - logging;
    12  * - the base constants;
    13  * - base types and primitives for declaring new types;
    14  * - primitives for importing C functions;
    15  * - primitives for dealing with integers, pointers, typed arrays;
    16  * - the base class OSError;
    17  * - a few additional utilities.
    18  */
    20 // Boilerplate used to be able to import this module both from the main
    21 // thread and from worker threads.
    22 if (typeof Components != "undefined") {
    23   // Global definition of |exports|, to keep everybody happy.
    24   // In non-main thread, |exports| is provided by the module
    25   // loader.
    26   this.exports = {};
    28   const Cu = Components.utils;
    29   const Ci = Components.interfaces;
    30   const Cc = Components.classes;
    32   Cu.import("resource://gre/modules/Services.jsm", this);
    33 }
    35 let EXPORTED_SYMBOLS = [
    36   "LOG",
    37   "clone",
    38   "Config",
    39   "Constants",
    40   "Type",
    41   "HollowStructure",
    42   "OSError",
    43   "Library",
    44   "declareFFI",
    45   "declareLazy",
    46   "declareLazyFFI",
    47   "normalizeToPointer",
    48   "projectValue",
    49   "isTypedArray",
    50   "defineLazyGetter",
    51   "offsetBy",
    52   "OS" // Warning: this exported symbol will disappear
    53 ];
    55 ////////////////////// Configuration of OS.File
    57 let Config = {
    58   /**
    59    * If |true|, calls to |LOG| are shown. Otherwise, they are hidden.
    60    *
    61    * This configuration option is controlled by preference "toolkit.osfile.log".
    62    */
    63   DEBUG: false,
    65   /**
    66    * TEST
    67    */
    68   TEST: false
    69 };
    70 exports.Config = Config;
    72 ////////////////////// OS Constants
    74 if (typeof Components != "undefined") {
    75   // On the main thread, OS.Constants is defined by a xpcom
    76   // component. On other threads, it is available automatically
    77   Cu.import("resource://gre/modules/ctypes.jsm");
    78   Cc["@mozilla.org/net/osfileconstantsservice;1"].
    79     getService(Ci.nsIOSFileConstantsService).init();
    80 }
    82 exports.Constants = OS.Constants;
    84 ///////////////////// Utilities
    86 // Define a lazy getter for a property
    87 let defineLazyGetter = function defineLazyGetter(object, name, getter) {
    88   Object.defineProperty(object, name, {
    89     configurable: true,
    90     get: function lazy() {
    91       delete this[name];
    92       let value = getter.call(this);
    93       Object.defineProperty(object, name, {
    94         value: value
    95       });
    96       return value;
    97     }
    98   });
    99 };
   100 exports.defineLazyGetter = defineLazyGetter;
   103 ///////////////////// Logging
   105 /**
   106  * The default implementation of the logger.
   107  *
   108  * The choice of logger can be overridden with Config.TEST.
   109  */
   110 let gLogger;
   111 if (typeof window != "undefined" && window.console && console.log) {
   112   gLogger = console.log.bind(console, "OS");
   113 } else {
   114   gLogger = function(...args) {
   115     dump("OS " + args.join(" ") + "\n");
   116   };
   117 }
   119 /**
   120  * Attempt to stringify an argument into something useful for
   121  * debugging purposes, by using |.toString()| or |JSON.stringify|
   122  * if available.
   123  *
   124  * @param {*} arg An argument to be stringified if possible.
   125  * @return {string} A stringified version of |arg|.
   126  */
   127 let stringifyArg = function stringifyArg(arg) {
   128   if (typeof arg === "string") {
   129     return arg;
   130   }
   131   if (arg && typeof arg === "object") {
   132     let argToString = "" + arg;
   134     /**
   135      * The only way to detect whether this object has a non-default
   136      * implementation of |toString| is to check whether it returns
   137      * '[object Object]'. Unfortunately, we cannot simply compare |arg.toString|
   138      * and |Object.prototype.toString| as |arg| typically comes from another
   139      * compartment.
   140      */
   141     if (argToString === "[object Object]") {
   142       return JSON.stringify(arg);
   143     } else {
   144       return argToString;
   145     }
   146   }
   147   return arg;
   148 };
   150 let LOG = function (...args) {
   151   if (!Config.DEBUG) {
   152     // If logging is deactivated, don't log
   153     return;
   154   }
   156   let logFunc = gLogger;
   157   if (Config.TEST && typeof Components != "undefined") {
   158     // If _TESTING_LOGGING is set, and if we are on the main thread,
   159     // redirect logs to Services.console, for testing purposes
   160     logFunc = function logFunc(...args) {
   161       let message = ["TEST", "OS"].concat(args).join(" ");
   162       Services.console.logStringMessage(message + "\n");
   163     };
   164   }
   165   logFunc.apply(null, [stringifyArg(arg) for (arg of args)]);
   166 };
   168 exports.LOG = LOG;
   170 /**
   171  * Return a shallow clone of the enumerable properties of an object.
   172  *
   173  * Utility used whenever normalizing options requires making (shallow)
   174  * changes to an option object. The copy ensures that we do not modify
   175  * a client-provided object by accident.
   176  *
   177  * Note: to reference and not copy specific fields, provide an optional
   178  * |refs| argument containing their names.
   179  *
   180  * @param {JSON} object Options to be cloned.
   181  * @param {Array} refs An optional array of field names to be passed by
   182  * reference instead of copying.
   183  */
   184 let clone = function (object, refs = []) {
   185   let result = {};
   186   // Make a reference between result[key] and object[key].
   187   let refer = function refer(result, key, object) {
   188     Object.defineProperty(result, key, {
   189       enumerable: true,
   190       get: function() {
   191         return object[key];
   192       },
   193       set: function(value) {
   194         object[key] = value;
   195       }
   196     });
   197   };
   198   for (let k in object) {
   199     if (refs.indexOf(k) < 0) {
   200       result[k] = object[k];
   201     } else {
   202       refer(result, k, object);
   203     }
   204   }
   205   return result;
   206 };
   208 exports.clone = clone;
   210 ///////////////////// Abstractions above js-ctypes
   212 /**
   213  * Abstraction above js-ctypes types.
   214  *
   215  * Use values of this type to register FFI functions. In addition to the
   216  * usual features of js-ctypes, values of this type perform the necessary
   217  * transformations to ensure that C errors are handled nicely, to connect
   218  * resources with their finalizer, etc.
   219  *
   220  * @param {string} name The name of the type. Must be unique.
   221  * @param {CType} implementation The js-ctypes implementation of the type.
   222  *
   223  * @constructor
   224  */
   225 function Type(name, implementation) {
   226   if (!(typeof name == "string")) {
   227     throw new TypeError("Type expects as first argument a name, got: "
   228                         + name);
   229   }
   230   if (!(implementation instanceof ctypes.CType)) {
   231     throw new TypeError("Type expects as second argument a ctypes.CType"+
   232                         ", got: " + implementation);
   233   }
   234   Object.defineProperty(this, "name", { value: name });
   235   Object.defineProperty(this, "implementation", { value: implementation });
   236 }
   237 Type.prototype = {
   238   /**
   239    * Serialize a value of |this| |Type| into a format that can
   240    * be transmitted as a message (not necessarily a string).
   241    *
   242    * In the default implementation, the method returns the
   243    * value unchanged.
   244    */
   245   toMsg: function default_toMsg(value) {
   246     return value;
   247   },
   248   /**
   249    * Deserialize a message to a value of |this| |Type|.
   250    *
   251    * In the default implementation, the method returns the
   252    * message unchanged.
   253    */
   254   fromMsg: function default_fromMsg(msg) {
   255     return msg;
   256   },
   257   /**
   258    * Import a value from C.
   259    *
   260    * In this default implementation, return the value
   261    * unchanged.
   262    */
   263   importFromC: function default_importFromC(value) {
   264     return value;
   265   },
   267   /**
   268    * A pointer/array used to pass data to the foreign function.
   269    */
   270   get in_ptr() {
   271     delete this.in_ptr;
   272     let ptr_t = new PtrType(
   273       "[in] " + this.name + "*",
   274       this.implementation.ptr,
   275       this);
   276     Object.defineProperty(this, "in_ptr",
   277     {
   278       get: function() {
   279         return ptr_t;
   280       }
   281     });
   282     return ptr_t;
   283   },
   285   /**
   286    * A pointer/array used to receive data from the foreign function.
   287    */
   288   get out_ptr() {
   289     delete this.out_ptr;
   290     let ptr_t = new PtrType(
   291       "[out] " + this.name + "*",
   292       this.implementation.ptr,
   293       this);
   294     Object.defineProperty(this, "out_ptr",
   295     {
   296       get: function() {
   297         return ptr_t;
   298       }
   299     });
   300     return ptr_t;
   301   },
   303   /**
   304    * A pointer/array used to both pass data to the foreign function
   305    * and receive data from the foreign function.
   306    *
   307    * Whenever possible, prefer using |in_ptr| or |out_ptr|, which
   308    * are generally faster.
   309    */
   310   get inout_ptr() {
   311     delete this.inout_ptr;
   312     let ptr_t = new PtrType(
   313       "[inout] " + this.name + "*",
   314       this.implementation.ptr,
   315       this);
   316     Object.defineProperty(this, "inout_ptr",
   317     {
   318       get: function() {
   319         return ptr_t;
   320       }
   321     });
   322     return ptr_t;
   323   },
   325   /**
   326    * Attach a finalizer to a type.
   327    */
   328   releaseWith: function releaseWith(finalizer) {
   329     let parent = this;
   330     let type = this.withName("[auto " + this.name + ", " + finalizer + "] ");
   331     type.importFromC = function importFromC(value, operation) {
   332       return ctypes.CDataFinalizer(
   333         parent.importFromC(value, operation),
   334         finalizer);
   335     };
   336     return type;
   337   },
   339   /**
   340    * Lazy variant of releaseWith.
   341    * Attach a finalizer lazily to a type.
   342    *
   343    * @param {function} getFinalizer The function that
   344    * returns finalizer lazily.
   345    */
   346   releaseWithLazy: function releaseWithLazy(getFinalizer) {
   347     let parent = this;
   348     let type = this.withName("[auto " + this.name + ", (lazy)] ");
   349     type.importFromC = function importFromC(value, operation) {
   350       return ctypes.CDataFinalizer(
   351         parent.importFromC(value, operation),
   352         getFinalizer());
   353     };
   354     return type;
   355   },
   357   /**
   358    * Return an alias to a type with a different name.
   359    */
   360   withName: function withName(name) {
   361     return Object.create(this, {name: {value: name}});
   362   },
   364   /**
   365    * Cast a C value to |this| type.
   366    *
   367    * Throw an error if the value cannot be casted.
   368    */
   369   cast: function cast(value) {
   370     return ctypes.cast(value, this.implementation);
   371   },
   373   /**
   374    * Return the number of bytes in a value of |this| type.
   375    *
   376    * This may not be defined, e.g. for |void_t|, array types
   377    * without length, etc.
   378    */
   379   get size() {
   380     return this.implementation.size;
   381   }
   382 };
   384 /**
   385  * Utility function used to determine whether an object is a typed array
   386  */
   387 let isTypedArray = function isTypedArray(obj) {
   388   return typeof obj == "object"
   389     && "byteOffset" in obj;
   390 };
   391 exports.isTypedArray = isTypedArray;
   393 /**
   394  * A |Type| of pointers.
   395  *
   396  * @param {string} name The name of this type.
   397  * @param {CType} implementation The type of this pointer.
   398  * @param {Type} targetType The target type.
   399  */
   400 function PtrType(name, implementation, targetType) {
   401   Type.call(this, name, implementation);
   402   if (targetType == null || !targetType instanceof Type) {
   403     throw new TypeError("targetType must be an instance of Type");
   404   }
   405   /**
   406    * The type of values targeted by this pointer type.
   407    */
   408   Object.defineProperty(this, "targetType", {
   409     value: targetType
   410   });
   411 }
   412 PtrType.prototype = Object.create(Type.prototype);
   414 /**
   415  * Convert a value to a pointer.
   416  *
   417  * Protocol:
   418  * - |null| returns |null|
   419  * - a string returns |{string: value}|
   420  * - a typed array returns |{ptr: address_of_buffer}|
   421  * - a C array returns |{ptr: address_of_buffer}|
   422  * everything else raises an error
   423  */
   424 PtrType.prototype.toMsg = function ptr_toMsg(value) {
   425   if (value == null) {
   426     return null;
   427   }
   428   if (typeof value == "string") {
   429     return { string: value };
   430   }
   431   let normalized;
   432   if (isTypedArray(value)) { // Typed array
   433     normalized = Type.uint8_t.in_ptr.implementation(value.buffer);
   434     if (value.byteOffset != 0) {
   435       normalized = offsetBy(normalized, value.byteOffset);
   436     }
   437   } else if ("addressOfElement" in value) { // C array
   438     normalized = value.addressOfElement(0);
   439   } else if ("isNull" in value) { // C pointer
   440     normalized = value;
   441   } else {
   442     throw new TypeError("Value " + value +
   443       " cannot be converted to a pointer");
   444   }
   445   let cast = Type.uintptr_t.cast(normalized);
   446   return {ptr: cast.value.toString()};
   447 };
   449 /**
   450  * Convert a message back to a pointer.
   451  */
   452 PtrType.prototype.fromMsg = function ptr_fromMsg(msg) {
   453   if (msg == null) {
   454     return null;
   455   }
   456   if ("string" in msg) {
   457     return msg.string;
   458   }
   459   if ("ptr" in msg) {
   460     let address = ctypes.uintptr_t(msg.ptr);
   461     return this.cast(address);
   462   }
   463   throw new TypeError("Message " + msg.toSource() +
   464     " does not represent a pointer");
   465 };
   467 exports.Type = Type;
   470 /*
   471  * Some values are large integers on 64 bit platforms. Unfortunately,
   472  * in practice, 64 bit integers cannot be manipulated in JS. We
   473  * therefore project them to regular numbers whenever possible.
   474  */
   476 let projectLargeInt = function projectLargeInt(x) {
   477   let str = x.toString();
   478   let rv = parseInt(str, 10);
   479   if (rv.toString() !== str) {
   480     throw new TypeError("Number " + str + " cannot be projected to a double");
   481   }
   482   return rv;
   483 };
   484 let projectLargeUInt = function projectLargeUInt(x) {
   485   return projectLargeInt(x);
   486 };
   487 let projectValue = function projectValue(x) {
   488   if (!(x instanceof ctypes.CData)) {
   489     return x;
   490   }
   491   if (!("value" in x)) { // Sanity check
   492     throw new TypeError("Number " + x.toSource() + " has no field |value|");
   493   }
   494   return x.value;
   495 };
   497 function projector(type, signed) {
   498   LOG("Determining best projection for", type,
   499     "(size: ", type.size, ")", signed?"signed":"unsigned");
   500   if (type instanceof Type) {
   501     type = type.implementation;
   502   }
   503   if (!type.size) {
   504     throw new TypeError("Argument is not a proper C type");
   505   }
   506   // Determine if type is projected to Int64/Uint64
   507   if (type.size == 8           // Usual case
   508       // The following cases have special treatment in js-ctypes
   509       // Regardless of their size, the value getter returns
   510       // a Int64/Uint64
   511       || type == ctypes.size_t // Special cases
   512       || type == ctypes.ssize_t
   513       || type == ctypes.intptr_t
   514       || type == ctypes.uintptr_t
   515       || type == ctypes.off_t) {
   516     if (signed) {
   517       LOG("Projected as a large signed integer");
   518       return projectLargeInt;
   519     } else {
   520       LOG("Projected as a large unsigned integer");
   521       return projectLargeUInt;
   522     }
   523   }
   524   LOG("Projected as a regular number");
   525   return projectValue;
   526 };
   527 exports.projectValue = projectValue;
   529 /**
   530  * Get the appropriate type for an unsigned int of the given size.
   531  *
   532  * This function is useful to define types such as |mode_t| whose
   533  * actual width depends on the OS/platform.
   534  *
   535  * @param {number} size The number of bytes requested.
   536  */
   537 Type.uintn_t = function uintn_t(size) {
   538   switch (size) {
   539   case 1: return Type.uint8_t;
   540   case 2: return Type.uint16_t;
   541   case 4: return Type.uint32_t;
   542   case 8: return Type.uint64_t;
   543   default:
   544     throw new Error("Cannot represent unsigned integers of " + size + " bytes");
   545   }
   546 };
   548 /**
   549  * Get the appropriate type for an signed int of the given size.
   550  *
   551  * This function is useful to define types such as |mode_t| whose
   552  * actual width depends on the OS/platform.
   553  *
   554  * @param {number} size The number of bytes requested.
   555  */
   556 Type.intn_t = function intn_t(size) {
   557   switch (size) {
   558   case 1: return Type.int8_t;
   559   case 2: return Type.int16_t;
   560   case 4: return Type.int32_t;
   561   case 8: return Type.int64_t;
   562   default:
   563     throw new Error("Cannot represent integers of " + size + " bytes");
   564   }
   565 };
   567 /**
   568  * Actual implementation of common C types.
   569  */
   571 /**
   572  * The void value.
   573  */
   574 Type.void_t =
   575   new Type("void",
   576            ctypes.void_t);
   578 /**
   579  * Shortcut for |void*|.
   580  */
   581 Type.voidptr_t =
   582   new PtrType("void*",
   583               ctypes.voidptr_t,
   584               Type.void_t);
   586 // void* is a special case as we can cast any pointer to/from it
   587 // so we have to shortcut |in_ptr|/|out_ptr|/|inout_ptr| and
   588 // ensure that js-ctypes' casting mechanism is invoked directly
   589 ["in_ptr", "out_ptr", "inout_ptr"].forEach(function(key) {
   590   Object.defineProperty(Type.void_t, key,
   591   {
   592     value: Type.voidptr_t
   593   });
   594 });
   596 /**
   597  * A Type of integers.
   598  *
   599  * @param {string} name The name of this type.
   600  * @param {CType} implementation The underlying js-ctypes implementation.
   601  * @param {bool} signed |true| if this is a type of signed integers,
   602  * |false| otherwise.
   603  *
   604  * @constructor
   605  */
   606 function IntType(name, implementation, signed) {
   607   Type.call(this, name, implementation);
   608   this.importFromC = projector(implementation, signed);
   609   this.project = this.importFromC;
   610 };
   611 IntType.prototype = Object.create(Type.prototype);
   612 IntType.prototype.toMsg = function toMsg(value) {
   613   if (typeof value == "number") {
   614     return value;
   615   }
   616   return this.project(value);
   617 };
   619 /**
   620  * A C char (one byte)
   621  */
   622 Type.char =
   623   new Type("char",
   624            ctypes.char);
   626 /**
   627  * A C wide char (two bytes)
   628  */
   629 Type.jschar =
   630   new Type("jschar",
   631            ctypes.jschar);
   633  /**
   634   * Base string types.
   635   */
   636 Type.cstring = Type.char.in_ptr.withName("[in] C string");
   637 Type.wstring = Type.jschar.in_ptr.withName("[in] wide string");
   638 Type.out_cstring = Type.char.out_ptr.withName("[out] C string");
   639 Type.out_wstring = Type.jschar.out_ptr.withName("[out] wide string");
   641 /**
   642  * A C integer (8-bits).
   643  */
   644 Type.int8_t =
   645   new IntType("int8_t", ctypes.int8_t, true);
   647 Type.uint8_t =
   648   new IntType("uint8_t", ctypes.uint8_t, false);
   650 /**
   651  * A C integer (16-bits).
   652  *
   653  * Also known as WORD under Windows.
   654  */
   655 Type.int16_t =
   656   new IntType("int16_t", ctypes.int16_t, true);
   658 Type.uint16_t =
   659   new IntType("uint16_t", ctypes.uint16_t, false);
   661 /**
   662  * A C integer (32-bits).
   663  *
   664  * Also known as DWORD under Windows.
   665  */
   666 Type.int32_t =
   667   new IntType("int32_t", ctypes.int32_t, true);
   669 Type.uint32_t =
   670   new IntType("uint32_t", ctypes.uint32_t, false);
   672 /**
   673  * A C integer (64-bits).
   674  */
   675 Type.int64_t =
   676   new IntType("int64_t", ctypes.int64_t, true);
   678 Type.uint64_t =
   679   new IntType("uint64_t", ctypes.uint64_t, false);
   681  /**
   682  * A C integer
   683  *
   684  * Size depends on the platform.
   685  */
   686 Type.int = Type.intn_t(ctypes.int.size).
   687   withName("int");
   689 Type.unsigned_int = Type.intn_t(ctypes.unsigned_int.size).
   690   withName("unsigned int");
   692 /**
   693  * A C long integer.
   694  *
   695  * Size depends on the platform.
   696  */
   697 Type.long =
   698   Type.intn_t(ctypes.long.size).withName("long");
   700 Type.unsigned_long =
   701   Type.intn_t(ctypes.unsigned_long.size).withName("unsigned long");
   703 /**
   704  * An unsigned integer with the same size as a pointer.
   705  *
   706  * Used to cast a pointer to an integer, whenever necessary.
   707  */
   708 Type.uintptr_t =
   709   Type.uintn_t(ctypes.uintptr_t.size).withName("uintptr_t");
   711 /**
   712  * A boolean.
   713  * Implemented as a C integer.
   714  */
   715 Type.bool = Type.int.withName("bool");
   716 Type.bool.importFromC = function projectBool(x) {
   717   return !!(x.value);
   718 };
   720 /**
   721  * A user identifier.
   722  *
   723  * Implemented as a C integer.
   724  */
   725 Type.uid_t =
   726   Type.int.withName("uid_t");
   728 /**
   729  * A group identifier.
   730  *
   731  * Implemented as a C integer.
   732  */
   733 Type.gid_t =
   734   Type.int.withName("gid_t");
   736 /**
   737  * An offset (positive or negative).
   738  *
   739  * Implemented as a C integer.
   740  */
   741 Type.off_t =
   742   new IntType("off_t", ctypes.off_t, true);
   744 /**
   745  * A size (positive).
   746  *
   747  * Implemented as a C size_t.
   748  */
   749 Type.size_t =
   750   new IntType("size_t", ctypes.size_t, false);
   752 /**
   753  * An offset (positive or negative).
   754  * Implemented as a C integer.
   755  */
   756 Type.ssize_t =
   757   new IntType("ssize_t", ctypes.ssize_t, true);
   759 /**
   760  * Encoding/decoding strings
   761  */
   762 Type.uencoder =
   763   new Type("uencoder", ctypes.StructType("uencoder"));
   764 Type.udecoder =
   765   new Type("udecoder", ctypes.StructType("udecoder"));
   767 /**
   768  * Utility class, used to build a |struct| type
   769  * from a set of field names, types and offsets.
   770  *
   771  * @param {string} name The name of the |struct| type.
   772  * @param {number} size The total size of the |struct| type in bytes.
   773  */
   774 function HollowStructure(name, size) {
   775   if (!name) {
   776     throw new TypeError("HollowStructure expects a name");
   777   }
   778   if (!size || size < 0) {
   779     throw new TypeError("HollowStructure expects a (positive) size");
   780   }
   782   // A mapping from offsets in the struct to name/type pairs
   783   // (or nothing if no field starts at that offset).
   784   this.offset_to_field_info = [];
   786   // The name of the struct
   787   this.name = name;
   789   // The size of the struct, in bytes
   790   this.size = size;
   792   // The number of paddings inserted so far.
   793   // Used to give distinct names to padding fields.
   794   this._paddings = 0;
   795 }
   796 HollowStructure.prototype = {
   797   /**
   798    * Add a field at a given offset.
   799    *
   800    * @param {number} offset The offset at which to insert the field.
   801    * @param {string} name The name of the field.
   802    * @param {CType|Type} type The type of the field.
   803    */
   804   add_field_at: function add_field_at(offset, name, type) {
   805     if (offset == null) {
   806       throw new TypeError("add_field_at requires a non-null offset");
   807     }
   808     if (!name) {
   809       throw new TypeError("add_field_at requires a non-null name");
   810     }
   811     if (!type) {
   812       throw new TypeError("add_field_at requires a non-null type");
   813     }
   814     if (type instanceof Type) {
   815       type = type.implementation;
   816     }
   817     if (this.offset_to_field_info[offset]) {
   818       throw new Error("HollowStructure " + this.name +
   819                       " already has a field at offset " + offset);
   820     }
   821     if (offset + type.size > this.size) {
   822       throw new Error("HollowStructure " + this.name +
   823                       " cannot place a value of type " + type +
   824                       " at offset " + offset +
   825                       " without exceeding its size of " + this.size);
   826     }
   827     let field = {name: name, type:type};
   828     this.offset_to_field_info[offset] = field;
   829   },
   831   /**
   832    * Create a pseudo-field that will only serve as padding.
   833    *
   834    * @param {number} size The number of bytes in the field.
   835    * @return {Object} An association field-name => field-type,
   836    * as expected by |ctypes.StructType|.
   837    */
   838   _makePaddingField: function makePaddingField(size) {
   839     let field = ({});
   840     field["padding_" + this._paddings] =
   841       ctypes.ArrayType(ctypes.uint8_t, size);
   842     this._paddings++;
   843     return field;
   844   },
   846   /**
   847    * Convert this |HollowStructure| into a |Type|.
   848    */
   849   getType: function getType() {
   850     // Contents of the structure, in the format expected
   851     // by ctypes.StructType.
   852     let struct = [];
   854     let i = 0;
   855     while (i < this.size) {
   856       let currentField = this.offset_to_field_info[i];
   857       if (!currentField) {
   858         // No field was specified at this offset, we need to
   859         // introduce some padding.
   861         // Firstly, determine how many bytes of padding
   862         let padding_length = 1;
   863         while (i + padding_length < this.size
   864             && !this.offset_to_field_info[i + padding_length]) {
   865           ++padding_length;
   866         }
   868         // Then add the padding
   869         struct.push(this._makePaddingField(padding_length));
   871         // And proceed
   872         i += padding_length;
   873       } else {
   874         // We have a field at this offset.
   876         // Firstly, ensure that we do not have two overlapping fields
   877         for (let j = 1; j < currentField.type.size; ++j) {
   878           let candidateField = this.offset_to_field_info[i + j];
   879           if (candidateField) {
   880             throw new Error("Fields " + currentField.name +
   881               " and " + candidateField.name +
   882               " overlap at position " + (i + j));
   883           }
   884         }
   886         // Then add the field
   887         let field = ({});
   888         field[currentField.name] = currentField.type;
   889         struct.push(field);
   891         // And proceed
   892         i += currentField.type.size;
   893       }
   894     }
   895     let result = new Type(this.name, ctypes.StructType(this.name, struct));
   896     if (result.implementation.size != this.size) {
   897       throw new Error("Wrong size for type " + this.name +
   898           ": expected " + this.size +
   899           ", found " + result.implementation.size +
   900           " (" + result.implementation.toSource() + ")");
   901     }
   902     return result;
   903   }
   904 };
   905 exports.HollowStructure = HollowStructure;
   907 /**
   908  * Representation of a native library.
   909  *
   910  * The native library is opened lazily, during the first call to its
   911  * field |library| or whenever accessing one of the methods imported
   912  * with declareLazyFFI.
   913  *
   914  * @param {string} name A human-readable name for the library. Used
   915  * for debugging and error reporting.
   916  * @param {string...} candidates A list of system libraries that may
   917  * represent this library. Used e.g. to try different library names
   918  * on distinct operating systems ("libxul", "XUL", etc.).
   919  *
   920  * @constructor
   921  */
   922 function Library(name, ...candidates) {
   923   this.name = name;
   924   this._candidates = candidates;
   925 };
   926 Library.prototype = Object.freeze({
   927   /**
   928    * The native library as a js-ctypes object.
   929    *
   930    * @throws {Error} If none of the candidate libraries could be opened.
   931    */
   932   get library() {
   933     let library;
   934     delete this.library;
   935     for (let candidate of this._candidates) {
   936       try {
   937         library = ctypes.open(candidate);
   938         break;
   939       } catch (ex) {
   940         LOG("Could not open library", candidate, ex);
   941       }
   942     }
   943     this._candidates = null;
   944     if (library) {
   945       Object.defineProperty(this, "library", {
   946         value: library
   947       });
   948       Object.freeze(this);
   949       return library;
   950     }
   951     let error = new Error("Could not open library " + this.name);
   952     Object.defineProperty(this, "library", {
   953       get: function() {
   954         throw error;
   955       }
   956     });
   957     Object.freeze(this);
   958     throw error;
   959   },
   961   /**
   962    * Declare a function, lazily.
   963    *
   964    * @param {object} The object containing the function as a field.
   965    * @param {string} The name of the field containing the function.
   966    * @param {string} symbol The name of the function, as defined in the
   967    * library.
   968    * @param {ctypes.abi} abi The abi to use, or |null| for default.
   969    * @param {Type} returnType The type of values returned by the function.
   970    * @param {...Type} argTypes The type of arguments to the function.
   971    */
   972   declareLazyFFI: function(object, field, ...args) {
   973     let lib = this;
   974     Object.defineProperty(object, field, {
   975       get: function() {
   976         delete this[field];
   977         let ffi = declareFFI(lib.library, ...args);
   978         if (ffi) {
   979           return this[field] = ffi;
   980         }
   981         return undefined;
   982       },
   983       configurable: true,
   984       enumerable: true
   985     });
   986   },
   988   /**
   989    * Define a js-ctypes function lazily using ctypes method declare.
   990    *
   991    * @param {object} The object containing the function as a field.
   992    * @param {string} The name of the field containing the function.
   993    * @param {string} symbol The name of the function, as defined in the
   994    * library.
   995    * @param {ctypes.abi} abi The abi to use, or |null| for default.
   996    * @param {ctypes.CType} returnType The type of values returned by the function.
   997    * @param {...ctypes.CType} argTypes The type of arguments to the function.
   998    */
   999   declareLazy: function(object, field, ...args) {
  1000     let lib = this;
  1001     Object.defineProperty(object, field, {
  1002       get: function() {
  1003         delete this[field];
  1004         let ffi = lib.library.declare(...args);
  1005         if (ffi) {
  1006           return this[field] = ffi;
  1008         return undefined;
  1009       },
  1010       configurable: true,
  1011       enumerable: true
  1012     });
  1013   },
  1015   toString: function() {
  1016     return "[Library " + this.name + "]";
  1018 });
  1019 exports.Library = Library;
  1021 /**
  1022  * Declare a function through js-ctypes
  1024  * @param {ctypes.library} lib The ctypes library holding the function.
  1025  * @param {string} symbol The name of the function, as defined in the
  1026  * library.
  1027  * @param {ctypes.abi} abi The abi to use, or |null| for default.
  1028  * @param {Type} returnType The type of values returned by the function.
  1029  * @param {...Type} argTypes The type of arguments to the function.
  1031  * @return null if the function could not be defined (generally because
  1032  * it does not exist), or a JavaScript wrapper performing the call to C
  1033  * and any type conversion required.
  1034  */
  1035 let declareFFI = function declareFFI(lib, symbol, abi,
  1036                                      returnType /*, argTypes ...*/) {
  1037   LOG("Attempting to declare FFI ", symbol);
  1038   // We guard agressively, to avoid any late surprise
  1039   if (typeof symbol != "string") {
  1040     throw new TypeError("declareFFI expects as first argument a string");
  1042   abi = abi || ctypes.default_abi;
  1043   if (Object.prototype.toString.call(abi) != "[object CABI]") {
  1044     // Note: This is the only known manner of checking whether an object
  1045     // is an abi.
  1046     throw new TypeError("declareFFI expects as second argument an abi or null");
  1048   if (!returnType.importFromC) {
  1049     throw new TypeError("declareFFI expects as third argument an instance of Type");
  1051   let signature = [symbol, abi];
  1052   let argtypes  = [];
  1053   for (let i = 3; i < arguments.length; ++i) {
  1054     let current = arguments[i];
  1055     if (!current) {
  1056       throw new TypeError("Missing type for argument " + ( i - 3 ) +
  1057                           " of symbol " + symbol);
  1059     if (!current.implementation) {
  1060       throw new TypeError("Missing implementation for argument " + (i - 3)
  1061                           + " of symbol " + symbol
  1062                           + " ( " + current.name + " )" );
  1064     signature.push(current.implementation);
  1066   try {
  1067     let fun = lib.declare.apply(lib, signature);
  1068     let result = function ffi(...args) {
  1069       for (let i = 0; i < args.length; i++) {
  1070         if (typeof args[i] == "undefined") {
  1071           throw new TypeError("Argument " + i + " of " + symbol + " is undefined");
  1074       let result = fun.apply(fun, args);
  1075       return returnType.importFromC(result, symbol);
  1076     };
  1077     LOG("Function", symbol, "declared");
  1078     return result;
  1079   } catch (x) {
  1080     // Note: Not being able to declare a function is normal.
  1081     // Some functions are OS (or OS version)-specific.
  1082     LOG("Could not declare function ", symbol, x);
  1083     return null;
  1085 };
  1086 exports.declareFFI = declareFFI;
  1088 /**
  1089  * Define a lazy getter to a js-ctypes function using declareFFI.
  1091  * @param {object} The object containing the function as a field.
  1092  * @param {string} The name of the field containing the function.
  1093  * @param {ctypes.library} lib The ctypes library holding the function.
  1094  * @param {string} symbol The name of the function, as defined in the
  1095  * library.
  1096  * @param {ctypes.abi} abi The abi to use, or |null| for default.
  1097  * @param {Type} returnType The type of values returned by the function.
  1098  * @param {...Type} argTypes The type of arguments to the function.
  1099  */
  1100 function declareLazyFFI(object, field, ...declareFFIArgs) {
  1101   Object.defineProperty(object, field, {
  1102     get: function() {
  1103       delete this[field];
  1104       let ffi = declareFFI(...declareFFIArgs);
  1105       if (ffi) {
  1106         return this[field] = ffi;
  1108       return undefined;
  1109     },
  1110     configurable: true,
  1111     enumerable: true
  1112   });
  1114 exports.declareLazyFFI = declareLazyFFI;
  1116 /**
  1117  * Define a lazy getter to a js-ctypes function using ctypes method declare.
  1119  * @param {object} The object containing the function as a field.
  1120  * @param {string} The name of the field containing the function.
  1121  * @param {ctypes.library} lib The ctypes library holding the function.
  1122  * @param {string} symbol The name of the function, as defined in the
  1123  * library.
  1124  * @param {ctypes.abi} abi The abi to use, or |null| for default.
  1125  * @param {ctypes.CType} returnType The type of values returned by the function.
  1126  * @param {...ctypes.CType} argTypes The type of arguments to the function.
  1127  */
  1128 function declareLazy(object, field, lib, ...declareArgs) {
  1129   Object.defineProperty(object, field, {
  1130     get: function() {
  1131       delete this[field];
  1132       try {
  1133         let ffi = lib.declare(...declareArgs);
  1134         return this[field] = ffi;
  1135       } catch (ex) {
  1136         // The symbol doesn't exist
  1137         return undefined;
  1139     },
  1140     configurable: true
  1141   });
  1143 exports.declareLazy = declareLazy;
  1145 // A bogus array type used to perform pointer arithmetics
  1146 let gOffsetByType;
  1148 /**
  1149  * Advance a pointer by a number of items.
  1151  * This method implements adding an integer to a pointer in C.
  1153  * Example:
  1154  *   // ptr is a uint16_t*,
  1155  *   offsetBy(ptr, 3)
  1156  *  // returns a uint16_t* with the address ptr + 3 * 2 bytes
  1158  * @param {C pointer} pointer The start pointer.
  1159  * @param {number} length The number of items to advance. Must not be
  1160  * negative.
  1162  * @return {C pointer} |pointer| advanced by |length| items
  1163  */
  1164 let offsetBy =
  1165   function offsetBy(pointer, length) {
  1166     if (length === undefined || length < 0) {
  1167       throw new TypeError("offsetBy expects a positive number");
  1169    if (!("isNull" in pointer)) {
  1170       throw new TypeError("offsetBy expects a pointer");
  1172     if (length == 0) {
  1173       return pointer;
  1175     let type = pointer.constructor;
  1176     let size = type.targetType.size;
  1177     if (size == 0 || size == null) {
  1178       throw new TypeError("offsetBy cannot be applied to a pointer without size");
  1180     let bytes = length * size;
  1181     if (!gOffsetByType || gOffsetByType.size <= bytes) {
  1182       gOffsetByType = ctypes.uint8_t.array(bytes * 2);
  1184     let addr = ctypes.cast(pointer, gOffsetByType.ptr).
  1185       contents.addressOfElement(bytes);
  1186     return ctypes.cast(addr, type);
  1187 };
  1188 exports.offsetBy = offsetBy;
  1190 /**
  1191  * Utility function used to normalize a Typed Array or C
  1192  * pointer into a uint8_t C pointer.
  1194  * Future versions might extend this to other data structures.
  1196  * @param {Typed array | C pointer} candidate The buffer. If
  1197  * a C pointer, it must be non-null.
  1198  * @param {number} bytes The number of bytes that |candidate| should contain.
  1199  * Used for sanity checking if the size of |candidate| can be determined.
  1201  * @return {ptr:{C pointer}, bytes:number} A C pointer of type uint8_t,
  1202  * corresponding to the start of |candidate|.
  1203  */
  1204 function normalizeToPointer(candidate, bytes) {
  1205   if (!candidate) {
  1206     throw new TypeError("Expecting  a Typed Array or a C pointer");
  1208   let ptr;
  1209   if ("isNull" in candidate) {
  1210     if (candidate.isNull()) {
  1211       throw new TypeError("Expecting a non-null pointer");
  1213     ptr = Type.uint8_t.out_ptr.cast(candidate);
  1214     if (bytes == null) {
  1215       throw new TypeError("C pointer missing bytes indication.");
  1217   } else if (isTypedArray(candidate)) {
  1218     // Typed Array
  1219     ptr = Type.uint8_t.out_ptr.implementation(candidate.buffer);
  1220     if (bytes == null) {
  1221       bytes = candidate.byteLength;
  1222     } else if (candidate.byteLength < bytes) {
  1223       throw new TypeError("Buffer is too short. I need at least " +
  1224                          bytes +
  1225                          " bytes but I have only " +
  1226                          candidate.byteLength +
  1227                           "bytes");
  1229   } else {
  1230     throw new TypeError("Expecting  a Typed Array or a C pointer");
  1232   return {ptr: ptr, bytes: bytes};
  1233 };
  1234 exports.normalizeToPointer = normalizeToPointer;
  1236 ///////////////////// OS interactions
  1238 /**
  1239  * An OS error.
  1241  * This class is provided mostly for type-matching. If you need more
  1242  * details about an error, you should use the platform-specific error
  1243  * codes provided by subclasses of |OS.Shared.Error|.
  1245  * @param {string} operation The operation that failed.
  1246  * @param {string=} path The path of the file on which the operation failed,
  1247  * or nothing if there was no file involved in the failure.
  1249  * @constructor
  1250  */
  1251 function OSError(operation, path = "") {
  1252   Error.call(this);
  1253   this.operation = operation;
  1254   this.path = path;
  1256 exports.OSError = OSError;
  1259 ///////////////////// Temporary boilerplate
  1260 // Boilerplate, to simplify the transition to require()
  1261 // Do not rely upon this symbol, it will disappear with
  1262 // bug 883050.
  1263 exports.OS = {
  1264   Constants: exports.Constants,
  1265   Shared: {
  1266     LOG: LOG,
  1267     clone: clone,
  1268     Type: Type,
  1269     HollowStructure: HollowStructure,
  1270     Error: OSError,
  1271     declareFFI: declareFFI,
  1272     projectValue: projectValue,
  1273     isTypedArray: isTypedArray,
  1274     defineLazyGetter: defineLazyGetter,
  1275     offsetBy: offsetBy
  1277 };
  1279 Object.defineProperty(exports.OS.Shared, "DEBUG", {
  1280   get: function() {
  1281     return Config.DEBUG;
  1282   },
  1283   set: function(x) {
  1284     return Config.DEBUG = x;
  1286 });
  1287 Object.defineProperty(exports.OS.Shared, "TEST", {
  1288   get: function() {
  1289     return Config.TEST;
  1290   },
  1291   set: function(x) {
  1292     return Config.TEST = x;
  1294 });
  1297 ///////////////////// Permanent boilerplate
  1298 if (typeof Components != "undefined") {
  1299   this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS;
  1300   for (let symbol of EXPORTED_SYMBOLS) {
  1301     this[symbol] = exports[symbol];

mercurial