toolkit/components/osfile/modules/osfile_shared_allthreads.jsm

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

mercurial