Bug 684017 - Implement ctypes.errno and ctypes.winLastError. r=jorendorff

This commit is contained in:
David Rajchenbach-Teller 2012-04-02 15:38:20 +02:00
parent ea3e5e0086
commit 4adff58654
8 changed files with 415 additions and 8 deletions

View File

@ -56,6 +56,12 @@
#include <sys/types.h> #include <sys/types.h>
#endif #endif
#if defined(XP_UNIX)
#include <errno.h>
#elif defined(XP_WIN)
#include <windows.h>
#endif
using namespace std; using namespace std;
namespace js { namespace js {
@ -86,6 +92,17 @@ namespace CType {
static JSBool ToString(JSContext* cx, unsigned argc, jsval* vp); static JSBool ToString(JSContext* cx, unsigned argc, jsval* vp);
static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp); static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp);
static JSBool HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp); static JSBool HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp);
/**
* Get the global "ctypes" object.
*
* |obj| must be a CType object.
*
* This function never returns NULL.
*/
static JSObject* GetGlobalCTypes(JSContext* cx, JSObject* obj);
} }
namespace PointerType { namespace PointerType {
@ -168,6 +185,14 @@ namespace CData {
static JSBool Address(JSContext* cx, unsigned argc, jsval* vp); static JSBool Address(JSContext* cx, unsigned argc, jsval* vp);
static JSBool ReadString(JSContext* cx, unsigned argc, jsval* vp); static JSBool ReadString(JSContext* cx, unsigned argc, jsval* vp);
static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp); static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp);
static JSBool ErrnoGetter(JSContext* cx, JSObject *obj, jsid idval,
jsval* vp);
#if defined(XP_WIN)
static JSBool LastErrorGetter(JSContext* cx, JSObject *obj, jsid idval,
jsval* vp);
#endif // defined(XP_WIN)
} }
// Int64Base provides functions common to Int64 and UInt64. // Int64Base provides functions common to Int64 and UInt64.
@ -442,6 +467,16 @@ static JSFunctionSpec sUInt64Functions[] = {
JS_FS_END JS_FS_END
}; };
static JSPropertySpec sModuleProps[] = {
{ "errno", 0, JSPROP_SHARED | JSPROP_PERMANENT,
CData::ErrnoGetter, NULL },
#if defined(XP_WIN)
{ "winLastError", 0, JSPROP_SHARED | JSPROP_PERMANENT,
CData::LastErrorGetter, NULL },
#endif // defined(XP_WIN)
{ 0, 0, 0, NULL, NULL }
};
static JSFunctionSpec sModuleFunctions[] = { static JSFunctionSpec sModuleFunctions[] = {
JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS), JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS),
JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS), JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS),
@ -729,9 +764,9 @@ static void
AttachProtos(JSObject* proto, JSObject** protos) AttachProtos(JSObject* proto, JSObject** protos)
{ {
// For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos' // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos'
// to the appropriate CTypeProtoSlot. (SLOT_UINT64PROTO is the last slot // to the appropriate CTypeProtoSlot. (SLOT_CTYPES is the last slot
// of [[Class]] "CTypeProto" that we fill in this automated manner.) // of [[Class]] "CTypeProto" that we fill in this automated manner.)
for (uint32_t i = 0; i <= SLOT_UINT64PROTO; ++i) for (uint32_t i = 0; i <= SLOT_CTYPES; ++i)
JS_SetReservedSlot(proto, i, OBJECT_TO_JSVAL(protos[i])); JS_SetReservedSlot(proto, i, OBJECT_TO_JSVAL(protos[i]));
} }
@ -847,6 +882,10 @@ InitTypeClasses(JSContext* cx, JSObject* parent)
if (!protos[SLOT_UINT64PROTO]) if (!protos[SLOT_UINT64PROTO])
return false; return false;
// Finally, store a pointer to the global ctypes object.
// Note that there is no other reliable manner of locating this object.
protos[SLOT_CTYPES] = parent;
// Attach the prototypes just created to each of ctypes.CType.prototype, // Attach the prototypes just created to each of ctypes.CType.prototype,
// and the special type constructors, so we can access them when constructing // and the special type constructors, so we can access them when constructing
// instances of those types. // instances of those types.
@ -942,8 +981,9 @@ JS_InitCTypesClass(JSContext* cx, JSObject* global)
if (!InitTypeClasses(cx, ctypes)) if (!InitTypeClasses(cx, ctypes))
return false; return false;
// attach API functions // attach API functions and properties
if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions)) if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions) ||
!JS_DefineProperties(cx, ctypes, sModuleProps))
return false; return false;
// Seal the ctypes object, to prevent modification. // Seal the ctypes object, to prevent modification.
@ -3226,6 +3266,23 @@ CType::HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp)
return JS_TRUE; return JS_TRUE;
} }
static JSObject*
CType::GetGlobalCTypes(JSContext* cx, JSObject* obj)
{
JS_ASSERT(CType::IsCType(obj));
JSObject *objTypeProto = JS_GetPrototype(obj);
if (!objTypeProto) {
}
JS_ASSERT(objTypeProto);
JS_ASSERT(CType::IsCTypeProto(objTypeProto));
jsval valCTypes = JS_GetReservedSlot(objTypeProto, SLOT_CTYPES);
JS_ASSERT(!JSVAL_IS_PRIMITIVE(valCTypes));
return JSVAL_TO_OBJECT(valCTypes);
}
/******************************************************************************* /*******************************************************************************
** PointerType implementation ** PointerType implementation
*******************************************************************************/ *******************************************************************************/
@ -5149,13 +5206,45 @@ FunctionType::Call(JSContext* cx,
uintptr_t fn = *reinterpret_cast<uintptr_t*>(CData::GetData(obj)); uintptr_t fn = *reinterpret_cast<uintptr_t*>(CData::GetData(obj));
#if defined(XP_WIN)
int32_t lastErrorStatus; // The status as defined by |GetLastError|
int32_t savedLastError = GetLastError();
SetLastError(0);
#endif //defined(XP_WIN)
int errnoStatus; // The status as defined by |errno|
int savedErrno = errno;
errno = 0;
// suspend the request before we call into the function, since the call // suspend the request before we call into the function, since the call
// may block or otherwise take a long time to return. // may block or otherwise take a long time to return.
{ {
JSAutoSuspendRequest suspend(cx); JSAutoSuspendRequest suspend(cx);
ffi_call(&fninfo->mCIF, FFI_FN(fn), returnValue.mData, ffi_call(&fninfo->mCIF, FFI_FN(fn), returnValue.mData,
reinterpret_cast<void**>(values.begin())); reinterpret_cast<void**>(values.begin()));
// Save error value.
// We need to save it before leaving the scope of |suspend| as destructing
// |suspend| has the side-effect of clearing |GetLastError|
// (see bug 684017).
errnoStatus = errno;
#if defined(XP_WIN)
lastErrorStatus = GetLastError();
#endif // defined(XP_WIN)
} }
#if defined(XP_WIN)
SetLastError(savedLastError);
#endif // defined(XP_WIN)
errno = savedErrno;
// Store the error value for later consultation with |ctypes.getStatus|
JSObject *objCTypes = CType::GetGlobalCTypes(cx, typeObj);
JS_SetReservedSlot(objCTypes, SLOT_ERRNO, INT_TO_JSVAL(errnoStatus));
#if defined(XP_WIN)
JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, INT_TO_JSVAL(lastErrorStatus));
#endif // defined(XP_WIN)
// Small integer types get returned as a word-sized ffi_arg. Coerce it back // Small integer types get returned as a word-sized ffi_arg. Coerce it back
// into the correct size for ConvertToJS. // into the correct size for ConvertToJS.
@ -5976,6 +6065,33 @@ CData::ToSource(JSContext* cx, unsigned argc, jsval* vp)
return JS_TRUE; return JS_TRUE;
} }
JSBool
CData::ErrnoGetter(JSContext* cx, JSObject* obj, jsid, jsval* vp)
{
if (!IsCTypesGlobal(obj)) {
JS_ReportError(cx, "this is not not global object ctypes");
return JS_FALSE;
}
*vp = JS_GetReservedSlot(obj, SLOT_ERRNO);
return JS_TRUE;
}
#if defined(XP_WIN)
JSBool
CData::LastErrorGetter(JSContext* cx, JSObject* obj, jsid, jsval* vp)
{
if (!IsCTypesGlobal(obj)) {
JS_ReportError(cx, "not global object ctypes");
return JS_FALSE;
}
*vp = JS_GetReservedSlot(obj, SLOT_LASTERROR);
return JS_TRUE;
}
#endif // defined(XP_WIN)
/******************************************************************************* /*******************************************************************************
** Int64 and UInt64 implementation ** Int64 and UInt64 implementation
*******************************************************************************/ *******************************************************************************/

View File

@ -369,6 +369,8 @@ JSBool ExplicitConvert(JSContext* cx, jsval val, JSObject* targetType,
enum CTypesGlobalSlot { enum CTypesGlobalSlot {
SLOT_CALLBACKS = 0, // pointer to JSCTypesCallbacks struct SLOT_CALLBACKS = 0, // pointer to JSCTypesCallbacks struct
SLOT_ERRNO = 1, // jsval for latest |errno|
SLOT_LASTERROR = 2, // jsval for latest |GetLastError|, used only with Windows
CTYPESGLOBAL_SLOTS CTYPESGLOBAL_SLOTS
}; };
@ -389,8 +391,9 @@ enum CTypeProtoSlot {
SLOT_FUNCTIONDATAPROTO = 8, // common ancestor of all CData objects of FunctionType SLOT_FUNCTIONDATAPROTO = 8, // common ancestor of all CData objects of FunctionType
SLOT_INT64PROTO = 9, // ctypes.Int64.prototype object SLOT_INT64PROTO = 9, // ctypes.Int64.prototype object
SLOT_UINT64PROTO = 10, // ctypes.UInt64.prototype object SLOT_UINT64PROTO = 10, // ctypes.UInt64.prototype object
SLOT_OURDATAPROTO = 11, // the data prototype corresponding to this object SLOT_CTYPES = 11, // ctypes object
SLOT_CLOSURECX = 12, // JSContext for use with FunctionType closures SLOT_OURDATAPROTO = 12, // the data prototype corresponding to this object
SLOT_CLOSURECX = 13, // JSContext for use with FunctionType closures
CTYPEPROTO_SLOTS CTYPEPROTO_SLOTS
}; };

View File

@ -21,6 +21,7 @@
# Contributor(s): # Contributor(s):
# Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com> # Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
# Dan Witte <dwitte@mozilla.com> # Dan Witte <dwitte@mozilla.com>
# David Rajchenbach-Teller <dteller@mozilla.com>
# #
# Alternatively, the contents of this file may be used under the terms of # Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or # either the GNU General Public License Version 2 or later (the "GPL"), or
@ -50,7 +51,9 @@ SHORT_LIBNAME = jscttest
FORCE_SHARED_LIB = 1 FORCE_SHARED_LIB = 1
NO_DIST_INSTALL = 1 NO_DIST_INSTALL = 1
CPPSRCS = jsctypes-test.cpp CPPSRCS = jsctypes-test.cpp \
jsctypes-test-errno.cpp \
$(NULL)
LOCAL_INCLUDES = \ LOCAL_INCLUDES = \
-I$(topsrcdir)/js/src/ctypes \ -I$(topsrcdir)/js/src/ctypes \

View File

@ -0,0 +1,77 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is js-ctypes.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation <http://www.mozilla.org/>.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Fredrik Larsson <nossralf@gmail.com>
* Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
* Dan Witte <dwitte@mozilla.com>
* David Rajchenbach-Teller <dteller@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <stdio.h>
#include <errno.h>
#if defined(XP_WIN)
#include <windows.h>
#endif // defined(XP_WIN)
#include "jsctypes-test-errno.h"
#define FAIL \
{ \
fprintf(stderr, "Assertion failed at line %i\n", __LINE__); \
(*(int*)NULL)++; \
}
void set_errno(int status)
{
errno = status;
}
int get_errno()
{
return errno;
}
#if defined(XP_WIN)
void set_last_error(int status)
{
SetLastError((int)status);
}
int get_last_error()
{
return (int)GetLastError();
}
#endif // defined(XP_WIN)

View File

@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is js-ctypes.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation <http://www.mozilla.org/>.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Fredrik Larsson <nossralf@gmail.com>
* Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
* Dan Witte <dwitte@mozilla.com>
* David Rajchenbach-Teller <dteller@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nscore.h"
#include "prtypes.h"
#include "jsapi.h"
#define EXPORT_CDECL(type) NS_EXPORT type
#define EXPORT_STDCALL(type) NS_EXPORT type NS_STDCALL
NS_EXTERN_C
{
EXPORT_CDECL(void) set_errno(int status);
EXPORT_CDECL(int) get_errno();
#if defined(XP_WIN)
EXPORT_CDECL(void) set_last_error(int status);
EXPORT_CDECL(int) get_last_error();
#endif // defined(XP_WIN)
}

View File

@ -0,0 +1,79 @@
try {
// We might be running without privileges, in which case it's up to the
// harness to give us the 'ctypes' object.
Components.utils.import("resource://gre/modules/ctypes.jsm");
} catch(e) {
}
function open_ctypes_test_lib()
{
return ctypes.open(do_get_file(ctypes.libraryName("jsctypes-test")).path);
}
/**
* Simple wrapper for tests that require cleanup.
*/
function ResourceTester(start, stop) {
this._start = start;
this._stop = stop;
}
ResourceTester.prototype = {
launch: function(size, test, args) {
Components.utils.forceGC();
this._start(size);
try {
test(size, args);
} catch (x) {
this._stop();
throw x;
}
Components.utils.forceGC();
this._stop();
}
};
function structural_check_eq(a, b) {
// 1. If objects can be "toSource()-ed", use this.
let result;
let finished = false;
let asource, bsource;
try {
asource = a.toSource();
bsource = b.toSource();
finished = true;
} catch (x) {
}
if (finished) {
return do_check_eq(asource, bsource);
}
// 2. Otherwise, perform slower comparison
try {
structural_check_eq_aux(a, b);
result = true;
} catch (x) {
dump(x);
result = false;
}
do_check_true(result);
}
function structural_check_eq_aux(a, b) {
let ak;
try {
ak = Object.keys(a);
} catch (x) {
if (a != b) {
throw new Error("Distinct values "+a, b);
}
return;
}
ak.forEach(
function(k) {
let av = a[k];
let bv = b[k];
structural_check_eq_aux(av, bv);
}
);
}

View File

@ -0,0 +1,69 @@
Components.utils.import("resource://gre/modules/ctypes.jsm");
// Scope used to relaunch the tests with |ctypes| opened in a limited scope.
let scope = {};
let ctypes = ctypes;
function run_test()
{
// Launch the test with regular loading of ctypes.jsm
main_test();
// Relaunch the test with exotic loading of ctypes.jsm
Components.utils.unload("resource://gre/modules/ctypes.jsm");
Components.utils.import("resource://gre/modules/ctypes.jsm", scope);
ctypes = scope.ctypes;
main_test();
}
function main_test()
{
"use strict";
let library = open_ctypes_test_lib();
let set_errno = library.declare("set_errno", ctypes.default_abi,
ctypes.void_t,
ctypes.int);
let get_errno = library.declare("get_errno", ctypes.default_abi,
ctypes.int);
for (let i = 50; i >= 0; --i) {
set_errno(i);
let status = ctypes.errno;
do_check_eq(status, i);
status = get_errno();
do_check_eq(status, 0);
status = ctypes.errno;
do_check_eq(status, 0);
}
let set_last_error, get_last_error;
try { // The following test is Windows-specific
set_last_error = library.declare("set_last_error", ctypes.default_abi,
ctypes.void_t,
ctypes.int);
get_last_error = library.declare("get_last_error", ctypes.default_abi,
ctypes.int);
} catch (x) {
do_check_eq(ctypes.winLastError, undefined);
}
if (set_last_error) {
do_check_neq(ctypes.winLastError, undefined);
for (let i = 0; i < 50; ++i) {
set_last_error(i);
let status = ctypes.winLastError;
do_check_eq(status, i);
status = get_last_error();
do_check_eq(status, 0);
status = ctypes.winLastError;
do_check_eq(status, 0);
}
}
library.close();
}

View File

@ -1,7 +1,9 @@
[DEFAULT] [DEFAULT]
head = head = head.js
tail = tail =
[test_errno.js]
[test_jsctypes.js] [test_jsctypes.js]
# Bug 676989: test fails consistently on Android # Bug 676989: test fails consistently on Android
fail-if = os == "android" fail-if = os == "android"