|  | /* Licensed to the Apache Software Foundation (ASF) under one or more | 
|  | * contributor license agreements.  See the NOTICE file distributed with | 
|  | * this work for additional information regarding copyright ownership. | 
|  | * The ASF licenses this file to You under the Apache License, Version 2.0 | 
|  | * (the "License"); you may not use this file except in compliance with | 
|  | * the License.  You may obtain a copy of the License at | 
|  | * | 
|  | *     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include <string.h> | 
|  | #include <stdio.h> | 
|  | #define CFC_NEED_BASE_STRUCT_DEF | 
|  | #define CFC_NEED_PERLSUB_STRUCT_DEF | 
|  | #include "CFCPerlSub.h" | 
|  | #include "CFCBase.h" | 
|  | #include "CFCFunction.h" | 
|  | #include "CFCUtil.h" | 
|  | #include "CFCParamList.h" | 
|  | #include "CFCPerlTypeMap.h" | 
|  | #include "CFCVariable.h" | 
|  | #include "CFCType.h" | 
|  |  | 
|  | #ifndef true | 
|  | #define true 1 | 
|  | #define false 0 | 
|  | #endif | 
|  |  | 
|  | static char* | 
|  | S_arg_assignment(CFCVariable *var, const char *val, | 
|  | const char *stack_location); | 
|  |  | 
|  | CFCPerlSub* | 
|  | CFCPerlSub_init(CFCPerlSub *self, CFCParamList *param_list, | 
|  | const char *class_name, const char *alias, | 
|  | int use_labeled_params) { | 
|  | CFCUTIL_NULL_CHECK(param_list); | 
|  | CFCUTIL_NULL_CHECK(class_name); | 
|  | CFCUTIL_NULL_CHECK(alias); | 
|  | self->param_list  = (CFCParamList*)CFCBase_incref((CFCBase*)param_list); | 
|  | self->class_name  = CFCUtil_strdup(class_name); | 
|  | self->alias       = CFCUtil_strdup(alias); | 
|  | self->use_labeled_params = use_labeled_params; | 
|  | self->perl_name = CFCUtil_sprintf("%s::%s", class_name, alias); | 
|  |  | 
|  | size_t c_name_len = strlen(self->perl_name) + sizeof("XS_") + 1; | 
|  | self->c_name = (char*)MALLOCATE(c_name_len); | 
|  | int j = 3; | 
|  | memcpy(self->c_name, "XS_", j); | 
|  | for (int i = 0, max = (int)strlen(self->perl_name); i < max; i++) { | 
|  | char c = self->perl_name[i]; | 
|  | if (c == ':') { | 
|  | while (self->perl_name[i + 1] == ':') { i++; } | 
|  | self->c_name[j++] = '_'; | 
|  | } | 
|  | else { | 
|  | self->c_name[j++] = c; | 
|  | } | 
|  | } | 
|  | self->c_name[j] = 0; // NULL-terminate. | 
|  |  | 
|  | return self; | 
|  | } | 
|  |  | 
|  | void | 
|  | CFCPerlSub_destroy(CFCPerlSub *self) { | 
|  | CFCBase_decref((CFCBase*)self->param_list); | 
|  | FREEMEM(self->class_name); | 
|  | FREEMEM(self->alias); | 
|  | FREEMEM(self->perl_name); | 
|  | FREEMEM(self->c_name); | 
|  | CFCBase_destroy((CFCBase*)self); | 
|  | } | 
|  |  | 
|  | char* | 
|  | CFCPerlSub_arg_declarations(CFCPerlSub *self, size_t first) { | 
|  | CFCParamList *param_list = self->param_list; | 
|  | CFCVariable **arg_vars   = CFCParamList_get_variables(param_list); | 
|  | size_t        num_vars   = CFCParamList_num_vars(param_list); | 
|  | char         *decls      = CFCUtil_strdup(""); | 
|  |  | 
|  | // Declare variables. | 
|  | for (size_t i = first; i < num_vars; i++) { | 
|  | CFCVariable *arg_var  = arg_vars[i]; | 
|  | CFCType     *type     = CFCVariable_get_type(arg_var); | 
|  | const char  *type_str = CFCType_to_c(type); | 
|  | const char  *var_name = CFCVariable_get_name(arg_var); | 
|  | decls = CFCUtil_cat(decls, "    ", type_str, " arg_", var_name, | 
|  | ";\n", NULL); | 
|  | } | 
|  |  | 
|  | return decls; | 
|  | } | 
|  |  | 
|  | char* | 
|  | CFCPerlSub_arg_name_list(CFCPerlSub *self) { | 
|  | CFCParamList  *param_list = self->param_list; | 
|  | CFCVariable  **arg_vars   = CFCParamList_get_variables(param_list); | 
|  | size_t         num_vars   = CFCParamList_num_vars(param_list); | 
|  | char          *name_list  = CFCUtil_strdup(""); | 
|  |  | 
|  | for (size_t i = 0; i < num_vars; i++) { | 
|  | const char *var_name = CFCVariable_get_name(arg_vars[i]); | 
|  | if (i > 0) { | 
|  | name_list = CFCUtil_cat(name_list, ", ", NULL); | 
|  | } | 
|  | name_list = CFCUtil_cat(name_list, "arg_", var_name, NULL); | 
|  | } | 
|  |  | 
|  | return name_list; | 
|  | } | 
|  |  | 
|  | char* | 
|  | CFCPerlSub_build_param_specs(CFCPerlSub *self, size_t first) { | 
|  | CFCParamList  *param_list = self->param_list; | 
|  | CFCVariable  **arg_vars   = CFCParamList_get_variables(param_list); | 
|  | const char   **arg_inits  = CFCParamList_get_initial_values(param_list); | 
|  | size_t         num_vars   = CFCParamList_num_vars(param_list); | 
|  |  | 
|  | const char *pattern | 
|  | = "    static const XSBind_ParamSpec param_specs[%d] = {"; | 
|  | char *param_specs = CFCUtil_sprintf(pattern, num_vars - first); | 
|  |  | 
|  | // Iterate over args in param list. | 
|  | for (size_t i = first; i < num_vars; i++) { | 
|  | if (i != first) { | 
|  | param_specs = CFCUtil_cat(param_specs, ",", NULL); | 
|  | } | 
|  |  | 
|  | CFCVariable *var  = arg_vars[i]; | 
|  | const char  *val  = arg_inits[i]; | 
|  | const char  *name = CFCVariable_get_name(var); | 
|  | int required = val ? 0 : 1; | 
|  |  | 
|  | char *spec = CFCUtil_sprintf("XSBIND_PARAM(\"%s\", %d)", name, | 
|  | required); | 
|  | param_specs = CFCUtil_cat(param_specs, "\n        ", spec, NULL); | 
|  | FREEMEM(spec); | 
|  | } | 
|  |  | 
|  | param_specs = CFCUtil_cat(param_specs, "\n    };\n", NULL); | 
|  |  | 
|  | return param_specs; | 
|  | } | 
|  |  | 
|  | char* | 
|  | CFCPerlSub_arg_assignments(CFCPerlSub *self) { | 
|  | CFCParamList  *param_list = self->param_list; | 
|  | CFCVariable  **arg_vars   = CFCParamList_get_variables(param_list); | 
|  | const char   **arg_inits  = CFCParamList_get_initial_values(param_list); | 
|  | size_t         num_vars   = CFCParamList_num_vars(param_list); | 
|  |  | 
|  | char *arg_assigns = CFCUtil_strdup(""); | 
|  |  | 
|  | for (size_t i = 1; i < num_vars; i++) { | 
|  | char stack_location[30]; | 
|  | if (self->use_labeled_params) { | 
|  | sprintf(stack_location, "locations[%u]", (unsigned)(i - 1)); | 
|  | } | 
|  | else { | 
|  | sprintf(stack_location, "%u", (unsigned)i); | 
|  | } | 
|  | char *statement = S_arg_assignment(arg_vars[i], arg_inits[i], | 
|  | stack_location); | 
|  | arg_assigns = CFCUtil_cat(arg_assigns, statement, NULL); | 
|  | FREEMEM(statement); | 
|  | } | 
|  |  | 
|  | return arg_assigns; | 
|  | } | 
|  |  | 
|  | static char* | 
|  | S_arg_assignment(CFCVariable *var, const char *val, | 
|  | const char *stack_location) { | 
|  | const char *var_name  = CFCVariable_get_name(var); | 
|  | CFCType    *var_type  = CFCVariable_get_type(var); | 
|  | char       *statement = NULL; | 
|  |  | 
|  | char *conversion = CFCPerlTypeMap_from_perl(var_type, "sv", var_name); | 
|  | if (!conversion) { | 
|  | const char *type_c = CFCType_to_c(var_type); | 
|  | CFCUtil_die("Can't map type '%s'", type_c); | 
|  | } | 
|  |  | 
|  | if (val) { | 
|  | if (CFCType_is_object(var_type)) { | 
|  | const char pattern[] = "    arg_%s = %s < items ? %s : %s;\n"; | 
|  | statement = CFCUtil_sprintf(pattern, var_name, stack_location, | 
|  | conversion, val); | 
|  | } | 
|  | else { | 
|  | const char pattern[] = | 
|  | "    arg_%s = %s < items && XSBind_sv_defined(aTHX_ sv)\n" | 
|  | "             ? %s : %s;\n"; | 
|  | statement = CFCUtil_sprintf(pattern, var_name, stack_location, | 
|  | conversion, val); | 
|  | } | 
|  | } | 
|  | else { | 
|  | if (CFCType_is_object(var_type)) { | 
|  | const char pattern[] = "    arg_%s = %s;\n"; | 
|  | statement = CFCUtil_sprintf(pattern, var_name, conversion); | 
|  | } | 
|  | else { | 
|  | const char pattern[] = | 
|  | "    if (!XSBind_sv_defined(aTHX_ sv)) {\n" | 
|  | "        XSBind_undef_arg_error(aTHX_ \"%s\");\n" | 
|  | "    }\n" | 
|  | "    arg_%s = %s;\n"; | 
|  | statement = CFCUtil_sprintf(pattern, var_name, var_name, | 
|  | conversion); | 
|  | } | 
|  | } | 
|  |  | 
|  | const char pattern[] = | 
|  | "    sv = ST(%s);\n" | 
|  | "%s"; | 
|  | char *retval = CFCUtil_sprintf(pattern, stack_location, statement); | 
|  |  | 
|  | FREEMEM(conversion); | 
|  | FREEMEM(statement); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | CFCParamList* | 
|  | CFCPerlSub_get_param_list(CFCPerlSub *self) { | 
|  | return self->param_list; | 
|  | } | 
|  |  | 
|  | const char* | 
|  | CFCPerlSub_get_class_name(CFCPerlSub *self) { | 
|  | return self->class_name; | 
|  | } | 
|  |  | 
|  | const char* | 
|  | CFCPerlSub_get_alias(CFCPerlSub *self) { | 
|  | return self->alias; | 
|  | } | 
|  |  | 
|  | int | 
|  | CFCPerlSub_use_labeled_params(CFCPerlSub *self) { | 
|  | return self->use_labeled_params; | 
|  | } | 
|  |  | 
|  | const char* | 
|  | CFCPerlSub_perl_name(CFCPerlSub *self) { | 
|  | return self->perl_name; | 
|  | } | 
|  |  | 
|  | const char* | 
|  | CFCPerlSub_c_name(CFCPerlSub *self) { | 
|  | return self->c_name; | 
|  | } | 
|  |  | 
|  | const char* | 
|  | CFCPerlSub_c_name_list(CFCPerlSub *self) { | 
|  | return CFCParamList_name_list(self->param_list); | 
|  | } | 
|  |  |