text: /*
   * Copyright (c) 2009, Wayne Meissner
   * Copyright (C) 2009 Andrea Fazzi <andrea.fazzi@alcacoop.it>
   * Copyright (c) 2008-2013, Ruby FFI project contributors
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
   *     * Redistributions of source code must retain the above copyright
   *       notice, this list of conditions and the following disclaimer.
   *     * Redistributions in binary form must reproduce the above copyright
   *       notice, this list of conditions and the following disclaimer in the
   *       documentation and/or other materials provided with the distribution.
   *     * Neither the name of the Ruby FFI project nor the
   *       names of its contributors may be used to endorse or promote products
   *       derived from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
   * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  #ifndef _MSC_VER
  # include <sys/param.h>
  #endif
  #include <sys/types.h>
  #include <stdio.h>
  
  #ifndef _MSC_VER
  # include <stdint.h>
  # include <stdbool.h>
  #else
  # include "win32/stdbool.h"
  #endif
  
  #include <errno.h>
  #include <ruby.h>
  
  #include <ffi.h>
  #include "rbffi.h"
  #include "compat.h"
  
  #include "AbstractMemory.h"
  #include "Types.h"
  #include "Type.h"
  #include "StructByValue.h"
  #include "Function.h"
  
  static VALUE fntype_allocate(VALUE klass);
  static VALUE fntype_initialize(int argc, VALUE* argv, VALUE self);
  static void fntype_mark(FunctionType*);
  static void fntype_free(FunctionType *);
  
  VALUE rbffi_FunctionTypeClass = Qnil;
  
  static VALUE
  fntype_allocate(VALUE klass)
  {
      FunctionType* fnInfo;
      VALUE obj = Data_Make_Struct(klass, FunctionType, fntype_mark, fntype_free, fnInfo);
  
      fnInfo->type.ffiType = &ffi_type_pointer;
      fnInfo->type.nativeType = NATIVE_FUNCTION;
      fnInfo->rbReturnType = Qnil;
      fnInfo->rbParameterTypes = Qnil;
      fnInfo->rbEnums = Qnil;
      fnInfo->invoke = rbffi_CallFunction;
      fnInfo->closurePool = NULL;
  
      return obj;
  }
  
  static void
  fntype_mark(FunctionType* fnInfo)
  {
      rb_gc_mark(fnInfo->rbReturnType);
      rb_gc_mark(fnInfo->rbParameterTypes);
      rb_gc_mark(fnInfo->rbEnums);
      if (fnInfo->callbackCount > 0 && fnInfo->callbackParameters != NULL) {
          rb_gc_mark_locations(&fnInfo->callbackParameters[0], &fnInfo->callbackParameters[fnInfo->callbackCount]);
      }
  }
  
  static void
  fntype_free(FunctionType* fnInfo)
  {
      xfree(fnInfo->parameterTypes);
      xfree(fnInfo->ffiParameterTypes);
      xfree(fnInfo->nativeParameterTypes);
      xfree(fnInfo->callbackParameters);
      if (fnInfo->closurePool != NULL) {
          rbffi_ClosurePool_Free(fnInfo->closurePool);
      }
      xfree(fnInfo);
  }
  
  /*
   * call-seq: initialize(return_type, param_types, options={})
   * @param [Type, Symbol] return_type return type for the function
   * @param [Array<Type, Symbol>] param_types array of parameters types
   * @param [Hash] options
   * @option options [Boolean] :blocking set to true if the C function is a blocking call
   * @option options [Symbol] :convention calling convention see {FFI::Library#calling_convention}
   * @option options [FFI::Enums] :enums
   * @return [self]
   * A new FunctionType instance.
   */
  static VALUE
  fntype_initialize(int argc, VALUE* argv, VALUE self)
  {
      FunctionType *fnInfo;
      ffi_status status;
      VALUE rbReturnType = Qnil, rbParamTypes = Qnil, rbOptions = Qnil;
      VALUE rbEnums = Qnil, rbConvention = Qnil, rbBlocking = Qnil;
  #if defined(X86_WIN32)
      VALUE rbConventionStr;
  #endif
      int i, nargs;
  
      nargs = rb_scan_args(argc, argv, "21", &rbReturnType, &rbParamTypes, &rbOptions);
      if (nargs >= 3 && rbOptions != Qnil) {
          rbConvention = rb_hash_aref(rbOptions, ID2SYM(rb_intern("convention")));
          rbEnums = rb_hash_aref(rbOptions, ID2SYM(rb_intern("enums")));
          rbBlocking = rb_hash_aref(rbOptions, ID2SYM(rb_intern("blocking")));
      }
  
      Check_Type(rbParamTypes, T_ARRAY);
  
      Data_Get_Struct(self, FunctionType, fnInfo);
      fnInfo->parameterCount = (int) RARRAY_LEN(rbParamTypes);
      fnInfo->parameterTypes = xcalloc(fnInfo->parameterCount, sizeof(*fnInfo->parameterTypes));
      fnInfo->ffiParameterTypes = xcalloc(fnInfo->parameterCount, sizeof(ffi_type *));
      fnInfo->nativeParameterTypes = xcalloc(fnInfo->parameterCount, sizeof(*fnInfo->nativeParameterTypes));
      fnInfo->rbParameterTypes = rb_ary_new2(fnInfo->parameterCount);
      fnInfo->rbEnums = rbEnums;
      fnInfo->blocking = RTEST(rbBlocking);
      fnInfo->hasStruct = false;
  
      for (i = 0; i < fnInfo->parameterCount; ++i) {
          VALUE entry = rb_ary_entry(rbParamTypes, i);
          VALUE type = rbffi_Type_Lookup(entry);
  
          if (!RTEST(type)) {
              VALUE typeName = rb_funcall2(entry, rb_intern("inspect"), 0, NULL);
              rb_raise(rb_eTypeError, "Invalid parameter type (%s)", RSTRING_PTR(typeName));
          }
  
          if (rb_obj_is_kind_of(type, rbffi_FunctionTypeClass)) {
              REALLOC_N(fnInfo->callbackParameters, VALUE, fnInfo->callbackCount + 1);
              fnInfo->callbackParameters[fnInfo->callbackCount++] = type;
          }
  
          if (rb_obj_is_kind_of(type, rbffi_StructByValueClass)) {
              fnInfo->hasStruct = true;
          }
  
          rb_ary_push(fnInfo->rbParameterTypes, type);
          Data_Get_Struct(type, Type, fnInfo->parameterTypes[i]);
          fnInfo->ffiParameterTypes[i] = fnInfo->parameterTypes[i]->ffiType;
          fnInfo->nativeParameterTypes[i] = fnInfo->parameterTypes[i]->nativeType;
      }
  
      fnInfo->rbReturnType = rbffi_Type_Lookup(rbReturnType);
      if (!RTEST(fnInfo->rbReturnType)) {
          VALUE typeName = rb_funcall2(rbReturnType, rb_intern("inspect"), 0, NULL);
          rb_raise(rb_eTypeError, "Invalid return type (%s)", RSTRING_PTR(typeName));
      }
  
      if (rb_obj_is_kind_of(fnInfo->rbReturnType, rbffi_StructByValueClass)) {
          fnInfo->hasStruct = true;
      }
  
      Data_Get_Struct(fnInfo->rbReturnType, Type, fnInfo->returnType);
      fnInfo->ffiReturnType = fnInfo->returnType->ffiType;
  
  #if defined(X86_WIN32)
      rbConventionStr = (rbConvention != Qnil) ? rb_funcall2(rbConvention, rb_intern("to_s"), 0, NULL) : Qnil;
      fnInfo->abi = (rbConventionStr != Qnil && strcmp(StringValueCStr(rbConventionStr), "stdcall") == 0)
              ? FFI_STDCALL : FFI_DEFAULT_ABI;
  #else
      fnInfo->abi = FFI_DEFAULT_ABI;
  #endif
  
      status = ffi_prep_cif(&fnInfo->ffi_cif, fnInfo->abi, fnInfo->parameterCount,
              fnInfo->ffiReturnType, fnInfo->ffiParameterTypes);
      switch (status) {
          case FFI_BAD_ABI:
              rb_raise(rb_eArgError, "Invalid ABI specified");
          case FFI_BAD_TYPEDEF:
              rb_raise(rb_eArgError, "Invalid argument type specified");
          case FFI_OK:
              break;
          default:
              rb_raise(rb_eArgError, "Unknown FFI error");
      }
  
      fnInfo->invoke = rbffi_GetInvoker(fnInfo);
  
      return self;
  }
  
  /*
   * call-seq: result_type
   * @return [Type]
   * Get the return type of the function type
   */
  static VALUE
  fntype_result_type(VALUE self)
  {
      FunctionType* ft;
  
      Data_Get_Struct(self, FunctionType, ft);
  
      return ft->rbReturnType;
  }
  
  /*
   * call-seq: param_types
   * @return [Array<Type>]
   * Get parameters types.
   */
  static VALUE
  fntype_param_types(VALUE self)
  {
      FunctionType* ft;
  
      Data_Get_Struct(self, FunctionType, ft);
  
      return rb_ary_dup(ft->rbParameterTypes);
  }
  
  void
  rbffi_FunctionInfo_Init(VALUE moduleFFI)
  {
      VALUE ffi_Type;
  
      ffi_Type = rbffi_TypeClass;
  
      /*
       * Document-class: FFI::FunctionType < FFI::Type
       */
      rbffi_FunctionTypeClass = rb_define_class_under(moduleFFI, "FunctionType",ffi_Type);
      rb_global_variable(&rbffi_FunctionTypeClass);
      /*
       * Document-const: FFI::CallbackInfo = FFI::FunctionType
       */
      rb_define_const(moduleFFI, "CallbackInfo", rbffi_FunctionTypeClass);
      /*
       * Document-const: FFI::FunctionInfo = FFI::FunctionType
       */
      rb_define_const(moduleFFI, "FunctionInfo", rbffi_FunctionTypeClass);
      /*
       * Document-const: FFI::Type::Function = FFI::FunctionType
       */
      rb_define_const(ffi_Type, "Function", rbffi_FunctionTypeClass);
  
      rb_define_alloc_func(rbffi_FunctionTypeClass, fntype_allocate);
      rb_define_method(rbffi_FunctionTypeClass, "initialize", fntype_initialize, -1);
      rb_define_method(rbffi_FunctionTypeClass, "result_type", fntype_result_type, 0);
      rb_define_method(rbffi_FunctionTypeClass, "param_types", fntype_param_types, 0);
  
  }
  
  