1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1303 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +/** 1.11 + * OS.File utilities used by all threads. 1.12 + * 1.13 + * This module defines: 1.14 + * - logging; 1.15 + * - the base constants; 1.16 + * - base types and primitives for declaring new types; 1.17 + * - primitives for importing C functions; 1.18 + * - primitives for dealing with integers, pointers, typed arrays; 1.19 + * - the base class OSError; 1.20 + * - a few additional utilities. 1.21 + */ 1.22 + 1.23 +// Boilerplate used to be able to import this module both from the main 1.24 +// thread and from worker threads. 1.25 +if (typeof Components != "undefined") { 1.26 + // Global definition of |exports|, to keep everybody happy. 1.27 + // In non-main thread, |exports| is provided by the module 1.28 + // loader. 1.29 + this.exports = {}; 1.30 + 1.31 + const Cu = Components.utils; 1.32 + const Ci = Components.interfaces; 1.33 + const Cc = Components.classes; 1.34 + 1.35 + Cu.import("resource://gre/modules/Services.jsm", this); 1.36 +} 1.37 + 1.38 +let EXPORTED_SYMBOLS = [ 1.39 + "LOG", 1.40 + "clone", 1.41 + "Config", 1.42 + "Constants", 1.43 + "Type", 1.44 + "HollowStructure", 1.45 + "OSError", 1.46 + "Library", 1.47 + "declareFFI", 1.48 + "declareLazy", 1.49 + "declareLazyFFI", 1.50 + "normalizeToPointer", 1.51 + "projectValue", 1.52 + "isTypedArray", 1.53 + "defineLazyGetter", 1.54 + "offsetBy", 1.55 + "OS" // Warning: this exported symbol will disappear 1.56 +]; 1.57 + 1.58 +////////////////////// Configuration of OS.File 1.59 + 1.60 +let Config = { 1.61 + /** 1.62 + * If |true|, calls to |LOG| are shown. Otherwise, they are hidden. 1.63 + * 1.64 + * This configuration option is controlled by preference "toolkit.osfile.log". 1.65 + */ 1.66 + DEBUG: false, 1.67 + 1.68 + /** 1.69 + * TEST 1.70 + */ 1.71 + TEST: false 1.72 +}; 1.73 +exports.Config = Config; 1.74 + 1.75 +////////////////////// OS Constants 1.76 + 1.77 +if (typeof Components != "undefined") { 1.78 + // On the main thread, OS.Constants is defined by a xpcom 1.79 + // component. On other threads, it is available automatically 1.80 + Cu.import("resource://gre/modules/ctypes.jsm"); 1.81 + Cc["@mozilla.org/net/osfileconstantsservice;1"]. 1.82 + getService(Ci.nsIOSFileConstantsService).init(); 1.83 +} 1.84 + 1.85 +exports.Constants = OS.Constants; 1.86 + 1.87 +///////////////////// Utilities 1.88 + 1.89 +// Define a lazy getter for a property 1.90 +let defineLazyGetter = function defineLazyGetter(object, name, getter) { 1.91 + Object.defineProperty(object, name, { 1.92 + configurable: true, 1.93 + get: function lazy() { 1.94 + delete this[name]; 1.95 + let value = getter.call(this); 1.96 + Object.defineProperty(object, name, { 1.97 + value: value 1.98 + }); 1.99 + return value; 1.100 + } 1.101 + }); 1.102 +}; 1.103 +exports.defineLazyGetter = defineLazyGetter; 1.104 + 1.105 + 1.106 +///////////////////// Logging 1.107 + 1.108 +/** 1.109 + * The default implementation of the logger. 1.110 + * 1.111 + * The choice of logger can be overridden with Config.TEST. 1.112 + */ 1.113 +let gLogger; 1.114 +if (typeof window != "undefined" && window.console && console.log) { 1.115 + gLogger = console.log.bind(console, "OS"); 1.116 +} else { 1.117 + gLogger = function(...args) { 1.118 + dump("OS " + args.join(" ") + "\n"); 1.119 + }; 1.120 +} 1.121 + 1.122 +/** 1.123 + * Attempt to stringify an argument into something useful for 1.124 + * debugging purposes, by using |.toString()| or |JSON.stringify| 1.125 + * if available. 1.126 + * 1.127 + * @param {*} arg An argument to be stringified if possible. 1.128 + * @return {string} A stringified version of |arg|. 1.129 + */ 1.130 +let stringifyArg = function stringifyArg(arg) { 1.131 + if (typeof arg === "string") { 1.132 + return arg; 1.133 + } 1.134 + if (arg && typeof arg === "object") { 1.135 + let argToString = "" + arg; 1.136 + 1.137 + /** 1.138 + * The only way to detect whether this object has a non-default 1.139 + * implementation of |toString| is to check whether it returns 1.140 + * '[object Object]'. Unfortunately, we cannot simply compare |arg.toString| 1.141 + * and |Object.prototype.toString| as |arg| typically comes from another 1.142 + * compartment. 1.143 + */ 1.144 + if (argToString === "[object Object]") { 1.145 + return JSON.stringify(arg); 1.146 + } else { 1.147 + return argToString; 1.148 + } 1.149 + } 1.150 + return arg; 1.151 +}; 1.152 + 1.153 +let LOG = function (...args) { 1.154 + if (!Config.DEBUG) { 1.155 + // If logging is deactivated, don't log 1.156 + return; 1.157 + } 1.158 + 1.159 + let logFunc = gLogger; 1.160 + if (Config.TEST && typeof Components != "undefined") { 1.161 + // If _TESTING_LOGGING is set, and if we are on the main thread, 1.162 + // redirect logs to Services.console, for testing purposes 1.163 + logFunc = function logFunc(...args) { 1.164 + let message = ["TEST", "OS"].concat(args).join(" "); 1.165 + Services.console.logStringMessage(message + "\n"); 1.166 + }; 1.167 + } 1.168 + logFunc.apply(null, [stringifyArg(arg) for (arg of args)]); 1.169 +}; 1.170 + 1.171 +exports.LOG = LOG; 1.172 + 1.173 +/** 1.174 + * Return a shallow clone of the enumerable properties of an object. 1.175 + * 1.176 + * Utility used whenever normalizing options requires making (shallow) 1.177 + * changes to an option object. The copy ensures that we do not modify 1.178 + * a client-provided object by accident. 1.179 + * 1.180 + * Note: to reference and not copy specific fields, provide an optional 1.181 + * |refs| argument containing their names. 1.182 + * 1.183 + * @param {JSON} object Options to be cloned. 1.184 + * @param {Array} refs An optional array of field names to be passed by 1.185 + * reference instead of copying. 1.186 + */ 1.187 +let clone = function (object, refs = []) { 1.188 + let result = {}; 1.189 + // Make a reference between result[key] and object[key]. 1.190 + let refer = function refer(result, key, object) { 1.191 + Object.defineProperty(result, key, { 1.192 + enumerable: true, 1.193 + get: function() { 1.194 + return object[key]; 1.195 + }, 1.196 + set: function(value) { 1.197 + object[key] = value; 1.198 + } 1.199 + }); 1.200 + }; 1.201 + for (let k in object) { 1.202 + if (refs.indexOf(k) < 0) { 1.203 + result[k] = object[k]; 1.204 + } else { 1.205 + refer(result, k, object); 1.206 + } 1.207 + } 1.208 + return result; 1.209 +}; 1.210 + 1.211 +exports.clone = clone; 1.212 + 1.213 +///////////////////// Abstractions above js-ctypes 1.214 + 1.215 +/** 1.216 + * Abstraction above js-ctypes types. 1.217 + * 1.218 + * Use values of this type to register FFI functions. In addition to the 1.219 + * usual features of js-ctypes, values of this type perform the necessary 1.220 + * transformations to ensure that C errors are handled nicely, to connect 1.221 + * resources with their finalizer, etc. 1.222 + * 1.223 + * @param {string} name The name of the type. Must be unique. 1.224 + * @param {CType} implementation The js-ctypes implementation of the type. 1.225 + * 1.226 + * @constructor 1.227 + */ 1.228 +function Type(name, implementation) { 1.229 + if (!(typeof name == "string")) { 1.230 + throw new TypeError("Type expects as first argument a name, got: " 1.231 + + name); 1.232 + } 1.233 + if (!(implementation instanceof ctypes.CType)) { 1.234 + throw new TypeError("Type expects as second argument a ctypes.CType"+ 1.235 + ", got: " + implementation); 1.236 + } 1.237 + Object.defineProperty(this, "name", { value: name }); 1.238 + Object.defineProperty(this, "implementation", { value: implementation }); 1.239 +} 1.240 +Type.prototype = { 1.241 + /** 1.242 + * Serialize a value of |this| |Type| into a format that can 1.243 + * be transmitted as a message (not necessarily a string). 1.244 + * 1.245 + * In the default implementation, the method returns the 1.246 + * value unchanged. 1.247 + */ 1.248 + toMsg: function default_toMsg(value) { 1.249 + return value; 1.250 + }, 1.251 + /** 1.252 + * Deserialize a message to a value of |this| |Type|. 1.253 + * 1.254 + * In the default implementation, the method returns the 1.255 + * message unchanged. 1.256 + */ 1.257 + fromMsg: function default_fromMsg(msg) { 1.258 + return msg; 1.259 + }, 1.260 + /** 1.261 + * Import a value from C. 1.262 + * 1.263 + * In this default implementation, return the value 1.264 + * unchanged. 1.265 + */ 1.266 + importFromC: function default_importFromC(value) { 1.267 + return value; 1.268 + }, 1.269 + 1.270 + /** 1.271 + * A pointer/array used to pass data to the foreign function. 1.272 + */ 1.273 + get in_ptr() { 1.274 + delete this.in_ptr; 1.275 + let ptr_t = new PtrType( 1.276 + "[in] " + this.name + "*", 1.277 + this.implementation.ptr, 1.278 + this); 1.279 + Object.defineProperty(this, "in_ptr", 1.280 + { 1.281 + get: function() { 1.282 + return ptr_t; 1.283 + } 1.284 + }); 1.285 + return ptr_t; 1.286 + }, 1.287 + 1.288 + /** 1.289 + * A pointer/array used to receive data from the foreign function. 1.290 + */ 1.291 + get out_ptr() { 1.292 + delete this.out_ptr; 1.293 + let ptr_t = new PtrType( 1.294 + "[out] " + this.name + "*", 1.295 + this.implementation.ptr, 1.296 + this); 1.297 + Object.defineProperty(this, "out_ptr", 1.298 + { 1.299 + get: function() { 1.300 + return ptr_t; 1.301 + } 1.302 + }); 1.303 + return ptr_t; 1.304 + }, 1.305 + 1.306 + /** 1.307 + * A pointer/array used to both pass data to the foreign function 1.308 + * and receive data from the foreign function. 1.309 + * 1.310 + * Whenever possible, prefer using |in_ptr| or |out_ptr|, which 1.311 + * are generally faster. 1.312 + */ 1.313 + get inout_ptr() { 1.314 + delete this.inout_ptr; 1.315 + let ptr_t = new PtrType( 1.316 + "[inout] " + this.name + "*", 1.317 + this.implementation.ptr, 1.318 + this); 1.319 + Object.defineProperty(this, "inout_ptr", 1.320 + { 1.321 + get: function() { 1.322 + return ptr_t; 1.323 + } 1.324 + }); 1.325 + return ptr_t; 1.326 + }, 1.327 + 1.328 + /** 1.329 + * Attach a finalizer to a type. 1.330 + */ 1.331 + releaseWith: function releaseWith(finalizer) { 1.332 + let parent = this; 1.333 + let type = this.withName("[auto " + this.name + ", " + finalizer + "] "); 1.334 + type.importFromC = function importFromC(value, operation) { 1.335 + return ctypes.CDataFinalizer( 1.336 + parent.importFromC(value, operation), 1.337 + finalizer); 1.338 + }; 1.339 + return type; 1.340 + }, 1.341 + 1.342 + /** 1.343 + * Lazy variant of releaseWith. 1.344 + * Attach a finalizer lazily to a type. 1.345 + * 1.346 + * @param {function} getFinalizer The function that 1.347 + * returns finalizer lazily. 1.348 + */ 1.349 + releaseWithLazy: function releaseWithLazy(getFinalizer) { 1.350 + let parent = this; 1.351 + let type = this.withName("[auto " + this.name + ", (lazy)] "); 1.352 + type.importFromC = function importFromC(value, operation) { 1.353 + return ctypes.CDataFinalizer( 1.354 + parent.importFromC(value, operation), 1.355 + getFinalizer()); 1.356 + }; 1.357 + return type; 1.358 + }, 1.359 + 1.360 + /** 1.361 + * Return an alias to a type with a different name. 1.362 + */ 1.363 + withName: function withName(name) { 1.364 + return Object.create(this, {name: {value: name}}); 1.365 + }, 1.366 + 1.367 + /** 1.368 + * Cast a C value to |this| type. 1.369 + * 1.370 + * Throw an error if the value cannot be casted. 1.371 + */ 1.372 + cast: function cast(value) { 1.373 + return ctypes.cast(value, this.implementation); 1.374 + }, 1.375 + 1.376 + /** 1.377 + * Return the number of bytes in a value of |this| type. 1.378 + * 1.379 + * This may not be defined, e.g. for |void_t|, array types 1.380 + * without length, etc. 1.381 + */ 1.382 + get size() { 1.383 + return this.implementation.size; 1.384 + } 1.385 +}; 1.386 + 1.387 +/** 1.388 + * Utility function used to determine whether an object is a typed array 1.389 + */ 1.390 +let isTypedArray = function isTypedArray(obj) { 1.391 + return typeof obj == "object" 1.392 + && "byteOffset" in obj; 1.393 +}; 1.394 +exports.isTypedArray = isTypedArray; 1.395 + 1.396 +/** 1.397 + * A |Type| of pointers. 1.398 + * 1.399 + * @param {string} name The name of this type. 1.400 + * @param {CType} implementation The type of this pointer. 1.401 + * @param {Type} targetType The target type. 1.402 + */ 1.403 +function PtrType(name, implementation, targetType) { 1.404 + Type.call(this, name, implementation); 1.405 + if (targetType == null || !targetType instanceof Type) { 1.406 + throw new TypeError("targetType must be an instance of Type"); 1.407 + } 1.408 + /** 1.409 + * The type of values targeted by this pointer type. 1.410 + */ 1.411 + Object.defineProperty(this, "targetType", { 1.412 + value: targetType 1.413 + }); 1.414 +} 1.415 +PtrType.prototype = Object.create(Type.prototype); 1.416 + 1.417 +/** 1.418 + * Convert a value to a pointer. 1.419 + * 1.420 + * Protocol: 1.421 + * - |null| returns |null| 1.422 + * - a string returns |{string: value}| 1.423 + * - a typed array returns |{ptr: address_of_buffer}| 1.424 + * - a C array returns |{ptr: address_of_buffer}| 1.425 + * everything else raises an error 1.426 + */ 1.427 +PtrType.prototype.toMsg = function ptr_toMsg(value) { 1.428 + if (value == null) { 1.429 + return null; 1.430 + } 1.431 + if (typeof value == "string") { 1.432 + return { string: value }; 1.433 + } 1.434 + let normalized; 1.435 + if (isTypedArray(value)) { // Typed array 1.436 + normalized = Type.uint8_t.in_ptr.implementation(value.buffer); 1.437 + if (value.byteOffset != 0) { 1.438 + normalized = offsetBy(normalized, value.byteOffset); 1.439 + } 1.440 + } else if ("addressOfElement" in value) { // C array 1.441 + normalized = value.addressOfElement(0); 1.442 + } else if ("isNull" in value) { // C pointer 1.443 + normalized = value; 1.444 + } else { 1.445 + throw new TypeError("Value " + value + 1.446 + " cannot be converted to a pointer"); 1.447 + } 1.448 + let cast = Type.uintptr_t.cast(normalized); 1.449 + return {ptr: cast.value.toString()}; 1.450 +}; 1.451 + 1.452 +/** 1.453 + * Convert a message back to a pointer. 1.454 + */ 1.455 +PtrType.prototype.fromMsg = function ptr_fromMsg(msg) { 1.456 + if (msg == null) { 1.457 + return null; 1.458 + } 1.459 + if ("string" in msg) { 1.460 + return msg.string; 1.461 + } 1.462 + if ("ptr" in msg) { 1.463 + let address = ctypes.uintptr_t(msg.ptr); 1.464 + return this.cast(address); 1.465 + } 1.466 + throw new TypeError("Message " + msg.toSource() + 1.467 + " does not represent a pointer"); 1.468 +}; 1.469 + 1.470 +exports.Type = Type; 1.471 + 1.472 + 1.473 +/* 1.474 + * Some values are large integers on 64 bit platforms. Unfortunately, 1.475 + * in practice, 64 bit integers cannot be manipulated in JS. We 1.476 + * therefore project them to regular numbers whenever possible. 1.477 + */ 1.478 + 1.479 +let projectLargeInt = function projectLargeInt(x) { 1.480 + let str = x.toString(); 1.481 + let rv = parseInt(str, 10); 1.482 + if (rv.toString() !== str) { 1.483 + throw new TypeError("Number " + str + " cannot be projected to a double"); 1.484 + } 1.485 + return rv; 1.486 +}; 1.487 +let projectLargeUInt = function projectLargeUInt(x) { 1.488 + return projectLargeInt(x); 1.489 +}; 1.490 +let projectValue = function projectValue(x) { 1.491 + if (!(x instanceof ctypes.CData)) { 1.492 + return x; 1.493 + } 1.494 + if (!("value" in x)) { // Sanity check 1.495 + throw new TypeError("Number " + x.toSource() + " has no field |value|"); 1.496 + } 1.497 + return x.value; 1.498 +}; 1.499 + 1.500 +function projector(type, signed) { 1.501 + LOG("Determining best projection for", type, 1.502 + "(size: ", type.size, ")", signed?"signed":"unsigned"); 1.503 + if (type instanceof Type) { 1.504 + type = type.implementation; 1.505 + } 1.506 + if (!type.size) { 1.507 + throw new TypeError("Argument is not a proper C type"); 1.508 + } 1.509 + // Determine if type is projected to Int64/Uint64 1.510 + if (type.size == 8 // Usual case 1.511 + // The following cases have special treatment in js-ctypes 1.512 + // Regardless of their size, the value getter returns 1.513 + // a Int64/Uint64 1.514 + || type == ctypes.size_t // Special cases 1.515 + || type == ctypes.ssize_t 1.516 + || type == ctypes.intptr_t 1.517 + || type == ctypes.uintptr_t 1.518 + || type == ctypes.off_t) { 1.519 + if (signed) { 1.520 + LOG("Projected as a large signed integer"); 1.521 + return projectLargeInt; 1.522 + } else { 1.523 + LOG("Projected as a large unsigned integer"); 1.524 + return projectLargeUInt; 1.525 + } 1.526 + } 1.527 + LOG("Projected as a regular number"); 1.528 + return projectValue; 1.529 +}; 1.530 +exports.projectValue = projectValue; 1.531 + 1.532 +/** 1.533 + * Get the appropriate type for an unsigned int of the given size. 1.534 + * 1.535 + * This function is useful to define types such as |mode_t| whose 1.536 + * actual width depends on the OS/platform. 1.537 + * 1.538 + * @param {number} size The number of bytes requested. 1.539 + */ 1.540 +Type.uintn_t = function uintn_t(size) { 1.541 + switch (size) { 1.542 + case 1: return Type.uint8_t; 1.543 + case 2: return Type.uint16_t; 1.544 + case 4: return Type.uint32_t; 1.545 + case 8: return Type.uint64_t; 1.546 + default: 1.547 + throw new Error("Cannot represent unsigned integers of " + size + " bytes"); 1.548 + } 1.549 +}; 1.550 + 1.551 +/** 1.552 + * Get the appropriate type for an signed int of the given size. 1.553 + * 1.554 + * This function is useful to define types such as |mode_t| whose 1.555 + * actual width depends on the OS/platform. 1.556 + * 1.557 + * @param {number} size The number of bytes requested. 1.558 + */ 1.559 +Type.intn_t = function intn_t(size) { 1.560 + switch (size) { 1.561 + case 1: return Type.int8_t; 1.562 + case 2: return Type.int16_t; 1.563 + case 4: return Type.int32_t; 1.564 + case 8: return Type.int64_t; 1.565 + default: 1.566 + throw new Error("Cannot represent integers of " + size + " bytes"); 1.567 + } 1.568 +}; 1.569 + 1.570 +/** 1.571 + * Actual implementation of common C types. 1.572 + */ 1.573 + 1.574 +/** 1.575 + * The void value. 1.576 + */ 1.577 +Type.void_t = 1.578 + new Type("void", 1.579 + ctypes.void_t); 1.580 + 1.581 +/** 1.582 + * Shortcut for |void*|. 1.583 + */ 1.584 +Type.voidptr_t = 1.585 + new PtrType("void*", 1.586 + ctypes.voidptr_t, 1.587 + Type.void_t); 1.588 + 1.589 +// void* is a special case as we can cast any pointer to/from it 1.590 +// so we have to shortcut |in_ptr|/|out_ptr|/|inout_ptr| and 1.591 +// ensure that js-ctypes' casting mechanism is invoked directly 1.592 +["in_ptr", "out_ptr", "inout_ptr"].forEach(function(key) { 1.593 + Object.defineProperty(Type.void_t, key, 1.594 + { 1.595 + value: Type.voidptr_t 1.596 + }); 1.597 +}); 1.598 + 1.599 +/** 1.600 + * A Type of integers. 1.601 + * 1.602 + * @param {string} name The name of this type. 1.603 + * @param {CType} implementation The underlying js-ctypes implementation. 1.604 + * @param {bool} signed |true| if this is a type of signed integers, 1.605 + * |false| otherwise. 1.606 + * 1.607 + * @constructor 1.608 + */ 1.609 +function IntType(name, implementation, signed) { 1.610 + Type.call(this, name, implementation); 1.611 + this.importFromC = projector(implementation, signed); 1.612 + this.project = this.importFromC; 1.613 +}; 1.614 +IntType.prototype = Object.create(Type.prototype); 1.615 +IntType.prototype.toMsg = function toMsg(value) { 1.616 + if (typeof value == "number") { 1.617 + return value; 1.618 + } 1.619 + return this.project(value); 1.620 +}; 1.621 + 1.622 +/** 1.623 + * A C char (one byte) 1.624 + */ 1.625 +Type.char = 1.626 + new Type("char", 1.627 + ctypes.char); 1.628 + 1.629 +/** 1.630 + * A C wide char (two bytes) 1.631 + */ 1.632 +Type.jschar = 1.633 + new Type("jschar", 1.634 + ctypes.jschar); 1.635 + 1.636 + /** 1.637 + * Base string types. 1.638 + */ 1.639 +Type.cstring = Type.char.in_ptr.withName("[in] C string"); 1.640 +Type.wstring = Type.jschar.in_ptr.withName("[in] wide string"); 1.641 +Type.out_cstring = Type.char.out_ptr.withName("[out] C string"); 1.642 +Type.out_wstring = Type.jschar.out_ptr.withName("[out] wide string"); 1.643 + 1.644 +/** 1.645 + * A C integer (8-bits). 1.646 + */ 1.647 +Type.int8_t = 1.648 + new IntType("int8_t", ctypes.int8_t, true); 1.649 + 1.650 +Type.uint8_t = 1.651 + new IntType("uint8_t", ctypes.uint8_t, false); 1.652 + 1.653 +/** 1.654 + * A C integer (16-bits). 1.655 + * 1.656 + * Also known as WORD under Windows. 1.657 + */ 1.658 +Type.int16_t = 1.659 + new IntType("int16_t", ctypes.int16_t, true); 1.660 + 1.661 +Type.uint16_t = 1.662 + new IntType("uint16_t", ctypes.uint16_t, false); 1.663 + 1.664 +/** 1.665 + * A C integer (32-bits). 1.666 + * 1.667 + * Also known as DWORD under Windows. 1.668 + */ 1.669 +Type.int32_t = 1.670 + new IntType("int32_t", ctypes.int32_t, true); 1.671 + 1.672 +Type.uint32_t = 1.673 + new IntType("uint32_t", ctypes.uint32_t, false); 1.674 + 1.675 +/** 1.676 + * A C integer (64-bits). 1.677 + */ 1.678 +Type.int64_t = 1.679 + new IntType("int64_t", ctypes.int64_t, true); 1.680 + 1.681 +Type.uint64_t = 1.682 + new IntType("uint64_t", ctypes.uint64_t, false); 1.683 + 1.684 + /** 1.685 + * A C integer 1.686 + * 1.687 + * Size depends on the platform. 1.688 + */ 1.689 +Type.int = Type.intn_t(ctypes.int.size). 1.690 + withName("int"); 1.691 + 1.692 +Type.unsigned_int = Type.intn_t(ctypes.unsigned_int.size). 1.693 + withName("unsigned int"); 1.694 + 1.695 +/** 1.696 + * A C long integer. 1.697 + * 1.698 + * Size depends on the platform. 1.699 + */ 1.700 +Type.long = 1.701 + Type.intn_t(ctypes.long.size).withName("long"); 1.702 + 1.703 +Type.unsigned_long = 1.704 + Type.intn_t(ctypes.unsigned_long.size).withName("unsigned long"); 1.705 + 1.706 +/** 1.707 + * An unsigned integer with the same size as a pointer. 1.708 + * 1.709 + * Used to cast a pointer to an integer, whenever necessary. 1.710 + */ 1.711 +Type.uintptr_t = 1.712 + Type.uintn_t(ctypes.uintptr_t.size).withName("uintptr_t"); 1.713 + 1.714 +/** 1.715 + * A boolean. 1.716 + * Implemented as a C integer. 1.717 + */ 1.718 +Type.bool = Type.int.withName("bool"); 1.719 +Type.bool.importFromC = function projectBool(x) { 1.720 + return !!(x.value); 1.721 +}; 1.722 + 1.723 +/** 1.724 + * A user identifier. 1.725 + * 1.726 + * Implemented as a C integer. 1.727 + */ 1.728 +Type.uid_t = 1.729 + Type.int.withName("uid_t"); 1.730 + 1.731 +/** 1.732 + * A group identifier. 1.733 + * 1.734 + * Implemented as a C integer. 1.735 + */ 1.736 +Type.gid_t = 1.737 + Type.int.withName("gid_t"); 1.738 + 1.739 +/** 1.740 + * An offset (positive or negative). 1.741 + * 1.742 + * Implemented as a C integer. 1.743 + */ 1.744 +Type.off_t = 1.745 + new IntType("off_t", ctypes.off_t, true); 1.746 + 1.747 +/** 1.748 + * A size (positive). 1.749 + * 1.750 + * Implemented as a C size_t. 1.751 + */ 1.752 +Type.size_t = 1.753 + new IntType("size_t", ctypes.size_t, false); 1.754 + 1.755 +/** 1.756 + * An offset (positive or negative). 1.757 + * Implemented as a C integer. 1.758 + */ 1.759 +Type.ssize_t = 1.760 + new IntType("ssize_t", ctypes.ssize_t, true); 1.761 + 1.762 +/** 1.763 + * Encoding/decoding strings 1.764 + */ 1.765 +Type.uencoder = 1.766 + new Type("uencoder", ctypes.StructType("uencoder")); 1.767 +Type.udecoder = 1.768 + new Type("udecoder", ctypes.StructType("udecoder")); 1.769 + 1.770 +/** 1.771 + * Utility class, used to build a |struct| type 1.772 + * from a set of field names, types and offsets. 1.773 + * 1.774 + * @param {string} name The name of the |struct| type. 1.775 + * @param {number} size The total size of the |struct| type in bytes. 1.776 + */ 1.777 +function HollowStructure(name, size) { 1.778 + if (!name) { 1.779 + throw new TypeError("HollowStructure expects a name"); 1.780 + } 1.781 + if (!size || size < 0) { 1.782 + throw new TypeError("HollowStructure expects a (positive) size"); 1.783 + } 1.784 + 1.785 + // A mapping from offsets in the struct to name/type pairs 1.786 + // (or nothing if no field starts at that offset). 1.787 + this.offset_to_field_info = []; 1.788 + 1.789 + // The name of the struct 1.790 + this.name = name; 1.791 + 1.792 + // The size of the struct, in bytes 1.793 + this.size = size; 1.794 + 1.795 + // The number of paddings inserted so far. 1.796 + // Used to give distinct names to padding fields. 1.797 + this._paddings = 0; 1.798 +} 1.799 +HollowStructure.prototype = { 1.800 + /** 1.801 + * Add a field at a given offset. 1.802 + * 1.803 + * @param {number} offset The offset at which to insert the field. 1.804 + * @param {string} name The name of the field. 1.805 + * @param {CType|Type} type The type of the field. 1.806 + */ 1.807 + add_field_at: function add_field_at(offset, name, type) { 1.808 + if (offset == null) { 1.809 + throw new TypeError("add_field_at requires a non-null offset"); 1.810 + } 1.811 + if (!name) { 1.812 + throw new TypeError("add_field_at requires a non-null name"); 1.813 + } 1.814 + if (!type) { 1.815 + throw new TypeError("add_field_at requires a non-null type"); 1.816 + } 1.817 + if (type instanceof Type) { 1.818 + type = type.implementation; 1.819 + } 1.820 + if (this.offset_to_field_info[offset]) { 1.821 + throw new Error("HollowStructure " + this.name + 1.822 + " already has a field at offset " + offset); 1.823 + } 1.824 + if (offset + type.size > this.size) { 1.825 + throw new Error("HollowStructure " + this.name + 1.826 + " cannot place a value of type " + type + 1.827 + " at offset " + offset + 1.828 + " without exceeding its size of " + this.size); 1.829 + } 1.830 + let field = {name: name, type:type}; 1.831 + this.offset_to_field_info[offset] = field; 1.832 + }, 1.833 + 1.834 + /** 1.835 + * Create a pseudo-field that will only serve as padding. 1.836 + * 1.837 + * @param {number} size The number of bytes in the field. 1.838 + * @return {Object} An association field-name => field-type, 1.839 + * as expected by |ctypes.StructType|. 1.840 + */ 1.841 + _makePaddingField: function makePaddingField(size) { 1.842 + let field = ({}); 1.843 + field["padding_" + this._paddings] = 1.844 + ctypes.ArrayType(ctypes.uint8_t, size); 1.845 + this._paddings++; 1.846 + return field; 1.847 + }, 1.848 + 1.849 + /** 1.850 + * Convert this |HollowStructure| into a |Type|. 1.851 + */ 1.852 + getType: function getType() { 1.853 + // Contents of the structure, in the format expected 1.854 + // by ctypes.StructType. 1.855 + let struct = []; 1.856 + 1.857 + let i = 0; 1.858 + while (i < this.size) { 1.859 + let currentField = this.offset_to_field_info[i]; 1.860 + if (!currentField) { 1.861 + // No field was specified at this offset, we need to 1.862 + // introduce some padding. 1.863 + 1.864 + // Firstly, determine how many bytes of padding 1.865 + let padding_length = 1; 1.866 + while (i + padding_length < this.size 1.867 + && !this.offset_to_field_info[i + padding_length]) { 1.868 + ++padding_length; 1.869 + } 1.870 + 1.871 + // Then add the padding 1.872 + struct.push(this._makePaddingField(padding_length)); 1.873 + 1.874 + // And proceed 1.875 + i += padding_length; 1.876 + } else { 1.877 + // We have a field at this offset. 1.878 + 1.879 + // Firstly, ensure that we do not have two overlapping fields 1.880 + for (let j = 1; j < currentField.type.size; ++j) { 1.881 + let candidateField = this.offset_to_field_info[i + j]; 1.882 + if (candidateField) { 1.883 + throw new Error("Fields " + currentField.name + 1.884 + " and " + candidateField.name + 1.885 + " overlap at position " + (i + j)); 1.886 + } 1.887 + } 1.888 + 1.889 + // Then add the field 1.890 + let field = ({}); 1.891 + field[currentField.name] = currentField.type; 1.892 + struct.push(field); 1.893 + 1.894 + // And proceed 1.895 + i += currentField.type.size; 1.896 + } 1.897 + } 1.898 + let result = new Type(this.name, ctypes.StructType(this.name, struct)); 1.899 + if (result.implementation.size != this.size) { 1.900 + throw new Error("Wrong size for type " + this.name + 1.901 + ": expected " + this.size + 1.902 + ", found " + result.implementation.size + 1.903 + " (" + result.implementation.toSource() + ")"); 1.904 + } 1.905 + return result; 1.906 + } 1.907 +}; 1.908 +exports.HollowStructure = HollowStructure; 1.909 + 1.910 +/** 1.911 + * Representation of a native library. 1.912 + * 1.913 + * The native library is opened lazily, during the first call to its 1.914 + * field |library| or whenever accessing one of the methods imported 1.915 + * with declareLazyFFI. 1.916 + * 1.917 + * @param {string} name A human-readable name for the library. Used 1.918 + * for debugging and error reporting. 1.919 + * @param {string...} candidates A list of system libraries that may 1.920 + * represent this library. Used e.g. to try different library names 1.921 + * on distinct operating systems ("libxul", "XUL", etc.). 1.922 + * 1.923 + * @constructor 1.924 + */ 1.925 +function Library(name, ...candidates) { 1.926 + this.name = name; 1.927 + this._candidates = candidates; 1.928 +}; 1.929 +Library.prototype = Object.freeze({ 1.930 + /** 1.931 + * The native library as a js-ctypes object. 1.932 + * 1.933 + * @throws {Error} If none of the candidate libraries could be opened. 1.934 + */ 1.935 + get library() { 1.936 + let library; 1.937 + delete this.library; 1.938 + for (let candidate of this._candidates) { 1.939 + try { 1.940 + library = ctypes.open(candidate); 1.941 + break; 1.942 + } catch (ex) { 1.943 + LOG("Could not open library", candidate, ex); 1.944 + } 1.945 + } 1.946 + this._candidates = null; 1.947 + if (library) { 1.948 + Object.defineProperty(this, "library", { 1.949 + value: library 1.950 + }); 1.951 + Object.freeze(this); 1.952 + return library; 1.953 + } 1.954 + let error = new Error("Could not open library " + this.name); 1.955 + Object.defineProperty(this, "library", { 1.956 + get: function() { 1.957 + throw error; 1.958 + } 1.959 + }); 1.960 + Object.freeze(this); 1.961 + throw error; 1.962 + }, 1.963 + 1.964 + /** 1.965 + * Declare a function, lazily. 1.966 + * 1.967 + * @param {object} The object containing the function as a field. 1.968 + * @param {string} The name of the field containing the function. 1.969 + * @param {string} symbol The name of the function, as defined in the 1.970 + * library. 1.971 + * @param {ctypes.abi} abi The abi to use, or |null| for default. 1.972 + * @param {Type} returnType The type of values returned by the function. 1.973 + * @param {...Type} argTypes The type of arguments to the function. 1.974 + */ 1.975 + declareLazyFFI: function(object, field, ...args) { 1.976 + let lib = this; 1.977 + Object.defineProperty(object, field, { 1.978 + get: function() { 1.979 + delete this[field]; 1.980 + let ffi = declareFFI(lib.library, ...args); 1.981 + if (ffi) { 1.982 + return this[field] = ffi; 1.983 + } 1.984 + return undefined; 1.985 + }, 1.986 + configurable: true, 1.987 + enumerable: true 1.988 + }); 1.989 + }, 1.990 + 1.991 + /** 1.992 + * Define a js-ctypes function lazily using ctypes method declare. 1.993 + * 1.994 + * @param {object} The object containing the function as a field. 1.995 + * @param {string} The name of the field containing the function. 1.996 + * @param {string} symbol The name of the function, as defined in the 1.997 + * library. 1.998 + * @param {ctypes.abi} abi The abi to use, or |null| for default. 1.999 + * @param {ctypes.CType} returnType The type of values returned by the function. 1.1000 + * @param {...ctypes.CType} argTypes The type of arguments to the function. 1.1001 + */ 1.1002 + declareLazy: function(object, field, ...args) { 1.1003 + let lib = this; 1.1004 + Object.defineProperty(object, field, { 1.1005 + get: function() { 1.1006 + delete this[field]; 1.1007 + let ffi = lib.library.declare(...args); 1.1008 + if (ffi) { 1.1009 + return this[field] = ffi; 1.1010 + } 1.1011 + return undefined; 1.1012 + }, 1.1013 + configurable: true, 1.1014 + enumerable: true 1.1015 + }); 1.1016 + }, 1.1017 + 1.1018 + toString: function() { 1.1019 + return "[Library " + this.name + "]"; 1.1020 + } 1.1021 +}); 1.1022 +exports.Library = Library; 1.1023 + 1.1024 +/** 1.1025 + * Declare a function through js-ctypes 1.1026 + * 1.1027 + * @param {ctypes.library} lib The ctypes library holding the function. 1.1028 + * @param {string} symbol The name of the function, as defined in the 1.1029 + * library. 1.1030 + * @param {ctypes.abi} abi The abi to use, or |null| for default. 1.1031 + * @param {Type} returnType The type of values returned by the function. 1.1032 + * @param {...Type} argTypes The type of arguments to the function. 1.1033 + * 1.1034 + * @return null if the function could not be defined (generally because 1.1035 + * it does not exist), or a JavaScript wrapper performing the call to C 1.1036 + * and any type conversion required. 1.1037 + */ 1.1038 +let declareFFI = function declareFFI(lib, symbol, abi, 1.1039 + returnType /*, argTypes ...*/) { 1.1040 + LOG("Attempting to declare FFI ", symbol); 1.1041 + // We guard agressively, to avoid any late surprise 1.1042 + if (typeof symbol != "string") { 1.1043 + throw new TypeError("declareFFI expects as first argument a string"); 1.1044 + } 1.1045 + abi = abi || ctypes.default_abi; 1.1046 + if (Object.prototype.toString.call(abi) != "[object CABI]") { 1.1047 + // Note: This is the only known manner of checking whether an object 1.1048 + // is an abi. 1.1049 + throw new TypeError("declareFFI expects as second argument an abi or null"); 1.1050 + } 1.1051 + if (!returnType.importFromC) { 1.1052 + throw new TypeError("declareFFI expects as third argument an instance of Type"); 1.1053 + } 1.1054 + let signature = [symbol, abi]; 1.1055 + let argtypes = []; 1.1056 + for (let i = 3; i < arguments.length; ++i) { 1.1057 + let current = arguments[i]; 1.1058 + if (!current) { 1.1059 + throw new TypeError("Missing type for argument " + ( i - 3 ) + 1.1060 + " of symbol " + symbol); 1.1061 + } 1.1062 + if (!current.implementation) { 1.1063 + throw new TypeError("Missing implementation for argument " + (i - 3) 1.1064 + + " of symbol " + symbol 1.1065 + + " ( " + current.name + " )" ); 1.1066 + } 1.1067 + signature.push(current.implementation); 1.1068 + } 1.1069 + try { 1.1070 + let fun = lib.declare.apply(lib, signature); 1.1071 + let result = function ffi(...args) { 1.1072 + for (let i = 0; i < args.length; i++) { 1.1073 + if (typeof args[i] == "undefined") { 1.1074 + throw new TypeError("Argument " + i + " of " + symbol + " is undefined"); 1.1075 + } 1.1076 + } 1.1077 + let result = fun.apply(fun, args); 1.1078 + return returnType.importFromC(result, symbol); 1.1079 + }; 1.1080 + LOG("Function", symbol, "declared"); 1.1081 + return result; 1.1082 + } catch (x) { 1.1083 + // Note: Not being able to declare a function is normal. 1.1084 + // Some functions are OS (or OS version)-specific. 1.1085 + LOG("Could not declare function ", symbol, x); 1.1086 + return null; 1.1087 + } 1.1088 +}; 1.1089 +exports.declareFFI = declareFFI; 1.1090 + 1.1091 +/** 1.1092 + * Define a lazy getter to a js-ctypes function using declareFFI. 1.1093 + * 1.1094 + * @param {object} The object containing the function as a field. 1.1095 + * @param {string} The name of the field containing the function. 1.1096 + * @param {ctypes.library} lib The ctypes library holding the function. 1.1097 + * @param {string} symbol The name of the function, as defined in the 1.1098 + * library. 1.1099 + * @param {ctypes.abi} abi The abi to use, or |null| for default. 1.1100 + * @param {Type} returnType The type of values returned by the function. 1.1101 + * @param {...Type} argTypes The type of arguments to the function. 1.1102 + */ 1.1103 +function declareLazyFFI(object, field, ...declareFFIArgs) { 1.1104 + Object.defineProperty(object, field, { 1.1105 + get: function() { 1.1106 + delete this[field]; 1.1107 + let ffi = declareFFI(...declareFFIArgs); 1.1108 + if (ffi) { 1.1109 + return this[field] = ffi; 1.1110 + } 1.1111 + return undefined; 1.1112 + }, 1.1113 + configurable: true, 1.1114 + enumerable: true 1.1115 + }); 1.1116 +} 1.1117 +exports.declareLazyFFI = declareLazyFFI; 1.1118 + 1.1119 +/** 1.1120 + * Define a lazy getter to a js-ctypes function using ctypes method declare. 1.1121 + * 1.1122 + * @param {object} The object containing the function as a field. 1.1123 + * @param {string} The name of the field containing the function. 1.1124 + * @param {ctypes.library} lib The ctypes library holding the function. 1.1125 + * @param {string} symbol The name of the function, as defined in the 1.1126 + * library. 1.1127 + * @param {ctypes.abi} abi The abi to use, or |null| for default. 1.1128 + * @param {ctypes.CType} returnType The type of values returned by the function. 1.1129 + * @param {...ctypes.CType} argTypes The type of arguments to the function. 1.1130 + */ 1.1131 +function declareLazy(object, field, lib, ...declareArgs) { 1.1132 + Object.defineProperty(object, field, { 1.1133 + get: function() { 1.1134 + delete this[field]; 1.1135 + try { 1.1136 + let ffi = lib.declare(...declareArgs); 1.1137 + return this[field] = ffi; 1.1138 + } catch (ex) { 1.1139 + // The symbol doesn't exist 1.1140 + return undefined; 1.1141 + } 1.1142 + }, 1.1143 + configurable: true 1.1144 + }); 1.1145 +} 1.1146 +exports.declareLazy = declareLazy; 1.1147 + 1.1148 +// A bogus array type used to perform pointer arithmetics 1.1149 +let gOffsetByType; 1.1150 + 1.1151 +/** 1.1152 + * Advance a pointer by a number of items. 1.1153 + * 1.1154 + * This method implements adding an integer to a pointer in C. 1.1155 + * 1.1156 + * Example: 1.1157 + * // ptr is a uint16_t*, 1.1158 + * offsetBy(ptr, 3) 1.1159 + * // returns a uint16_t* with the address ptr + 3 * 2 bytes 1.1160 + * 1.1161 + * @param {C pointer} pointer The start pointer. 1.1162 + * @param {number} length The number of items to advance. Must not be 1.1163 + * negative. 1.1164 + * 1.1165 + * @return {C pointer} |pointer| advanced by |length| items 1.1166 + */ 1.1167 +let offsetBy = 1.1168 + function offsetBy(pointer, length) { 1.1169 + if (length === undefined || length < 0) { 1.1170 + throw new TypeError("offsetBy expects a positive number"); 1.1171 + } 1.1172 + if (!("isNull" in pointer)) { 1.1173 + throw new TypeError("offsetBy expects a pointer"); 1.1174 + } 1.1175 + if (length == 0) { 1.1176 + return pointer; 1.1177 + } 1.1178 + let type = pointer.constructor; 1.1179 + let size = type.targetType.size; 1.1180 + if (size == 0 || size == null) { 1.1181 + throw new TypeError("offsetBy cannot be applied to a pointer without size"); 1.1182 + } 1.1183 + let bytes = length * size; 1.1184 + if (!gOffsetByType || gOffsetByType.size <= bytes) { 1.1185 + gOffsetByType = ctypes.uint8_t.array(bytes * 2); 1.1186 + } 1.1187 + let addr = ctypes.cast(pointer, gOffsetByType.ptr). 1.1188 + contents.addressOfElement(bytes); 1.1189 + return ctypes.cast(addr, type); 1.1190 +}; 1.1191 +exports.offsetBy = offsetBy; 1.1192 + 1.1193 +/** 1.1194 + * Utility function used to normalize a Typed Array or C 1.1195 + * pointer into a uint8_t C pointer. 1.1196 + * 1.1197 + * Future versions might extend this to other data structures. 1.1198 + * 1.1199 + * @param {Typed array | C pointer} candidate The buffer. If 1.1200 + * a C pointer, it must be non-null. 1.1201 + * @param {number} bytes The number of bytes that |candidate| should contain. 1.1202 + * Used for sanity checking if the size of |candidate| can be determined. 1.1203 + * 1.1204 + * @return {ptr:{C pointer}, bytes:number} A C pointer of type uint8_t, 1.1205 + * corresponding to the start of |candidate|. 1.1206 + */ 1.1207 +function normalizeToPointer(candidate, bytes) { 1.1208 + if (!candidate) { 1.1209 + throw new TypeError("Expecting a Typed Array or a C pointer"); 1.1210 + } 1.1211 + let ptr; 1.1212 + if ("isNull" in candidate) { 1.1213 + if (candidate.isNull()) { 1.1214 + throw new TypeError("Expecting a non-null pointer"); 1.1215 + } 1.1216 + ptr = Type.uint8_t.out_ptr.cast(candidate); 1.1217 + if (bytes == null) { 1.1218 + throw new TypeError("C pointer missing bytes indication."); 1.1219 + } 1.1220 + } else if (isTypedArray(candidate)) { 1.1221 + // Typed Array 1.1222 + ptr = Type.uint8_t.out_ptr.implementation(candidate.buffer); 1.1223 + if (bytes == null) { 1.1224 + bytes = candidate.byteLength; 1.1225 + } else if (candidate.byteLength < bytes) { 1.1226 + throw new TypeError("Buffer is too short. I need at least " + 1.1227 + bytes + 1.1228 + " bytes but I have only " + 1.1229 + candidate.byteLength + 1.1230 + "bytes"); 1.1231 + } 1.1232 + } else { 1.1233 + throw new TypeError("Expecting a Typed Array or a C pointer"); 1.1234 + } 1.1235 + return {ptr: ptr, bytes: bytes}; 1.1236 +}; 1.1237 +exports.normalizeToPointer = normalizeToPointer; 1.1238 + 1.1239 +///////////////////// OS interactions 1.1240 + 1.1241 +/** 1.1242 + * An OS error. 1.1243 + * 1.1244 + * This class is provided mostly for type-matching. If you need more 1.1245 + * details about an error, you should use the platform-specific error 1.1246 + * codes provided by subclasses of |OS.Shared.Error|. 1.1247 + * 1.1248 + * @param {string} operation The operation that failed. 1.1249 + * @param {string=} path The path of the file on which the operation failed, 1.1250 + * or nothing if there was no file involved in the failure. 1.1251 + * 1.1252 + * @constructor 1.1253 + */ 1.1254 +function OSError(operation, path = "") { 1.1255 + Error.call(this); 1.1256 + this.operation = operation; 1.1257 + this.path = path; 1.1258 +} 1.1259 +exports.OSError = OSError; 1.1260 + 1.1261 + 1.1262 +///////////////////// Temporary boilerplate 1.1263 +// Boilerplate, to simplify the transition to require() 1.1264 +// Do not rely upon this symbol, it will disappear with 1.1265 +// bug 883050. 1.1266 +exports.OS = { 1.1267 + Constants: exports.Constants, 1.1268 + Shared: { 1.1269 + LOG: LOG, 1.1270 + clone: clone, 1.1271 + Type: Type, 1.1272 + HollowStructure: HollowStructure, 1.1273 + Error: OSError, 1.1274 + declareFFI: declareFFI, 1.1275 + projectValue: projectValue, 1.1276 + isTypedArray: isTypedArray, 1.1277 + defineLazyGetter: defineLazyGetter, 1.1278 + offsetBy: offsetBy 1.1279 + } 1.1280 +}; 1.1281 + 1.1282 +Object.defineProperty(exports.OS.Shared, "DEBUG", { 1.1283 + get: function() { 1.1284 + return Config.DEBUG; 1.1285 + }, 1.1286 + set: function(x) { 1.1287 + return Config.DEBUG = x; 1.1288 + } 1.1289 +}); 1.1290 +Object.defineProperty(exports.OS.Shared, "TEST", { 1.1291 + get: function() { 1.1292 + return Config.TEST; 1.1293 + }, 1.1294 + set: function(x) { 1.1295 + return Config.TEST = x; 1.1296 + } 1.1297 +}); 1.1298 + 1.1299 + 1.1300 +///////////////////// Permanent boilerplate 1.1301 +if (typeof Components != "undefined") { 1.1302 + this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS; 1.1303 + for (let symbol of EXPORTED_SYMBOLS) { 1.1304 + this[symbol] = exports[symbol]; 1.1305 + } 1.1306 +}