示例#1
0
cons_t* proc_ffi_prep_cif_var(cons_t* p, environment_t*)
{
  assert_length(p, 3, 4);

  ffi_abi abi = FFI_DEFAULT_ABI;

  /*
   * ARGUMENT 1: ABI for foreign function
   */
  abi = parse_ffi_abi(car(p));

  /*
   * ARGUMENT 2:
   * Return type for foreign function
   */
  ffi_type* rtype = parse_ffi_type(cadr(p));

  /*
   * ARGUMENT 3:
   * Number of fixed vars
   */
  assert_type(INTEGER, caddr(p));
  unsigned int fixedargs = caddr(p)->number.integer;

  /*
   * ARGUMENT 4:
   * Types for foreign function's input parameters.
   */
  ffi_type** argtypes = NULL;
  unsigned int nargs = 0;

  if ( length(p) >= 4 ) {
    cons_t *args = cadddr(p);
    assert_type(PAIR, args);
    nargs = length(args);

    if ( nargs > 0 ) {
      argtypes = static_cast<ffi_type**>(malloc(nargs*sizeof(ffi_type*)));

      for ( unsigned int n=0; n<nargs; ++n ) {
        argtypes[n] = parse_ffi_type(car(args));
        args = cdr(args);
      }
    }
  }

  /*
   * Initialize returned struct
   */
  ffi_cif *cif = new ffi_cif();
  memset(cif, 0, sizeof(ffi_cif));

  check(ffi_prep_cif_var(cif, abi, fixedargs, nargs, rtype, argtypes));
  return pointer(tag_ffi_cif, cif);

  /*
   * In the future, the malloced argtypes should be added to the
   * pointer-return value here, so that it too can be freed.
   */
}
示例#2
0
callback*
create_callback(JNIEnv* env, jobject obj, jobject method,
                jobjectArray arg_classes, jclass return_class,
                callconv_t calling_convention, 
                jint options,
                jstring encoding) {
  jboolean direct = options & CB_OPTION_DIRECT;
  jboolean in_dll = options & CB_OPTION_IN_DLL;
  callback* cb;
  ffi_abi abi = (calling_convention == CALLCONV_C
		 ? FFI_DEFAULT_ABI : (ffi_abi)calling_convention);
  ffi_abi java_abi = FFI_DEFAULT_ABI;
  ffi_type* return_type;
  ffi_status status;
  jsize argc;
  JavaVM* vm;
  int rtype;
  char msg[MSG_SIZE];
  int i;
  int cvt = 0;
  const char* throw_type = NULL;
  const char* throw_msg = NULL;

  if ((*env)->GetJavaVM(env, &vm) != JNI_OK) {
    throwByName(env, EUnsatisfiedLink, "Couldn't obtain Java VM reference when creating native callback");
    return NULL;
  }
  argc = (*env)->GetArrayLength(env, arg_classes);

  cb = (callback *)malloc(sizeof(callback));
  cb->closure = ffi_closure_alloc(sizeof(ffi_closure), &cb->x_closure);
  cb->saved_x_closure = cb->x_closure;
  cb->object = (*env)->NewWeakGlobalRef(env, obj);
  cb->methodID = (*env)->FromReflectedMethod(env, method);

  cb->vm = vm;
  cb->arg_types = (ffi_type**)malloc(sizeof(ffi_type*) * argc);
  cb->java_arg_types = (ffi_type**)malloc(sizeof(ffi_type*) * (argc + 3));
  cb->arg_jtypes = (char*)malloc(sizeof(char) * argc);
  cb->conversion_flags = (int *)malloc(sizeof(int) * argc);
  cb->rflag = CVT_DEFAULT;
  cb->arg_classes = (jobject*)malloc(sizeof(jobject) * argc);
 
  cb->direct = direct;
  cb->java_arg_types[0] = cb->java_arg_types[1] = cb->java_arg_types[2] = &ffi_type_pointer;
  cb->encoding = newCStringUTF8(env, encoding);

  for (i=0;i < argc;i++) {
    int jtype;
    jclass cls = (*env)->GetObjectArrayElement(env, arg_classes, i);
    if ((cb->conversion_flags[i] = get_conversion_flag(env, cls)) != CVT_DEFAULT) {
      cb->arg_classes[i] = (*env)->NewWeakGlobalRef(env, cls);
      cvt = 1;
    }
    else {
      cb->arg_classes[i] = NULL;
    }

    jtype = get_java_type(env, cls);
    if (jtype == -1) {
      snprintf(msg, sizeof(msg), "Unsupported callback argument at index %d", i);
      throw_type = EIllegalArgument;
      throw_msg = msg;
      goto failure_cleanup;
    }
    cb->arg_jtypes[i] = (char)jtype;
    cb->java_arg_types[i+3] = cb->arg_types[i] = get_ffi_type(env, cls, cb->arg_jtypes[i]);
    if (!cb->java_arg_types[i+3]) {
      goto failure_cleanup;
    }
    if (cb->conversion_flags[i] == CVT_NATIVE_MAPPED
        || cb->conversion_flags[i] == CVT_POINTER_TYPE
        || cb->conversion_flags[i] == CVT_INTEGER_TYPE) {
      jclass ncls;
      ncls = getNativeType(env, cls);
      jtype = get_java_type(env, ncls);
      if (jtype == -1) {
        snprintf(msg, sizeof(msg), "Unsupported NativeMapped callback argument native type at argument %d", i);
        throw_type = EIllegalArgument;
        throw_msg = msg;
        goto failure_cleanup;
      }
      cb->arg_jtypes[i] = (char)jtype;
      cb->java_arg_types[i+3] = &ffi_type_pointer;
      cb->arg_types[i] = get_ffi_type(env, ncls, cb->arg_jtypes[i]);
      if (!cb->arg_types[i]) {
        goto failure_cleanup;
      }
    }

    // Java callback method is called using varargs, so promote floats to 
    // double where appropriate for the platform
    if (cb->arg_types[i]->type == FFI_TYPE_FLOAT) {
      cb->java_arg_types[i+3] = &ffi_type_double;
      cb->conversion_flags[i] = CVT_FLOAT;
      cvt = 1;
    }
    else if (cb->java_arg_types[i+3]->type == FFI_TYPE_STRUCT) {
      // All callback structure arguments are passed as a jobject
      cb->java_arg_types[i+3] = &ffi_type_pointer;
    }
  }
  if (!direct || !cvt) {
    free(cb->conversion_flags);
    cb->conversion_flags = NULL;
    free(cb->arg_classes);
    cb->arg_classes = NULL;
  }
  if (direct) {
    cb->rflag = get_conversion_flag(env, return_class);
    if (cb->rflag == CVT_NATIVE_MAPPED
        || cb->rflag == CVT_INTEGER_TYPE
        || cb->rflag == CVT_POINTER_TYPE) {
      return_class = getNativeType(env, return_class);
    }
  }

#if defined(_WIN32)
  if (calling_convention == CALLCONV_STDCALL) {
#if defined(_WIN64) || defined(_WIN32_WCE)
    // Ignore requests for stdcall on win64/wince
    abi = FFI_DEFAULT_ABI;
#else
    abi = FFI_STDCALL;
    // All JNI entry points on win32 use stdcall
    java_abi = FFI_STDCALL;
#endif
  }
#endif // _WIN32

  if (!(abi > FFI_FIRST_ABI && abi < FFI_LAST_ABI)) {
    snprintf(msg, sizeof(msg), "Invalid calling convention %d", abi);
    throw_type = EIllegalArgument;
    throw_msg = msg;
    goto failure_cleanup;
  }

  rtype = get_java_type(env, return_class);
  if (rtype == -1) {
    throw_type = EIllegalArgument;
    throw_msg = "Unsupported callback return type";
    goto failure_cleanup;
  }
  return_type = get_ffi_return_type(env, return_class, (char)rtype);
  if (!return_type) {
    throw_type = EIllegalArgument;
    throw_msg = "Error in callback return type";
    goto failure_cleanup;
  }
  status = ffi_prep_cif(&cb->cif, abi, argc, return_type, cb->arg_types);
  if (!ffi_error(env, "callback setup", status)) {
    ffi_type* java_return_type = return_type;

    if (cb->rflag == CVT_STRUCTURE_BYVAL
        || cb->rflag == CVT_NATIVE_MAPPED
        || cb->rflag == CVT_POINTER_TYPE
        || cb->rflag == CVT_INTEGER_TYPE) {
      // Java method returns a jobject, not a struct
      java_return_type = &ffi_type_pointer;
      rtype = '*';
    }
    switch(rtype) {
#define OFFSETOF(ENV,METHOD) ((size_t)((char *)&(*(ENV))->METHOD - (char *)(*(ENV))))
    case 'V': cb->fptr_offset = OFFSETOF(env, CallVoidMethod); break;
    case 'Z': cb->fptr_offset = OFFSETOF(env, CallBooleanMethod); break;
    case 'B': cb->fptr_offset = OFFSETOF(env, CallByteMethod); break;
    case 'S': cb->fptr_offset = OFFSETOF(env, CallShortMethod); break;
    case 'C': cb->fptr_offset = OFFSETOF(env, CallCharMethod); break;
    case 'I': cb->fptr_offset = OFFSETOF(env, CallIntMethod); break;
    case 'J': cb->fptr_offset = OFFSETOF(env, CallLongMethod); break;
    case 'F': cb->fptr_offset = OFFSETOF(env, CallFloatMethod); break;
    case 'D': cb->fptr_offset = OFFSETOF(env, CallDoubleMethod); break;
    default: cb->fptr_offset = OFFSETOF(env, CallObjectMethod); break;
    }
    status = ffi_prep_cif_var(&cb->java_cif, java_abi, 2, argc+3, java_return_type, cb->java_arg_types);
    if (!ffi_error(env, "callback setup (2)", status)) {
      ffi_prep_closure_loc(cb->closure, &cb->cif, dispatch_callback, cb,
                           cb->x_closure);
#ifdef DLL_FPTRS
      // Find an available function pointer and assign it
      if (in_dll) {
        for (i=0;i < DLL_FPTRS;i++) {
          if (fn[i] == NULL) {
            fn[i] = cb->x_closure;
            cb->x_closure = dll_fptrs[i];
            break;
          }
        }
        if (i == DLL_FPTRS) {
          throw_type = EOutOfMemory;
          throw_msg = "No more DLL callback slots available";
          goto failure_cleanup;
        }
      }
#endif
      return cb;
    }
  }

 failure_cleanup:
  free_callback(env, cb);
  if (throw_type) {
    throwByName(env, throw_type, throw_msg);
  }

  return NULL;
}
示例#3
0
static VALUE
variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues)
{
    VariadicInvoker* invoker;
    FFIStorage* params;
    void* retval;
    ffi_cif cif;
    void** ffiValues;
    ffi_type** ffiParamTypes;
    ffi_type* ffiReturnType;
    Type** paramTypes;
    VALUE* argv;
    int paramCount = 0, fixedCount = 0, i;
    ffi_status ffiStatus;
    rbffi_frame_t frame = { 0 };

    Check_Type(parameterTypes, T_ARRAY);
    Check_Type(parameterValues, T_ARRAY);

    Data_Get_Struct(self, VariadicInvoker, invoker);
    paramCount = (int) RARRAY_LEN(parameterTypes);
    paramTypes = ALLOCA_N(Type *, paramCount);
    ffiParamTypes = ALLOCA_N(ffi_type *, paramCount);
    params = ALLOCA_N(FFIStorage, paramCount);
    ffiValues = ALLOCA_N(void*, paramCount);
    argv = ALLOCA_N(VALUE, paramCount);
    retval = alloca(MAX(invoker->returnType->ffiType->size, FFI_SIZEOF_ARG));

    for (i = 0; i < paramCount; ++i) {
        VALUE rbType = rb_ary_entry(parameterTypes, i);

        if (!rb_obj_is_kind_of(rbType, rbffi_TypeClass)) {
            rb_raise(rb_eTypeError, "wrong type.  Expected (FFI::Type)");
        }
        Data_Get_Struct(rbType, Type, paramTypes[i]);

        switch (paramTypes[i]->nativeType) {
        case NATIVE_INT8:
        case NATIVE_INT16:
        case NATIVE_INT32:
            rbType = rb_const_get(rbffi_TypeClass, rb_intern("INT32"));
            Data_Get_Struct(rbType, Type, paramTypes[i]);
            break;
        case NATIVE_UINT8:
        case NATIVE_UINT16:
        case NATIVE_UINT32:
            rbType = rb_const_get(rbffi_TypeClass, rb_intern("UINT32"));
            Data_Get_Struct(rbType, Type, paramTypes[i]);
            break;

        case NATIVE_FLOAT32:
            rbType = rb_const_get(rbffi_TypeClass, rb_intern("DOUBLE"));
            Data_Get_Struct(rbType, Type, paramTypes[i]);
            break;

        default:
            break;
        }


        ffiParamTypes[i] = paramTypes[i]->ffiType;
        if (ffiParamTypes[i] == NULL) {
            rb_raise(rb_eArgError, "Invalid parameter type #%x", paramTypes[i]->nativeType);
        }
        argv[i] = rb_ary_entry(parameterValues, i);
    }

    ffiReturnType = invoker->returnType->ffiType;
    if (ffiReturnType == NULL) {
        rb_raise(rb_eArgError, "Invalid return type");
    }

    /*Get the number of fixed args from @fixed array*/
    fixedCount = RARRAY_LEN(rb_iv_get(self, "@fixed"));

#ifdef HAVE_FFI_PREP_CIF_VAR
    ffiStatus = ffi_prep_cif_var(&cif, invoker->abi, fixedCount, paramCount, ffiReturnType, ffiParamTypes);
#else
    ffiStatus = ffi_prep_cif(&cif, invoker->abi, paramCount, ffiReturnType, ffiParamTypes);
#endif
    switch (ffiStatus) {
    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");
    }

    rbffi_SetupCallParams(paramCount, argv, -1, paramTypes, params,
                          ffiValues, NULL, 0, invoker->rbEnums);

    rbffi_frame_push(&frame);
    ffi_call(&cif, FFI_FN(invoker->function), retval, ffiValues);
    rbffi_frame_pop(&frame);

    rbffi_save_errno();

    if (RTEST(frame.exc) && frame.exc != Qnil) {
        rb_exc_raise(frame.exc);
    }

    return rbffi_NativeValue_ToRuby(invoker->returnType, invoker->rbReturnType, retval);
}
示例#4
0
文件: Variadic.c 项目: AJCHN/sonic-pi
static VALUE
variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues)
{
    VariadicInvoker* invoker;
    FFIStorage* params;
    void* retval;
    ffi_cif cif;
    void** ffiValues;
    ffi_type** ffiParamTypes;
    ffi_type* ffiReturnType;
    Type** paramTypes;
    VALUE* argv;
    int paramCount = 0, fixedCount = 0, i;
    ffi_status ffiStatus;
    rbffi_frame_t frame = { 0 };

    Check_Type(parameterTypes, T_ARRAY);
    Check_Type(parameterValues, T_ARRAY);

    Data_Get_Struct(self, VariadicInvoker, invoker);
    paramCount = (int) RARRAY_LEN(parameterTypes);
    paramTypes = ALLOCA_N(Type *, paramCount);
    ffiParamTypes = ALLOCA_N(ffi_type *, paramCount);
    params = ALLOCA_N(FFIStorage, paramCount);
    ffiValues = ALLOCA_N(void*, paramCount);
    argv = ALLOCA_N(VALUE, paramCount);
    retval = alloca(MAX(invoker->returnType->ffiType->size, FFI_SIZEOF_ARG));

    for (i = 0; i < paramCount; ++i) {
        VALUE rbType = rb_ary_entry(parameterTypes, i);
        
        if (!rb_obj_is_kind_of(rbType, rbffi_TypeClass)) {
            rb_raise(rb_eTypeError, "wrong type.  Expected (FFI::Type)");
        }
        Data_Get_Struct(rbType, Type, paramTypes[i]);

        switch (paramTypes[i]->nativeType) {
            case NATIVE_INT8:
            case NATIVE_INT16:
            case NATIVE_INT32:
                rbType = rb_const_get(rbffi_TypeClass, rb_intern("INT32"));
                Data_Get_Struct(rbType, Type, paramTypes[i]);
                break;
            case NATIVE_UINT8:
            case NATIVE_UINT16:
            case NATIVE_UINT32:
                rbType = rb_const_get(rbffi_TypeClass, rb_intern("UINT32"));
                Data_Get_Struct(rbType, Type, paramTypes[i]);
                break;
            
            case NATIVE_FLOAT32:
                rbType = rb_const_get(rbffi_TypeClass, rb_intern("DOUBLE"));
                Data_Get_Struct(rbType, Type, paramTypes[i]);
                break;

            default:
                break;
        }
        
        
        ffiParamTypes[i] = paramTypes[i]->ffiType;
        if (ffiParamTypes[i] == NULL) {
            rb_raise(rb_eArgError, "Invalid parameter type #%x", paramTypes[i]->nativeType);
        }
        argv[i] = rb_ary_entry(parameterValues, i);
    }

    ffiReturnType = invoker->returnType->ffiType;
    if (ffiReturnType == NULL) {
        rb_raise(rb_eArgError, "Invalid return type");
    }

    /*Get the number of fixed args from @fixed array*/
    fixedCount = RARRAY_LEN(rb_iv_get(self, "@fixed"));

#ifdef HAVE_FFI_PREP_CIF_VAR
    ffiStatus = ffi_prep_cif_var(&cif, invoker->abi, fixedCount, paramCount, ffiReturnType, ffiParamTypes);
#else
    ffiStatus = ffi_prep_cif(&cif, invoker->abi, paramCount, ffiReturnType, ffiParamTypes);
#endif
    switch (ffiStatus) {
        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");
    }

    rbffi_SetupCallParams(paramCount, argv, -1, paramTypes, params,
        ffiValues, NULL, 0, invoker->rbEnums);
    
    rbffi_frame_push(&frame);
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
    /* In Call.c, blocking: true is supported on older ruby variants
     * without rb_thread_call_without_gvl by allocating on the heap instead
     * of the stack. Since this functionality is being added later,
     * we’re skipping support for old rubies here. */
    if(unlikely(invoker->blocking)) {
        rbffi_blocking_call_t* bc;
        bc = ALLOCA_N(rbffi_blocking_call_t, 1);
        bc->retval = retval;
        bc->function = invoker->function;
        bc->ffiValues = ffiValues;
        bc->params = params;
        bc->frame = &frame;
        bc->cif = cif;

        rb_rescue2(rbffi_do_blocking_call, (VALUE) bc, rbffi_save_frame_exception, (VALUE) &frame, rb_eException, (VALUE) 0);
    } else {
        ffi_call(&cif, FFI_FN(invoker->function), retval, ffiValues);
    }
#else
    ffi_call(&cif, FFI_FN(invoker->function), retval, ffiValues);
#endif
    rbffi_frame_pop(&frame);
    
    rbffi_save_errno();
    
    if (RTEST(frame.exc) && frame.exc != Qnil) {
        rb_exc_raise(frame.exc);
    }

    return rbffi_NativeValue_ToRuby(invoker->returnType, invoker->rbReturnType, retval);
}
示例#5
0
文件: float_va.c 项目: DevlshOne/jna
int main (void)
{
  ffi_cif    cif;

  ffi_type    *arg_types[5];
  void        *values[5];
  double        doubles[5];
  unsigned int firstarg;
  double        resfp;

  /* First test, pass float_va_fn(0,2.0) - note there are no actual
   * variadic parameters, but it's declared variadic so the ABI may be
   * different. */
  /* Call it statically and then via ffi */
  resfp=float_va_fn(0,2.0);
  // { dg-output "0: 2.0 : total: 2.0" }
  printf("compiled: %.1lf\n", resfp);
  // { dg-output "\ncompiled: 2.0" }

  arg_types[0] = &ffi_type_uint;
  arg_types[1] = &ffi_type_double;
  arg_types[2] = NULL;
  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 2, 2,
        &ffi_type_double, arg_types) == FFI_OK);

  firstarg = 0;
  doubles[0] = 2.0;
  values[0] = &firstarg;
  values[1] = &doubles[0];
  ffi_call(&cif, FFI_FN(float_va_fn), &resfp, values);
  // { dg-output "\n0: 2.0 : total: 2.0" }
  printf("ffi: %.1lf\n", resfp);
  // { dg-output "\nffi: 2.0" }

  /* Second test, float_va_fn(2,2.0,3.0,4.0), now with variadic params */
  /* Call it statically and then via ffi */
  resfp=float_va_fn(2,2.0,3.0,4.0);
  // { dg-output "\n2: 2.0 : 0:3.0  1:4.0  total: 11.0" }
  printf("compiled: %.1lf\n", resfp);
  // { dg-output "\ncompiled: 11.0" }

  arg_types[0] = &ffi_type_uint;
  arg_types[1] = &ffi_type_double;
  arg_types[2] = &ffi_type_double;
  arg_types[3] = &ffi_type_double;
  arg_types[4] = NULL;
  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 2, 4,
        &ffi_type_double, arg_types) == FFI_OK);

  firstarg = 2;
  doubles[0] = 2.0;
  doubles[1] = 3.0;
  doubles[2] = 4.0;
  values[0] = &firstarg;
  values[1] = &doubles[0];
  values[2] = &doubles[1];
  values[3] = &doubles[2];
  ffi_call(&cif, FFI_FN(float_va_fn), &resfp, values);
  // { dg-output "\n2: 2.0 : 0:3.0  1:4.0  total: 11.0" }
  printf("ffi: %.1lf\n", resfp);
  // { dg-output "\nffi: 11.0" }

  exit(0);
}