Bug 552533 - Support thiscall Win32 ABI for js-ctypes. Original patch is dwitte. r=jorendorff
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Thu, 17 Mar 2016 12:00:35 +0900
changeset 312895 3a4835fe862f1651322a0a115907e821b8b4098f
parent 312894 fdd1faf35dbfd32211e0f47e8e8d13ecc0e9856f
child 312896 eb4fb4fef6d483485d520e01d25ad59debfcfb48
push id9480
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 17:12:58 +0000
treeherdermozilla-aurora@0d6a91c76a9e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs552533
milestone48.0a1
Bug 552533 - Support thiscall Win32 ABI for js-ctypes. Original patch is dwitte. r=jorendorff MozReview-Commit-ID: 5gatJC2xRek
js/src/ctypes/CTypes.cpp
js/src/ctypes/CTypes.h
toolkit/components/ctypes/tests/jsctypes-test.cpp
toolkit/components/ctypes/tests/jsctypes-test.h
toolkit/components/ctypes/tests/unit/test_jsctypes.js
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -2268,16 +2268,17 @@ InitTypeClasses(JSContext* cx, HandleObj
 
   RootedObject ABIProto(cx, InitABIClass(cx));
   if (!ABIProto)
     return false;
 
   // Attach objects representing ABI constants.
   if (!DefineABIConstant(cx, ctypesObj, "default_abi", ABI_DEFAULT, ABIProto) ||
       !DefineABIConstant(cx, ctypesObj, "stdcall_abi", ABI_STDCALL, ABIProto) ||
+      !DefineABIConstant(cx, ctypesObj, "thiscall_abi", ABI_THISCALL, ABIProto) ||
       !DefineABIConstant(cx, ctypesObj, "winapi_abi", ABI_WINAPI, ABIProto))
     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
@@ -3912,16 +3913,18 @@ BuildTypeName(JSContext* cx, JSObject* t
       // Add in the calling convention, if it's not cdecl.
       // There's no trailing or leading space needed here, as none of the
       // modifiers can produce a string beginning with an identifier ---
       // except for TYPE_function itself, which is fine because functions
       // can't return functions.
       ABICode abi = GetABICode(fninfo->mABI);
       if (abi == ABI_STDCALL)
         PrependString(result, "__stdcall");
+      else if (abi == ABI_THISCALL)
+        PrependString(result, "__thiscall");
       else if (abi == ABI_WINAPI)
         PrependString(result, "WINAPI");
 
       // Function application binds more tightly than dereferencing, so
       // wrap pointer types in parens. Functions can't return functions
       // (only pointers to them), and arrays can't hold functions
       // (similarly), so we don't need to address those cases.
       if (prevGrouping == TYPE_pointer) {
@@ -4017,16 +4020,19 @@ BuildTypeSource(JSContext* cx,
 
     switch (GetABICode(fninfo->mABI)) {
     case ABI_DEFAULT:
       AppendString(result, "ctypes.default_abi, ");
       break;
     case ABI_STDCALL:
       AppendString(result, "ctypes.stdcall_abi, ");
       break;
+    case ABI_THISCALL:
+      AppendString(result, "ctypes.thiscall_abi, ");
+      break;
     case ABI_WINAPI:
       AppendString(result, "ctypes.winapi_abi, ");
       break;
     case INVALID_ABI:
       MOZ_CRASH("invalid abi");
     }
 
     // Recursively build the source string describing the function return and
@@ -5007,16 +5013,19 @@ ABI::ToSource(JSContext* cx, unsigned ar
   JSString* result;
   switch (GetABICode(obj)) {
     case ABI_DEFAULT:
       result = JS_NewStringCopyZ(cx, "ctypes.default_abi");
       break;
     case ABI_STDCALL:
       result = JS_NewStringCopyZ(cx, "ctypes.stdcall_abi");
       break;
+    case ABI_THISCALL:
+      result = JS_NewStringCopyZ(cx, "ctypes.thiscall_abi");
+      break;
     case ABI_WINAPI:
       result = JS_NewStringCopyZ(cx, "ctypes.winapi_abi");
       break;
     default:
       JS_ReportError(cx, "not a valid ABICode");
       return false;
   }
   if (!result)
@@ -6542,16 +6551,26 @@ GetABI(JSContext* cx, Value 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_THISCALL:
+#if defined(_WIN64)
+    *result = FFI_WIN64;
+    return true;
+#elif defined(_WIN32)
+    *result = FFI_THISCALL;
+    return true;
+#else
+    break;
+#endif
   case ABI_STDCALL:
   case ABI_WINAPI:
 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
     *result = FFI_STDCALL;
     return true;
 #elif (defined(_WIN64))
     // We'd like the same code to work across Win32 and Win64, so stdcall_api
     // and winapi_abi become aliases to the lone Win64 ABI.
@@ -6687,16 +6706,17 @@ void
 FunctionType::BuildSymbolName(JSString* name,
                               JSObject* typeObj,
                               AutoCString& result)
 {
   FunctionInfo* fninfo = GetFunctionInfo(typeObj);
 
   switch (GetABICode(fninfo->mABI)) {
   case ABI_DEFAULT:
+  case ABI_THISCALL:
   case ABI_WINAPI:
     // For cdecl or WINAPI functions, no mangling is necessary.
     AppendString(result, name);
     break;
 
   case ABI_STDCALL: {
 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
     // On WIN32, stdcall functions look like:
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -206,16 +206,17 @@ enum ErrorNum {
  * ctypes.default_abi corresponds to the cdecl convention, and in almost all
  * cases is the correct choice. ctypes.stdcall_abi is provided for calling
  * 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_THISCALL,
   ABI_WINAPI,
   INVALID_ABI
 };
 
 enum TypeCode {
   TYPE_void_t,
 #define DEFINE_TYPE(name, type, ffiType) TYPE_##name,
   CTYPES_FOR_EACH_TYPE(DEFINE_TYPE)
--- a/toolkit/components/ctypes/tests/jsctypes-test.cpp
+++ b/toolkit/components/ctypes/tests/jsctypes-test.cpp
@@ -379,8 +379,20 @@ test_vector_add_va_cdecl(uint8_t num_vec
     for (uint8_t j = 0; j < vec_len; ++j)
       result[j] += vec[j];
   }
   va_end(list);
   return result;
 }
 
 myRECT data_rect = { -1, -2, 3, 4 };
+
+TestClass::TestClass(int32_t a)
+{
+  mInt =a;
+}
+
+int32_t
+TestClass::Add(int32_t aOther)
+{
+  mInt += aOther;
+  return mInt;
+}
--- a/toolkit/components/ctypes/tests/jsctypes-test.h
+++ b/toolkit/components/ctypes/tests/jsctypes-test.h
@@ -180,9 +180,18 @@ MOZ_BEGIN_EXTERN_C
   EXPORT_CDECL(int32_t*) test_vector_add_va_cdecl(uint8_t num_vecs,
                                                   uint8_t vec_len,
                                                   int32_t* result, ...);
 
   MOZ_EXPORT extern myRECT data_rect;
 
 MOZ_END_EXTERN_C
 
+class MOZ_EXPORT TestClass final {
+public:
+  explicit TestClass(int32_t);
+  int32_t Add(int32_t);
+
+private:
+  int32_t mInt;
+};
+
 #endif
--- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js
+++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js
@@ -177,16 +177,17 @@ function run_test()
 
   run_string_tests(library);
   run_readstring_tests(library);
   run_struct_tests(library);
   run_function_tests(library);
   run_closure_tests(library);
   run_variadic_tests(library);
   run_static_data_tests(library);
+  run_cpp_class_tests(library);
 
   // test library.close
   let test_void_t = library.declare("test_void_t_cdecl", ctypes.default_abi, ctypes.void_t);
   library.close();
   do_check_throws(function() { test_void_t(); }, Error);
   do_check_throws(function() {
     library.declare("test_void_t_cdecl", ctypes.default_abi, ctypes.void_t);
   }, Error);
@@ -2786,16 +2787,52 @@ function run_static_data_tests(library)
   let data_rect_2 = library.declare("data_rect", rect_t);
   do_check_eq(data_rect_2.top, 9);
   do_check_eq(data_rect_2.left, 8);
   do_check_eq(data_rect_2.bottom, -11);
   do_check_eq(data_rect_2.right, -12);
   do_check_eq(ptrValue(data_rect.address()), ptrValue(data_rect_2.address()));
 }
 
+function run_cpp_class_tests(library)
+{
+  // try the gcc mangling, unless we're using MSVC.
+  let OS = get_os();
+  let ctor_symbol;
+  let add_symbol;
+  let abi;
+  if (OS == "WINNT") {
+    // for compatibility for Win32 vs Win64
+    abi = ctypes.thiscall_abi;
+    if (ctypes.size_t.size == 8) {
+      ctor_symbol = '??0TestClass@@QEAA@H@Z';
+      add_symbol = '?Add@TestClass@@QEAAHH@Z';
+    } else {
+      ctor_symbol = '??0TestClass@@QAE@H@Z';
+      add_symbol = '?Add@TestClass@@QAEHH@Z';
+    }
+  } else {
+    abi = ctypes.default_abi;
+    ctor_symbol = "_ZN9TestClassC1Ei";
+    add_symbol = "_ZN9TestClass3AddEi";
+  }
+
+  let test_class_ctor = library.declare(ctor_symbol, abi, ctypes.void_t,
+                                        ctypes.int32_t.ptr, ctypes.int32_t);
+  let i = ctypes.int32_t();
+  test_class_ctor(i.address(), 8);
+  do_check_eq(i.value, 8);
+
+  let test_class_add = library.declare(add_symbol, abi, ctypes.int32_t,
+                                       ctypes.int32_t.ptr, ctypes.int32_t);
+  let j = test_class_add(i.address(), 5);
+  do_check_eq(j, 13);
+  do_check_eq(i.value, 13);
+}
+
 // bug 522360 - try loading system library without full path
 function run_load_system_library()
 {
   let syslib;
   let OS = get_os();
   if (OS == "WINNT") {
     syslib = ctypes.open("user32.dll");
   } else if (OS == "Darwin") {