| /* 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 "XSBind.h" |
| |
| #include "Lucy/Object/VTable.h" |
| |
| #include "Lucy/Object/Obj.h" |
| #include "Lucy/Object/Host.h" |
| #include "Lucy/Object/CharBuf.h" |
| #include "Lucy/Object/Err.h" |
| #include "Lucy/Util/Memory.h" |
| |
| static SV* |
| S_do_callback_sv(void *vobj, char *method, uint32_t num_args, va_list args); |
| |
| // Convert all arguments to Perl and place them on the Perl stack. |
| static CHY_INLINE void |
| SI_push_args(void *vobj, va_list args, uint32_t num_args) { |
| lucy_Obj *obj = (lucy_Obj*)vobj; |
| SV *invoker; |
| uint32_t i; |
| dSP; |
| |
| uint32_t stack_slots_needed = num_args < 2 |
| ? num_args + 1 |
| : (num_args * 2) + 1; |
| EXTEND(SP, stack_slots_needed); |
| |
| if (Lucy_Obj_Is_A(obj, LUCY_VTABLE)) { |
| lucy_VTable *vtable = (lucy_VTable*)obj; |
| // TODO: Creating a new class name SV every time is wasteful. |
| invoker = XSBind_cb_to_sv(Lucy_VTable_Get_Name(vtable)); |
| } |
| else { |
| invoker = (SV*)Lucy_Obj_To_Host(obj); |
| } |
| |
| ENTER; |
| SAVETMPS; |
| PUSHMARK(SP); |
| PUSHs(sv_2mortal(invoker)); |
| |
| for (i = 0; i < num_args; i++) { |
| uint32_t arg_type = va_arg(args, uint32_t); |
| char *label = va_arg(args, char*); |
| if (num_args > 1) { |
| PUSHs(sv_2mortal(newSVpvn(label, strlen(label)))); |
| } |
| switch (arg_type & CFISH_HOST_ARGTYPE_MASK) { |
| case CFISH_HOST_ARGTYPE_I32: { |
| int32_t value = va_arg(args, int32_t); |
| PUSHs(sv_2mortal(newSViv(value))); |
| } |
| break; |
| case CFISH_HOST_ARGTYPE_I64: { |
| int64_t value = va_arg(args, int64_t); |
| if (sizeof(IV) == 8) { |
| PUSHs(sv_2mortal(newSViv((IV)value))); |
| } |
| else { |
| // lossy |
| PUSHs(sv_2mortal(newSVnv((double)value))); |
| } |
| } |
| break; |
| case CFISH_HOST_ARGTYPE_F32: |
| case CFISH_HOST_ARGTYPE_F64: { |
| // Floats are promoted to doubles by variadic calling. |
| double value = va_arg(args, double); |
| PUSHs(sv_2mortal(newSVnv(value))); |
| } |
| break; |
| case CFISH_HOST_ARGTYPE_STR: { |
| lucy_CharBuf *string = va_arg(args, lucy_CharBuf*); |
| PUSHs(sv_2mortal(XSBind_cb_to_sv(string))); |
| } |
| break; |
| case CFISH_HOST_ARGTYPE_OBJ: { |
| lucy_Obj* anObj = va_arg(args, lucy_Obj*); |
| SV *arg_sv = anObj == NULL |
| ? newSV(0) |
| : XSBind_cfish_to_perl(anObj); |
| PUSHs(sv_2mortal(arg_sv)); |
| } |
| break; |
| default: |
| CFISH_THROW(LUCY_ERR, "Unrecognized arg type: %u32", |
| arg_type); |
| } |
| } |
| |
| PUTBACK; |
| } |
| |
| void |
| lucy_Host_callback(void *vobj, char *method, uint32_t num_args, ...) { |
| va_list args; |
| |
| va_start(args, num_args); |
| SI_push_args(vobj, args, num_args); |
| va_end(args); |
| |
| { |
| int count = call_method(method, G_VOID | G_DISCARD); |
| if (count != 0) { |
| CFISH_THROW(LUCY_ERR, "callback '%s' returned too many values: %i32", |
| method, (int32_t)count); |
| } |
| FREETMPS; |
| LEAVE; |
| } |
| } |
| |
| int64_t |
| lucy_Host_callback_i64(void *vobj, char *method, uint32_t num_args, ...) { |
| va_list args; |
| SV *return_sv; |
| int64_t retval; |
| |
| va_start(args, num_args); |
| return_sv = S_do_callback_sv(vobj, method, num_args, args); |
| va_end(args); |
| if (sizeof(IV) == 8) { |
| retval = (int64_t)SvIV(return_sv); |
| } |
| else { |
| if (SvIOK(return_sv)) { |
| // It's already no more than 32 bits, so don't convert. |
| retval = SvIV(return_sv); |
| } |
| else { |
| // Maybe lossy. |
| double temp = SvNV(return_sv); |
| retval = (int64_t)temp; |
| } |
| } |
| |
| FREETMPS; |
| LEAVE; |
| |
| return retval; |
| } |
| |
| double |
| lucy_Host_callback_f64(void *vobj, char *method, uint32_t num_args, ...) { |
| va_list args; |
| SV *return_sv; |
| double retval; |
| |
| va_start(args, num_args); |
| return_sv = S_do_callback_sv(vobj, method, num_args, args); |
| va_end(args); |
| retval = SvNV(return_sv); |
| |
| FREETMPS; |
| LEAVE; |
| |
| return retval; |
| } |
| |
| lucy_Obj* |
| lucy_Host_callback_obj(void *vobj, char *method, uint32_t num_args, ...) { |
| va_list args; |
| SV *temp_retval; |
| lucy_Obj *retval = NULL; |
| |
| va_start(args, num_args); |
| temp_retval = S_do_callback_sv(vobj, method, num_args, args); |
| va_end(args); |
| |
| retval = XSBind_perl_to_cfish(temp_retval); |
| |
| FREETMPS; |
| LEAVE; |
| |
| return retval; |
| } |
| |
| lucy_CharBuf* |
| lucy_Host_callback_str(void *vobj, char *method, uint32_t num_args, ...) { |
| va_list args; |
| SV *temp_retval; |
| lucy_CharBuf *retval = NULL; |
| |
| va_start(args, num_args); |
| temp_retval = S_do_callback_sv(vobj, method, num_args, args); |
| va_end(args); |
| |
| // Make a stringified copy. |
| if (temp_retval && XSBind_sv_defined(temp_retval)) { |
| STRLEN len; |
| char *ptr = SvPVutf8(temp_retval, len); |
| retval = lucy_CB_new_from_trusted_utf8(ptr, len); |
| } |
| |
| FREETMPS; |
| LEAVE; |
| |
| return retval; |
| } |
| |
| void* |
| lucy_Host_callback_host(void *vobj, char *method, uint32_t num_args, ...) { |
| va_list args; |
| SV *retval; |
| |
| va_start(args, num_args); |
| retval = S_do_callback_sv(vobj, method, num_args, args); |
| va_end(args); |
| SvREFCNT_inc(retval); |
| |
| FREETMPS; |
| LEAVE; |
| |
| return retval; |
| } |
| |
| static SV* |
| S_do_callback_sv(void *vobj, char *method, uint32_t num_args, va_list args) { |
| SV *return_val; |
| SI_push_args(vobj, args, num_args); |
| { |
| int num_returned = call_method(method, G_SCALAR); |
| dSP; |
| if (num_returned != 1) { |
| CFISH_THROW(LUCY_ERR, "Bad number of return vals from %s: %i32", |
| method, (int32_t)num_returned); |
| } |
| return_val = POPs; |
| PUTBACK; |
| } |
| return return_val; |
| } |
| |
| |