2012-06-10 16:44:50 -07:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
{
|
|
|
|
if (typeof Components != "undefined") {
|
|
|
|
// We do not wish osfile_shared.jsm to be used directly as a main thread
|
|
|
|
// module yet. When time comes, it will be loaded by a combination of
|
|
|
|
// a main thread front-end/worker thread implementation that makes sure
|
|
|
|
// that we are not executing synchronous IO code in the main thread.
|
|
|
|
|
|
|
|
throw new Error("osfile_shared.jsm cannot be used from the main thread yet");
|
|
|
|
}
|
|
|
|
|
|
|
|
(function(exports) {
|
|
|
|
"use strict";
|
|
|
|
/*
|
|
|
|
* This block defines |OS.Shared.Type|. However, |OS| can exist already
|
|
|
|
* (in particular, if this code is executed in a worker thread, it is
|
|
|
|
* defined).
|
|
|
|
*/
|
|
|
|
if (!exports.OS) {
|
|
|
|
exports.OS = {};
|
|
|
|
}
|
|
|
|
if (!exports.OS.Shared) {
|
|
|
|
exports.OS.Shared = {};
|
|
|
|
}
|
|
|
|
if (exports.OS.Shared.Type) {
|
|
|
|
return; // Avoid double-initialization
|
|
|
|
}
|
|
|
|
|
|
|
|
let LOG;
|
|
|
|
if (typeof console != "undefined" && console.log) {
|
|
|
|
LOG = console.log.bind(console, "OS");
|
|
|
|
} else {
|
|
|
|
LOG = function() {
|
|
|
|
let text = "OS";
|
|
|
|
for (let i = 0; i < arguments.length; ++i) {
|
|
|
|
text += (" " + arguments[i]);
|
|
|
|
}
|
|
|
|
dump(text + "\n");
|
|
|
|
};
|
|
|
|
}
|
|
|
|
exports.OS.Shared.LOG = LOG;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An OS error.
|
|
|
|
*
|
|
|
|
* This class is provided mostly for type-matching. If you need more
|
|
|
|
* details about an error, you should use the platform-specific error
|
|
|
|
* codes provided by subclasses of |OS.Shared.Error|.
|
|
|
|
*
|
|
|
|
* @param {string} operation The operation that failed.
|
|
|
|
*
|
|
|
|
* @constructor
|
|
|
|
*/
|
|
|
|
function OSError(operation) {
|
|
|
|
Error.call(this);
|
|
|
|
this.operation = operation;
|
|
|
|
}
|
|
|
|
exports.OS.Shared.Error = OSError;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Abstraction above js-ctypes types.
|
|
|
|
*
|
|
|
|
* Use values of this type to register FFI functions. In addition to the
|
|
|
|
* usual features of js-ctypes, values of this type perform the necessary
|
|
|
|
* transformations to ensure that C errors are handled nicely, to connect
|
|
|
|
* resources with their finalizer, etc.
|
|
|
|
*
|
|
|
|
* @param {string} name The name of the type. Must be unique.
|
|
|
|
* @param {function=} convert_from_c A function to call to construct a
|
|
|
|
* value of this type from a C return value.
|
|
|
|
*
|
|
|
|
* @constructor
|
|
|
|
*/
|
|
|
|
function Type(name, implementation, convert_from_c) {
|
|
|
|
if (!(typeof name == "string")) {
|
|
|
|
throw new TypeError("Type expects as first argument a name, got: "
|
|
|
|
+ name);
|
|
|
|
}
|
|
|
|
if (!(implementation instanceof ctypes.CType)) {
|
|
|
|
throw new TypeError("Type expects as second argument a ctypes.CType"+
|
|
|
|
", got: " + implementation);
|
|
|
|
}
|
|
|
|
this.name = name;
|
|
|
|
this.implementation = implementation;
|
|
|
|
if (convert_from_c) {
|
|
|
|
this.convert_from_c = convert_from_c;
|
|
|
|
} else {// Optimization: Ensure harmony of shapes
|
|
|
|
this.convert_from_c = Type.prototype.convert_from_c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Type.prototype = {
|
|
|
|
convert_from_c: function(value) {
|
|
|
|
return value;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A pointer/array used to pass data to the foreign function.
|
|
|
|
*/
|
|
|
|
get in_ptr() {
|
|
|
|
delete this.in_ptr;
|
|
|
|
let ptr_t = new Type("[int] " + this.name + "*",
|
|
|
|
this.implementation.ptr);
|
|
|
|
Object.defineProperty(this, "in_ptr",
|
|
|
|
{
|
|
|
|
get: function() {
|
|
|
|
return ptr_t;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return ptr_t;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A pointer/array used to receive data from the foreign function.
|
|
|
|
*/
|
|
|
|
get out_ptr() {
|
|
|
|
delete this.out_ptr;
|
|
|
|
let ptr_t = new Type("[out] " + this.name + "*",
|
|
|
|
this.implementation.ptr);
|
|
|
|
Object.defineProperty(this, "out_ptr",
|
|
|
|
{
|
|
|
|
get: function() {
|
|
|
|
return ptr_t;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return ptr_t;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A pointer/array used to both pass data to the foreign function
|
|
|
|
* and receive data from the foreign function.
|
|
|
|
*
|
|
|
|
* Whenever possible, prefer using |in_ptr| or |out_ptr|, which
|
|
|
|
* are generally faster.
|
|
|
|
*/
|
|
|
|
get inout_ptr() {
|
|
|
|
delete this.inout_ptr;
|
|
|
|
let ptr_t = new Type("[inout] " + this.name + "*",
|
|
|
|
this.implementation.ptr);
|
|
|
|
Object.defineProperty(this, "inout_ptr",
|
|
|
|
{
|
|
|
|
get: function() {
|
|
|
|
return ptr_t;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return ptr_t;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attach a finalizer to a type.
|
|
|
|
*/
|
|
|
|
releaseWith: function(finalizer) {
|
|
|
|
let parent = this;
|
|
|
|
let type = new Type("[auto " + finalizer +"] " + this.name,
|
|
|
|
this.implementation,
|
|
|
|
function (value, operation) {
|
|
|
|
return ctypes.CDataFinalizer(
|
|
|
|
parent.convert_from_c(value, operation),
|
|
|
|
finalizer);
|
|
|
|
});
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.OS.Shared.Type = Type;
|
|
|
|
let Types = Type;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some values are large integers on 64 bit platforms. Unfortunately,
|
|
|
|
* in practice, 64 bit integers cannot be manipulated in JS. We
|
|
|
|
* therefore project them to regular numbers whenever possible.
|
|
|
|
*/
|
|
|
|
|
|
|
|
let projectLargeInt = function projectLargeInt(x) {
|
2012-06-15 08:22:50 -07:00
|
|
|
return parseInt(x.toString(), 10);
|
2012-06-10 16:44:50 -07:00
|
|
|
};
|
|
|
|
let projectLargeUInt = function projectLargeUInt(x) {
|
|
|
|
if (ctypes.UInt64.hi(x)) {
|
|
|
|
throw new Error("Number too large " + x +
|
|
|
|
"(unsigned, hi: " + ctypes.UInt64.hi(x) +
|
|
|
|
", lo:" + ctypes.UInt64.lo(x) + ")");
|
|
|
|
}
|
|
|
|
return ctypes.UInt64.lo(x);
|
|
|
|
};
|
|
|
|
let projectValue = function projectValue(x) {
|
|
|
|
if (!(x instanceof ctypes.CData)) {
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
if (!("value" in x)) { // Sanity check
|
|
|
|
throw new TypeError("Number " + x.toSource() + " has no field |value|");
|
|
|
|
}
|
|
|
|
return x.value;
|
|
|
|
};
|
|
|
|
|
|
|
|
function projector(type, signed) {
|
|
|
|
if (!type.size) {
|
|
|
|
throw new TypeError("Argument is not a proper C type");
|
|
|
|
}
|
|
|
|
LOG("Determining best projection for", type,
|
|
|
|
"(size: ", type.size, ")", signed?"signed":"unsigned");
|
|
|
|
// Determine if type is projected to Int64/Uint64
|
|
|
|
if (type.size == 8 // Usual case
|
|
|
|
|| type == ctypes.size_t // Special cases
|
|
|
|
|| type == ctypes.ssize_t
|
|
|
|
|| type == ctypes.intptr_t
|
|
|
|
|| type == ctypes.uintptr_t
|
|
|
|
|| type == ctypes.off_t){
|
|
|
|
if (signed) {
|
|
|
|
LOG("Projected as a large signed integer");
|
|
|
|
return projectLargeInt;
|
|
|
|
} else {
|
|
|
|
LOG("Projected as a large unsigned integer");
|
|
|
|
return projectLargeUInt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LOG("Projected as a regular number");
|
|
|
|
return projectValue;
|
|
|
|
};
|
2012-06-27 17:15:33 -07:00
|
|
|
exports.OS.Shared.projectValue = projectValue;
|
|
|
|
|
2012-06-10 16:44:50 -07:00
|
|
|
|
|
|
|
|
2012-06-27 17:15:33 -07:00
|
|
|
/**
|
|
|
|
* Get the appropriate type for an unsigned int of the given size.
|
|
|
|
*
|
|
|
|
* This function is useful to define types such as |mode_t| whose
|
|
|
|
* actual width depends on the OS/platform.
|
|
|
|
*
|
|
|
|
* @param {number} size The number of bytes requested.
|
|
|
|
*/
|
|
|
|
Types.uintn_t = function uintn_t(size) {
|
|
|
|
switch (size) {
|
|
|
|
case 1: return Types.uint8_t;
|
|
|
|
case 2: return Types.uint16_t;
|
|
|
|
case 4: return Types.uint32_t;
|
|
|
|
case 8: return Types.uint64_t;
|
|
|
|
default:
|
|
|
|
throw new Error("Cannot represent unsigned integers of " + size + " bytes");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the appropriate type for an signed int of the given size.
|
|
|
|
*
|
|
|
|
* This function is useful to define types such as |mode_t| whose
|
|
|
|
* actual width depends on the OS/platform.
|
|
|
|
*
|
|
|
|
* @param {number} size The number of bytes requested.
|
|
|
|
*/
|
|
|
|
Types.intn_t = function intn_t(size) {
|
|
|
|
switch (size) {
|
|
|
|
case 1: return Types.int8_t;
|
|
|
|
case 2: return Types.int16_t;
|
|
|
|
case 4: return Types.int32_t;
|
|
|
|
case 8: return Types.int64_t;
|
|
|
|
default:
|
|
|
|
throw new Error("Cannot represent integers of " + size + " bytes");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-06-10 16:44:50 -07:00
|
|
|
/**
|
|
|
|
* Actual implementation of common C types.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The void value.
|
|
|
|
*/
|
|
|
|
Types.void_t =
|
|
|
|
new Type("void",
|
|
|
|
ctypes.void_t);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Shortcut for |void*|.
|
|
|
|
*/
|
|
|
|
Types.voidptr_t =
|
|
|
|
new Type("void*",
|
|
|
|
ctypes.voidptr_t);
|
|
|
|
|
|
|
|
// void* is a special case as we can cast any pointer to/from it
|
|
|
|
// so we have to shortcut |in_ptr|/|out_ptr|/|inout_ptr| and
|
|
|
|
// ensure that js-ctypes' casting mechanism is invoked directly
|
|
|
|
["in_ptr", "out_ptr", "inout_ptr"].forEach(function(key) {
|
|
|
|
Object.defineProperty(Types.void_t, key,
|
|
|
|
{
|
|
|
|
get: function() {
|
|
|
|
return Types.voidptr_t;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A C char (one byte)
|
|
|
|
*/
|
|
|
|
Types.char =
|
|
|
|
new Type("char",
|
|
|
|
ctypes.char);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A C wide char (two bytes)
|
|
|
|
*/
|
|
|
|
Types.jschar =
|
|
|
|
new Type("jschar",
|
|
|
|
ctypes.jschar);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A C integer
|
|
|
|
*
|
|
|
|
* Size depends on the platform.
|
|
|
|
*/
|
|
|
|
Types.int =
|
|
|
|
new Type("int",
|
|
|
|
ctypes.int,
|
|
|
|
projector(ctypes.int, true));
|
|
|
|
|
|
|
|
Types.unsigned_int =
|
|
|
|
new Type("unsigned int",
|
|
|
|
ctypes.unsigned_int,
|
|
|
|
projector(ctypes.unsigned_int, false));
|
|
|
|
|
2012-06-27 17:15:33 -07:00
|
|
|
/**
|
|
|
|
* A C integer (8-bits).
|
|
|
|
*/
|
|
|
|
Types.int8_t =
|
|
|
|
new Type("int8_t",
|
|
|
|
ctypes.int8_t,
|
|
|
|
projectValue);
|
|
|
|
|
|
|
|
Types.uint8_t =
|
|
|
|
new Type("uint8_t",
|
|
|
|
ctypes.uint8_t,
|
|
|
|
projectValue);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A C integer (16-bits).
|
|
|
|
*
|
|
|
|
* Also known as WORD under Windows.
|
|
|
|
*/
|
|
|
|
Types.int16_t =
|
|
|
|
new Type("int16_t",
|
|
|
|
ctypes.int16_t,
|
|
|
|
projectValue);
|
|
|
|
|
|
|
|
Types.uint16_t =
|
|
|
|
new Type("uint16_t",
|
|
|
|
ctypes.uint16_t,
|
|
|
|
projectValue);
|
|
|
|
|
2012-06-10 16:44:50 -07:00
|
|
|
/**
|
|
|
|
* A C integer (32-bits).
|
|
|
|
*
|
|
|
|
* Also known as DWORD under Windows.
|
|
|
|
*/
|
|
|
|
Types.int32_t =
|
|
|
|
new Type("int32_t",
|
|
|
|
ctypes.int32_t,
|
|
|
|
projectValue);
|
|
|
|
|
|
|
|
Types.uint32_t =
|
|
|
|
new Type("uint32_t",
|
|
|
|
ctypes.uint32_t,
|
|
|
|
projectValue);
|
|
|
|
|
2012-06-22 17:24:27 -07:00
|
|
|
/**
|
|
|
|
* A C integer (64-bits).
|
|
|
|
*/
|
|
|
|
Types.int64_t =
|
|
|
|
new Type("int64_t",
|
|
|
|
ctypes.int64_t,
|
|
|
|
projectLargeInt);
|
|
|
|
|
|
|
|
Types.uint64_t =
|
|
|
|
new Type("uint64_t",
|
|
|
|
ctypes.uint64_t,
|
|
|
|
projectLargeUInt);
|
|
|
|
|
2012-06-10 16:44:50 -07:00
|
|
|
/**
|
|
|
|
* A C integer.
|
|
|
|
* Size depends on the platform.
|
|
|
|
*/
|
|
|
|
Types.long =
|
|
|
|
new Type("long",
|
|
|
|
ctypes.long,
|
|
|
|
projector(ctypes.long, true));
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A boolean.
|
|
|
|
* Implemented as a C integer.
|
|
|
|
*/
|
|
|
|
Types.bool =
|
|
|
|
new Type("bool",
|
|
|
|
ctypes.int,
|
|
|
|
function projectBool(x) {
|
|
|
|
return !!(x.value);
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A user identifier.
|
|
|
|
* Implemented as a C integer.
|
|
|
|
*/
|
|
|
|
Types.uid_t =
|
|
|
|
new Type("uid_t",
|
|
|
|
ctypes.int,
|
|
|
|
projector(ctypes.int, true));
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A group identifier.
|
|
|
|
* Implemented as a C integer.
|
|
|
|
*/
|
|
|
|
Types.gid_t =
|
|
|
|
new Type("gid_t",
|
|
|
|
ctypes.int,
|
|
|
|
projector(ctypes.int, true));
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An offset (positive or negative).
|
|
|
|
* Implemented as a C integer.
|
|
|
|
*/
|
|
|
|
Types.off_t =
|
|
|
|
new Type("off_t",
|
|
|
|
ctypes.off_t,
|
|
|
|
projector(ctypes.off_t, true));
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A size (positive).
|
|
|
|
* Implemented as a C size_t.
|
|
|
|
*/
|
|
|
|
Types.size_t =
|
|
|
|
new Type("size_t",
|
|
|
|
ctypes.size_t,
|
|
|
|
projector(ctypes.size_t, false));
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An offset (positive or negative).
|
|
|
|
* Implemented as a C integer.
|
|
|
|
*/
|
|
|
|
Types.ssize_t =
|
|
|
|
new Type("ssize_t",
|
|
|
|
ctypes.ssize_t,
|
|
|
|
projector(ctypes.ssize_t, true));
|
|
|
|
|
2012-07-05 01:20:07 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Utility class, used to build a |struct| type
|
|
|
|
* from a set of field names, types and offsets.
|
|
|
|
*
|
|
|
|
* @param {string} name The name of the |struct| type.
|
|
|
|
* @param {number} size The total size of the |struct| type in bytes.
|
|
|
|
*/
|
|
|
|
function HollowStructure(name, size) {
|
|
|
|
if (!name) {
|
|
|
|
throw new TypeError("HollowStructure expects a name");
|
|
|
|
}
|
|
|
|
if (!size || size < 0) {
|
|
|
|
throw new TypeError("HollowStructure expects a (positive) size");
|
|
|
|
}
|
|
|
|
|
|
|
|
// A mapping from offsets in the struct to name/type pairs
|
|
|
|
// (or nothing if no field starts at that offset).
|
|
|
|
this.offset_to_field_info = [];
|
|
|
|
|
|
|
|
// The name of the struct
|
|
|
|
this.name = name;
|
|
|
|
|
|
|
|
// The size of the struct, in bytes
|
|
|
|
this.size = size;
|
|
|
|
|
|
|
|
// The number of paddings inserted so far.
|
|
|
|
// Used to give distinct names to padding fields.
|
|
|
|
this._paddings = 0;
|
|
|
|
}
|
|
|
|
HollowStructure.prototype = {
|
|
|
|
/**
|
|
|
|
* Add a field at a given offset.
|
|
|
|
*
|
|
|
|
* @param {number} offset The offset at which to insert the field.
|
|
|
|
* @param {string} name The name of the field.
|
|
|
|
* @param {CType|Type} type The type of the field.
|
|
|
|
*/
|
|
|
|
add_field_at: function add_field_at(offset, name, type) {
|
|
|
|
if (offset === null) {
|
|
|
|
throw new TypeError("add_field_at requires a non-null offset");
|
|
|
|
}
|
|
|
|
if (!name) {
|
|
|
|
throw new TypeError("add_field_at requires a non-null name");
|
|
|
|
}
|
|
|
|
if (!type) {
|
|
|
|
throw new TypeError("add_field_at requires a non-null type");
|
|
|
|
}
|
|
|
|
if (type instanceof Type) {
|
|
|
|
type = type.implementation;
|
|
|
|
}
|
|
|
|
if (this.offset_to_field_info[offset]) {
|
|
|
|
throw new Error("HollowStructure " + this.name +
|
|
|
|
" already has a field at offset " + offset);
|
|
|
|
}
|
|
|
|
if (offset + type.size > this.size) {
|
|
|
|
throw new Error("HollowStructure " + this.name +
|
|
|
|
" cannot place a value of type " + type +
|
|
|
|
" at offset " + offset +
|
|
|
|
" without exceeding its size of " + this.size);
|
|
|
|
}
|
|
|
|
let field = {name: name, type:type};
|
|
|
|
this.offset_to_field_info[offset] = field;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a pseudo-field that will only serve as padding.
|
|
|
|
*
|
|
|
|
* @param {number} size The number of bytes in the field.
|
|
|
|
* @return {Object} An association field-name => field-type,
|
|
|
|
* as expected by |ctypes.StructType|.
|
|
|
|
*/
|
|
|
|
_makePaddingField: function makePaddingField(size) {
|
|
|
|
let field = ({});
|
|
|
|
field["padding_" + this._paddings] =
|
|
|
|
ctypes.ArrayType(ctypes.uint8_t, size);
|
|
|
|
this._paddings++;
|
|
|
|
return field;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert this |HollowStructure| into a |Type|.
|
|
|
|
*/
|
|
|
|
getType: function getType() {
|
|
|
|
// Contents of the structure, in the format expected
|
|
|
|
// by ctypes.StructType.
|
|
|
|
let struct = [];
|
|
|
|
|
|
|
|
let i = 0;
|
|
|
|
while (i < this.size) {
|
|
|
|
let currentField = this.offset_to_field_info[i];
|
|
|
|
if (!currentField) {
|
|
|
|
// No field was specified at this offset, we need to
|
|
|
|
// introduce some padding.
|
|
|
|
|
|
|
|
// Firstly, determine how many bytes of padding
|
|
|
|
let padding_length = 1;
|
|
|
|
while (i + padding_length < this.size
|
|
|
|
&& !this.offset_to_field_info[i + padding_length]) {
|
|
|
|
++padding_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then add the padding
|
|
|
|
struct.push(this._makePaddingField(padding_length));
|
|
|
|
|
|
|
|
// And proceed
|
|
|
|
i += padding_length;
|
|
|
|
} else {
|
|
|
|
// We have a field at this offset.
|
|
|
|
|
|
|
|
// Firstly, ensure that we do not have two overlapping fields
|
|
|
|
for (let j = 1; j < currentField.type.size; ++j) {
|
|
|
|
let candidateField = this.offset_to_field_info[i + j];
|
|
|
|
if (candidateField) {
|
|
|
|
throw new Error("Fields " + currentField.name +
|
|
|
|
" and " + candidateField.name +
|
|
|
|
" overlap at position " + (i + j));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then add the field
|
|
|
|
let field = ({});
|
|
|
|
field[currentField.name] = currentField.type;
|
|
|
|
struct.push(field);
|
|
|
|
|
|
|
|
// And proceed
|
|
|
|
i += currentField.type.size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let result = new Type(this.name, ctypes.StructType(this.name, struct));
|
|
|
|
if (result.implementation.size != this.size) {
|
|
|
|
throw new Error("Wrong size for type " + this.name +
|
|
|
|
": expected " + this.size +
|
|
|
|
", found " + result.implementation.size +
|
|
|
|
" (" + result.implementation.toSource() + ")");
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
exports.OS.Shared.HollowStructure = HollowStructure;
|
|
|
|
|
2012-06-10 16:44:50 -07:00
|
|
|
/**
|
|
|
|
* Declare a function through js-ctypes
|
|
|
|
*
|
|
|
|
* @param {ctypes.library} lib The ctypes library holding the function.
|
|
|
|
* @param {string} symbol The name of the function, as defined in the
|
|
|
|
* library.
|
|
|
|
* @param {ctypes.abi} abi The abi to use, or |null| for default.
|
|
|
|
* @param {Type} returnType The type of values returned by the function.
|
|
|
|
* @param {...Type} argTypes The type of arguments to the function.
|
|
|
|
*
|
|
|
|
* @return null if the function could not be defined (generally because
|
|
|
|
* it does not exist), or a JavaScript wrapper performing the call to C
|
|
|
|
* and any type conversion required.
|
|
|
|
*/// Note: Future versions will use a different implementation of this
|
|
|
|
// function on the main thread, osfile worker thread and regular worker
|
|
|
|
// thread
|
|
|
|
let declareFFI = function declareFFI(lib, symbol, abi,
|
|
|
|
returnType /*, argTypes ...*/) {
|
|
|
|
LOG("Attempting to declare FFI ", symbol);
|
|
|
|
// We guard agressively, to avoid any late surprise
|
|
|
|
if (typeof symbol != "string") {
|
|
|
|
throw new TypeError("declareFFI expects as first argument a string");
|
|
|
|
}
|
|
|
|
abi = abi || ctypes.default_abi;
|
|
|
|
if (Object.prototype.toString.call(abi) != "[object CABI]") {
|
|
|
|
// Note: This is the only known manner of checking whether an object
|
|
|
|
// is an abi.
|
|
|
|
throw new TypeError("declareFFI expects as second argument an abi or null");
|
|
|
|
}
|
|
|
|
let signature = [symbol, abi];
|
|
|
|
let argtypes = [];
|
|
|
|
for (let i = 3; i < arguments.length; ++i) {
|
|
|
|
let current = arguments[i];
|
2012-07-05 01:21:09 -07:00
|
|
|
if (!current) {
|
|
|
|
throw new TypeError("Missing type for argument " + ( i - 3 ) +
|
|
|
|
" of symbol " + symbol);
|
|
|
|
}
|
2012-06-10 16:44:50 -07:00
|
|
|
if (!current.implementation) {
|
|
|
|
throw new TypeError("Missing implementation for argument " + (i - 3)
|
|
|
|
+ " of symbol " + symbol
|
|
|
|
+ " ( " + current.name + " )" );
|
|
|
|
}
|
|
|
|
signature.push(current.implementation);
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
let fun = lib.declare.apply(lib, signature);
|
|
|
|
let result = function ffi(/*arguments*/) {
|
|
|
|
let result = fun.apply(fun, arguments);
|
|
|
|
return returnType.convert_from_c(result, symbol);
|
|
|
|
};
|
|
|
|
if (exports.OS.Shared.DEBUG) {
|
|
|
|
result.fun = fun; // Also return the raw FFI function.
|
|
|
|
}
|
|
|
|
LOG("Function", symbol, "declared");
|
|
|
|
return result;
|
|
|
|
} catch (x) {
|
|
|
|
// Note: Not being able to declare a function is normal.
|
|
|
|
// Some functions are OS (or OS version)-specific.
|
|
|
|
LOG("Could not declare function " + symbol, x);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
exports.OS.Shared.declareFFI = declareFFI;
|
2012-06-15 08:22:50 -07:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Specific tools that don't really fit anywhere.
|
|
|
|
*/
|
|
|
|
let _aux = {};
|
|
|
|
exports.OS.Shared._aux = _aux;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Utility function shared by implementations of |OS.File.open|:
|
|
|
|
* extract read/write/trunc/create/existing flags from a |mode|
|
|
|
|
* object.
|
|
|
|
*
|
|
|
|
* @param {*=} mode An object that may contain fields |read|,
|
|
|
|
* |write|, |truncate|, |create|, |existing|. These fields
|
|
|
|
* are interpreted only if true-ish.
|
|
|
|
* @return {{read:bool, write:bool, trunc:bool, create:bool,
|
|
|
|
* existing:bool}} an object recapitulating the options set
|
|
|
|
* by |mode|.
|
|
|
|
* @throws {TypeError} If |mode| contains other fields, or
|
|
|
|
* if it contains both |create| and |truncate|, or |create|
|
|
|
|
* and |existing|.
|
|
|
|
*/
|
|
|
|
_aux.normalizeOpenMode = function normalizeOpenMode(mode) {
|
|
|
|
let result = {
|
|
|
|
read: false,
|
|
|
|
write: false,
|
|
|
|
trunc: false,
|
|
|
|
create: false,
|
|
|
|
existing: false
|
|
|
|
};
|
|
|
|
for (let key in mode) {
|
|
|
|
if (!mode[key]) continue; // Only interpret true-ish keys
|
|
|
|
switch (key) {
|
|
|
|
case "read":
|
|
|
|
result.read = true;
|
|
|
|
break;
|
|
|
|
case "write":
|
|
|
|
result.write = true;
|
|
|
|
break;
|
|
|
|
case "truncate": // fallthrough
|
|
|
|
case "trunc":
|
|
|
|
result.trunc = true;
|
|
|
|
result.write = true;
|
|
|
|
break;
|
|
|
|
case "create":
|
|
|
|
result.create = true;
|
|
|
|
result.write = true;
|
|
|
|
break;
|
|
|
|
case "existing": // fallthrough
|
|
|
|
case "exist":
|
|
|
|
result.existing = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new TypeError("Mode " + key + " not understood");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Reject opposite modes
|
|
|
|
if (result.existing && result.create) {
|
|
|
|
throw new TypeError("Cannot specify both existing:true and create:true");
|
|
|
|
}
|
|
|
|
if (result.trunc && result.create) {
|
|
|
|
throw new TypeError("Cannot specify both trunc:true and create:true");
|
|
|
|
}
|
|
|
|
// Handle read/write
|
|
|
|
if (!result.write) {
|
|
|
|
result.read = true;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
};
|
2012-06-10 16:44:50 -07:00
|
|
|
})(this);
|
|
|
|
}
|