Bug 585175 - Don't automangle ctypes stdcall symbols for WINAPI. r=bsmedberg, a=blocker
authorDan Witte <dwitte@mozilla.com>
Tue, 10 Aug 2010 09:10:50 -0700
changeset 50469 1973d4086362bbb0633ea3bbc5583a1934dd67f2
parent 50468 7acc0fe02f45f647bcf14e4b4a007c26edcb23ad
child 50470 233ad8dd6f484bc329ae8fdce293b336284fbf1e
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg, blocker
bugs585175
milestone2.0b4pre
Bug 585175 - Don't automangle ctypes stdcall symbols for WINAPI. r=bsmedberg, a=blocker
js/src/ctypes/CTypes.cpp
js/src/ctypes/CTypes.h
toolkit/components/ctypes/tests/unit/test_jsctypes.js.in
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -865,17 +865,18 @@ InitTypeClasses(JSContext* cx, JSObject*
       !AttachProtos(cx, protos[SLOT_POINTERPROTO], protos) ||
       !AttachProtos(cx, protos[SLOT_ARRAYPROTO], protos) ||
       !AttachProtos(cx, protos[SLOT_STRUCTPROTO], protos) ||
       !AttachProtos(cx, protos[SLOT_FUNCTIONPROTO], protos))
      return false;
 
   // Attach objects representing ABI constants.
   if (!DefineABIConstant(cx, parent, "default_abi", ABI_DEFAULT) ||
-      !DefineABIConstant(cx, parent, "stdcall_abi", ABI_STDCALL))
+      !DefineABIConstant(cx, parent, "stdcall_abi", ABI_STDCALL) ||
+      !DefineABIConstant(cx, parent, "winapi_abi", ABI_WINAPI))
     return false;
 
   // Create objects representing the builtin types, and attach them to the
   // ctypes object. Each type object 't' has:
   //   * [[Class]] "CType"
   //   * __proto__ === ctypes.CType.prototype
   //   * A constructor which creates and returns a CData object, containing
   //     binary data of the given type.
@@ -2122,18 +2123,21 @@ BuildTypeName(JSContext* cx, JSObject* t
       typeObj = ArrayType::GetBaseType(cx, typeObj);
       prevGrouping = currentGrouping;
       continue;
     }
     case TYPE_function: {
       FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
 
       // Add in the calling convention, if it's not cdecl.
-      if (GetABICode(cx, fninfo->mABI) == ABI_STDCALL)
+      ABICode abi = GetABICode(cx, fninfo->mABI);
+      if (abi == ABI_STDCALL)
         PrependString(result, "__stdcall ");
+      else if (abi == ABI_WINAPI)
+        PrependString(result, "WINAPI ");
 
       // Wrap the entire expression so far with parens.
       PrependString(result, "(");
       AppendString(result, ")");
 
       // Argument list goes on the right.
       AppendString(result, "(");
       for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
@@ -2212,16 +2216,19 @@ BuildTypeSource(JSContext* cx,
 
     switch (GetABICode(cx, fninfo->mABI)) {
     case ABI_DEFAULT:
       AppendString(result, "ctypes.default_abi, ");
       break;
     case ABI_STDCALL:
       AppendString(result, "ctypes.stdcall_abi, ");
       break;
+    case ABI_WINAPI:
+      AppendString(result, "ctypes.winapi_abi, ");
+      break;
     case INVALID_ABI:
       JS_NOT_REACHED("invalid abi");
       break;
     }
 
     // Recursively build the source string describing the function return and
     // argument types.
     BuildTypeSource(cx, fninfo->mReturnType, true, result);
@@ -4505,16 +4512,17 @@ GetABI(JSContext* cx, jsval abiType, ffi
   // determine the ABI from the subset of those available on the
   // given platform. ABI_DEFAULT specifies the default
   // C calling convention (cdecl) on each platform.
   switch (abi) {
   case ABI_DEFAULT:
     *result = FFI_DEFAULT_ABI;
     return true;
   case ABI_STDCALL:
+  case ABI_WINAPI:
 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
     *result = FFI_STDCALL;
     return true;
 #endif
   case INVALID_ABI:
     break;
   }
   return false;
@@ -4641,17 +4649,18 @@ FunctionType::BuildSymbolName(JSContext*
                               JSString* name,
                               JSObject* typeObj,
                               AutoCString& result)
 {
   FunctionInfo* fninfo = GetFunctionInfo(cx, typeObj);
 
   switch (GetABICode(cx, fninfo->mABI)) {
   case ABI_DEFAULT:
-    // For cdecl functions, no mangling is necessary.
+  case ABI_WINAPI:
+    // For cdecl or WINAPI functions, no mangling is necessary.
     AppendString(result, name);
     break;
 
   case ABI_STDCALL: {
     // On WIN32, stdcall functions look like:
     //   _foo@40
     // where 'foo' is the function name, and '40' is the aligned size of the
     // arguments.
@@ -4834,32 +4843,40 @@ FunctionType::CreateInternal(JSContext* 
   if (!JS_SetReservedSlot(cx, typeObj, SLOT_FNINFO,
          PRIVATE_TO_JSVAL(fninfo.get())))
     return NULL;
   fninfo.forget();
 
   return typeObj;
 }
 
+// Construct a function pointer to a JS function (see CClosure::Create()).
+// Regular function pointers are constructed directly in
+// PointerType::ConstructData().
 JSBool
 FunctionType::ConstructData(JSContext* cx,
                             JSObject* typeObj,
                             JSObject* dataObj,
                             JSObject* fnObj,
                             JSObject* thisObj)
 {
   JS_ASSERT(CType::GetTypeCode(cx, typeObj) == TYPE_function);
 
   PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(cx, dataObj));
 
   FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
   if (fninfo->mIsVariadic) {
     JS_ReportError(cx, "Can't declare a variadic callback function");
     return JS_FALSE;
   }
+  if (GetABICode(cx, fninfo->mABI) == ABI_WINAPI) {
+    JS_ReportError(cx, "Can't declare a ctypes.winapi_abi callback function, "
+                   "use ctypes.stdcall_abi instead");
+    return JS_FALSE;
+  }
 
   JSObject* closureObj = CClosure::Create(cx, typeObj, fnObj, thisObj, data);
   if (!closureObj)
     return JS_FALSE;
   js::AutoObjectRooter root(cx, closureObj);
 
   // Set the closure object as the referent of the new CData object.
   if (!JS_SetReservedSlot(cx, dataObj, SLOT_REFERENT,
@@ -5146,16 +5163,17 @@ CClosure::Create(JSContext* cx,
   JSObject* result = JS_NewObject(cx, &sCClosureClass, NULL, NULL);
   if (!result)
     return NULL;
   js::AutoObjectRooter root(cx, result);
 
   // Get the FunctionInfo from the FunctionType.
   FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
   JS_ASSERT(!fninfo->mIsVariadic);
+  JS_ASSERT(GetABICode(cx, fninfo->mABI) != ABI_WINAPI);
 
   AutoPtr<ClosureInfo> cinfo(new ClosureInfo());
   if (!cinfo) {
     JS_ReportOutOfMemory(cx);
     return NULL;
   }
 
   // Get the prototype of the FunctionType object, of class CTypeProto,
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -231,21 +231,23 @@ enum ErrorNum {
 const JSErrorFormatString*
 GetErrorMessage(void* userRef, const char* locale, const uintN errorNumber);
 JSBool TypeError(JSContext* cx, const char* expected, jsval actual);
 
 /**
  * ABI constants that specify the calling convention to use.
  * ctypes.default_abi corresponds to the cdecl convention, and in almost all
  * cases is the correct choice. ctypes.stdcall_abi is provided for calling
- * functions in the Microsoft Win32 API.
+ * stdcall functions on Win32, and implies stdcall symbol name decoration;
+ * ctypes.winapi_abi is just stdcall but without decoration.
  */
 enum ABICode {
   ABI_DEFAULT,
   ABI_STDCALL,
+  ABI_WINAPI,
   INVALID_ABI
 };
 
 enum TypeCode {
   TYPE_void_t,
 #define DEFINE_TYPE(name, type, ffiType) TYPE_##name,
 #include "typedefs.h"
   TYPE_pointer,
--- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in
+++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in
@@ -661,16 +661,25 @@ function run_basic_abi_tests(library, t,
 
 #ifdef WIN32
 #ifndef HAVE_64BIT_OS
   function declare_fn_stdcall(fn_t, prefix) {
     return library.declare(prefix + name + "_stdcall", fn_t);
   }
   run_single_abi_tests(declare_fn_stdcall, ctypes.stdcall_abi, t,
     toprimitive, get_test, set_tests, sum_tests, sum_many_tests);
+
+  // Check that declaring a WINAPI function gets the right symbol name.
+  let libuser32 = ctypes.open("user32.dll");
+  let charupper = libuser32.declare("CharUpperA",
+                                    ctypes.winapi_abi,
+                                    ctypes.char.ptr,
+                                    ctypes.char.ptr);
+  let hello = ctypes.char.array()("hello!");
+  do_check_eq(charupper(hello).readString(), "HELLO!");
 #endif
 #endif
 
   // Check the alignment of the type, and its behavior in a struct,
   // against what C says.
   check_struct_stats(library, t);
 }
 
@@ -1837,16 +1846,19 @@ function run_FunctionType_tests() {
     ctypes.char.ptr.array().ptr).ptr.ptr.array(8).array();
   do_check_eq(f3_t.name, "char*(*(**[][8])())[]");
 
 #ifdef WIN32
 #ifndef HAVE_64BIT_OS
   f3_t = ctypes.FunctionType(ctypes.stdcall_abi,
     ctypes.char.ptr.array().ptr).ptr.ptr.array(8).array();
   do_check_eq(f3_t.name, "char*(*(__stdcall **[][8])())[]");
+  f3_t = ctypes.FunctionType(ctypes.winapi_abi,
+    ctypes.char.ptr.array().ptr).ptr.ptr.array(8).array();
+  do_check_eq(f3_t.name, "char*(*(WINAPI **[][8])())[]");
 #endif
 #endif
 
   let f4_t = ctypes.FunctionType(ctypes.default_abi,
     ctypes.char.ptr.array().ptr, [ ctypes.int32_t, fp_t ]);
   do_check_true(f4_t.argTypes.length == 2);
   do_check_true(f4_t.argTypes[0] === ctypes.int32_t);
   do_check_true(f4_t.argTypes[1] === fp_t);
@@ -2028,16 +2040,21 @@ function run_void_tests(library) {
   do_check_throws(function() {
     library.declare("test_void_t_cdecl", ctypes.default_abi, ctypes.void_t, ctypes.void_t);
   }, Error);
 
 #ifdef WIN32
 #ifndef HAVE_64BIT_OS
   test_void_t = library.declare("test_void_t_stdcall", ctypes.stdcall_abi, ctypes.void_t);
   do_check_eq(test_void_t(), undefined);
+
+  // Check that WINAPI symbol lookup for a regular stdcall function fails.
+  do_check_throws(function() {
+    let test_winapi_t = library.declare("test_void_t_stdcall", ctypes.winapi_abi, ctypes.void_t);
+  }, Error);
 #endif
 #endif
 }
 
 function run_string_tests(library) {
   let test_ansi_len = library.declare("test_ansi_len", ctypes.default_abi, ctypes.int32_t, ctypes.char.ptr);
   do_check_eq(test_ansi_len(""), 0);
   do_check_eq(test_ansi_len("hello world"), 11);
@@ -2181,16 +2198,24 @@ function run_function_tests(library)
 }
 
 function run_closure_tests(library)
 {
   run_single_closure_tests(library, ctypes.default_abi, "cdecl");
 #ifdef WIN32
 #ifndef HAVE_64BIT_OS
   run_single_closure_tests(library, ctypes.stdcall_abi, "stdcall");
+
+  // Check that attempting to construct a ctypes.winapi_abi closure throws.
+  function closure_fn()
+  {
+    return 1;
+  }
+  let fn_t = ctypes.FunctionType(ctypes.winapi_abi, ctypes.int32_t, []).ptr;
+  do_check_throws(function() { fn_t(closure_fn) }, Error);
 #endif
 #endif
 }
 
 function run_single_closure_tests(library, abi, suffix)
 {
   let b = 23;
 
@@ -2247,16 +2272,20 @@ function run_variadic_tests(library) {
   }, Error);
 
 #ifdef WIN32
 #ifndef HAVE_64BIT_OS
   do_check_throws(function() {
       ctypes.FunctionType(ctypes.stdcall_abi, ctypes.bool,
                           [ctypes.bool, "..."]);
   }, Error);
+  do_check_throws(function() {
+      ctypes.FunctionType(ctypes.winapi_abi, ctypes.bool,
+                          [ctypes.bool, "..."]);
+  }, Error);
 #endif
 #endif
 
   do_check_throws(function() {
     // No variadic closure callbacks allowed.
     sum_va_type(function(){});
   }, Error);