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.

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

mercurial