|
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 } |