diff --git a/src/function.cc b/src/function.cc index 6380211..00affdd 100644 --- a/src/function.cc +++ b/src/function.cc @@ -106,7 +106,196 @@ bool IsDestroyNotify (GIBaseInfo *info) { /** - * Calls a function + * The constructor just stores the GIBaseInfo ref. The rest of the + * initialization is done in FunctionInfo::Init, lazily. + */ +FunctionInfo::FunctionInfo (GIBaseInfo* gi_info) { + info = g_base_info_ref (gi_info); + call_parameters = nullptr; +} + +FunctionInfo::~FunctionInfo () { + g_base_info_unref (info); + + if (call_parameters != nullptr) { + g_function_invoker_destroy (&invoker); + delete[] call_parameters; + } +} + +/** + * Initializes the parameters metadata (number, directionality, type) and caches it + */ +bool FunctionInfo::Init() { + + if (call_parameters != nullptr) + return true; + + g_function_info_prep_invoker (info, &invoker, NULL); + + is_method = IsMethod(info); + can_throw = g_callable_info_can_throw_gerror (info); + + n_callable_args = g_callable_info_get_n_args (info); + n_total_args = n_callable_args; + n_out_args = 0; + n_in_args = 0; + + if (is_method) + n_total_args++; + + if (can_throw) + n_total_args++; + + call_parameters = new Parameter[n_callable_args](); + + /* + * Examine load parameter types and count arguments + */ + + for (int i = 0; i < n_callable_args; i++) { + GIArgInfo arg_info; + GITypeInfo type_info; + g_callable_info_load_arg ((GICallableInfo *) info, i, &arg_info); + g_arg_info_load_type (&arg_info, &type_info); + + bool may_be_null = g_arg_info_may_be_null (&arg_info); + GIDirection direction = g_arg_info_get_direction (&arg_info); + GITypeTag tag = g_type_info_get_tag(&type_info); + + call_parameters[i].direction = direction; + + if (call_parameters[i].type == ParameterType::SKIP) + continue; + + // If there is an array length, this is an array + int length_i = g_type_info_get_array_length (&type_info); + if (tag == GI_TYPE_TAG_ARRAY && length_i >= 0) { + call_parameters[i].type = ParameterType::ARRAY; + call_parameters[length_i].type = ParameterType::SKIP; + + // If array length came before, we need to remove it from args count + + if (IsDirectionIn(call_parameters[length_i].direction) && length_i < i) + n_in_args--; + + if (IsDirectionOut(call_parameters[length_i].direction) && length_i < i) + n_out_args--; + + } else if (tag == GI_TYPE_TAG_INTERFACE) { + + GIBaseInfo* interface_info = g_type_info_get_interface(&type_info); + GIInfoType interface_type = g_base_info_get_type(interface_info); + + if (interface_type == GI_INFO_TYPE_CALLBACK) { + if (IsDestroyNotify(interface_info)) { + /* Skip GDestroyNotify if they appear before the respective callback */ + call_parameters[i].type = ParameterType::SKIP; + } else { + call_parameters[i].type = ParameterType::CALLBACK; + + int destroy_i = g_arg_info_get_destroy(&arg_info); + int closure_i = g_arg_info_get_closure(&arg_info); + + if (destroy_i >= 0 && closure_i < 0) { + Throw::UnsupportedCallback (info); + g_base_info_unref(interface_info); + return false; + } + + if (destroy_i >= 0 && destroy_i < n_callable_args) + call_parameters[destroy_i].type = ParameterType::SKIP; + + if (closure_i >= 0 && closure_i < n_callable_args) + call_parameters[closure_i].type = ParameterType::SKIP; + + if (destroy_i < i) { + if (IsDirectionIn(call_parameters[destroy_i].direction)) + n_in_args--; + if (IsDirectionOut(call_parameters[destroy_i].direction)) + n_out_args--; + } + + if (closure_i < i) { + if (IsDirectionIn(call_parameters[closure_i].direction)) + n_in_args--; + if (IsDirectionOut(call_parameters[closure_i].direction)) + n_out_args--; + } + } + } + + g_base_info_unref(interface_info); + } + + if (IsDirectionIn(call_parameters[i].direction) && !may_be_null) + n_in_args++; + + if (IsDirectionOut(call_parameters[i].direction)) + n_out_args++; + + } + + /* + * Examine return type + */ + + GITypeInfo return_type; + g_callable_info_load_return_type(info, &return_type); + int return_length_i = g_type_info_get_array_length(&return_type); + + if (return_length_i != -1) + n_out_args--; + + if (!ShouldSkipReturn(info, &return_type)) + n_out_args++; + + return true; +} + +/** + * Type checks the JS arguments, throwing an error. + * @returns true if types match + */ +bool FunctionInfo::TypeCheck (const Nan::FunctionCallbackInfo &arguments) { + + if (arguments.Length() < n_in_args) { + Throw::NotEnoughArguments(n_in_args, arguments.Length()); + return false; + } + + /* + * Type check every IN-argument that is not skipped + */ + + for (int in_arg = 0, i = 0; i < n_callable_args; i++) { + Parameter ¶m = call_parameters[i]; + + if (param.type == ParameterType::SKIP) + continue; + + GIArgInfo arg_info; + g_callable_info_load_arg (info, i, &arg_info); + GIDirection direction = g_arg_info_get_direction (&arg_info); + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) { + GITypeInfo type_info; + g_arg_info_load_type (&arg_info, &type_info); + bool may_be_null = g_arg_info_may_be_null (&arg_info); + + if (!CanConvertV8ToGIArgument(&type_info, arguments[in_arg], may_be_null)) { + Throw::InvalidType(&arg_info, &type_info, arguments[in_arg]); + return false; + } + in_arg++; + } + } + + return true; +} + +/** + * Calls a C function from JS arguments * @param func the function info * @param info JS call informations * @param return_value (out, nullable) the C return value @@ -119,7 +308,6 @@ Local FunctionCall ( GIArgument *return_value, GError **error ) { - /* FIXME(return_value is never use) */ Local jsReturnValue; GIBaseInfo *gi_info = func->info; // do-not-free @@ -346,195 +534,6 @@ Local FunctionCall ( } -/** - * The constructor just stores the GIBaseInfo ref. The rest of the - * initialization is done in FunctionInfo::Init, lazily. - */ -FunctionInfo::FunctionInfo (GIBaseInfo* gi_info) { - info = g_base_info_ref (gi_info); - call_parameters = nullptr; -} - -FunctionInfo::~FunctionInfo () { - g_base_info_unref (info); - - if (call_parameters != nullptr) { - g_function_invoker_destroy (&invoker); - delete[] call_parameters; - } -} - -/** - * Initializes the function calling data. - */ -bool FunctionInfo::Init() { - - if (call_parameters != nullptr) - return true; - - g_function_info_prep_invoker (info, &invoker, NULL); - - is_method = IsMethod(info); - can_throw = g_callable_info_can_throw_gerror (info); - - n_callable_args = g_callable_info_get_n_args (info); - n_total_args = n_callable_args; - n_out_args = 0; - n_in_args = 0; - - if (is_method) - n_total_args++; - - if (can_throw) - n_total_args++; - - call_parameters = new Parameter[n_callable_args](); - - /* - * Examine load parameter types and count arguments - */ - - for (int i = 0; i < n_callable_args; i++) { - GIArgInfo arg_info; - GITypeInfo type_info; - g_callable_info_load_arg ((GICallableInfo *) info, i, &arg_info); - g_arg_info_load_type (&arg_info, &type_info); - - bool may_be_null = g_arg_info_may_be_null (&arg_info); - GIDirection direction = g_arg_info_get_direction (&arg_info); - GITypeTag tag = g_type_info_get_tag(&type_info); - - call_parameters[i].direction = direction; - - if (call_parameters[i].type == ParameterType::SKIP) - continue; - - // If there is an array length, this is an array - int length_i = g_type_info_get_array_length (&type_info); - if (tag == GI_TYPE_TAG_ARRAY && length_i >= 0) { - call_parameters[i].type = ParameterType::ARRAY; - call_parameters[length_i].type = ParameterType::SKIP; - - // If array length came before, we need to remove it from args count - - if (IsDirectionIn(call_parameters[length_i].direction) && length_i < i) - n_in_args--; - - if (IsDirectionOut(call_parameters[length_i].direction) && length_i < i) - n_out_args--; - - } else if (tag == GI_TYPE_TAG_INTERFACE) { - - GIBaseInfo* interface_info = g_type_info_get_interface(&type_info); - GIInfoType interface_type = g_base_info_get_type(interface_info); - - if (interface_type == GI_INFO_TYPE_CALLBACK) { - if (IsDestroyNotify(interface_info)) { - /* Skip GDestroyNotify if they appear before the respective callback */ - call_parameters[i].type = ParameterType::SKIP; - } else { - call_parameters[i].type = ParameterType::CALLBACK; - - int destroy_i = g_arg_info_get_destroy(&arg_info); - int closure_i = g_arg_info_get_closure(&arg_info); - - if (destroy_i >= 0 && closure_i < 0) { - Throw::UnsupportedCallback (info); - g_base_info_unref(interface_info); - return false; - } - - if (destroy_i >= 0 && destroy_i < n_callable_args) - call_parameters[destroy_i].type = ParameterType::SKIP; - - if (closure_i >= 0 && closure_i < n_callable_args) - call_parameters[closure_i].type = ParameterType::SKIP; - - if (destroy_i < i) { - if (IsDirectionIn(call_parameters[destroy_i].direction)) - n_in_args--; - if (IsDirectionOut(call_parameters[destroy_i].direction)) - n_out_args--; - } - - if (closure_i < i) { - if (IsDirectionIn(call_parameters[closure_i].direction)) - n_in_args--; - if (IsDirectionOut(call_parameters[closure_i].direction)) - n_out_args--; - } - } - } - - g_base_info_unref(interface_info); - } - - if (IsDirectionIn(call_parameters[i].direction) && !may_be_null) - n_in_args++; - - if (IsDirectionOut(call_parameters[i].direction)) - n_out_args++; - - } - - /* - * Examine return type - */ - - GITypeInfo return_type; - g_callable_info_load_return_type(info, &return_type); - int return_length_i = g_type_info_get_array_length(&return_type); - - if (return_length_i != -1) - n_out_args--; - - if (!ShouldSkipReturn(info, &return_type)) - n_out_args++; - - return true; -} - -/** - * Type checks the JS arguments, throwing an error. - * @returns true if types match - */ -bool FunctionInfo::TypeCheck (const Nan::FunctionCallbackInfo &arguments) { - - if (arguments.Length() < n_in_args) { - Throw::NotEnoughArguments(n_in_args, arguments.Length()); - return false; - } - - /* - * Type check every IN-argument that is not skipped - */ - - for (int in_arg = 0, i = 0; i < n_callable_args; i++) { - Parameter ¶m = call_parameters[i]; - - if (param.type == ParameterType::SKIP) - continue; - - GIArgInfo arg_info; - g_callable_info_load_arg (info, i, &arg_info); - GIDirection direction = g_arg_info_get_direction (&arg_info); - - if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) { - GITypeInfo type_info; - g_arg_info_load_type (&arg_info, &type_info); - bool may_be_null = g_arg_info_may_be_null (&arg_info); - - if (!CanConvertV8ToGIArgument(&type_info, arguments[in_arg], may_be_null)) { - Throw::InvalidType(&arg_info, &type_info, arguments[in_arg]); - return false; - } - in_arg++; - } - } - - return true; -} - /** * Creates the JS return value from the C arguments list * @returns the JS return value