static void ffi_ptr_dereference_assign(VMState *state, CallInfo *info) { VM_ASSERT(info->args_len == 3, "wrong arity: expected 2, got %i", info->args_len); Object *root = state->root; Object *pointer_base = state->shared->vcache.pointer_base; FFIObject *ffi = (FFIObject*) AS_OBJ(OBJECT_LOOKUP(root, ffi)); Object *ffi_type = AS_OBJ(OBJECT_LOOKUP((Object*) ffi, type)); Object *thisptr = AS_OBJ(load_arg(state->frame, info->this_arg)); VM_ASSERT(thisptr->parent == pointer_base, "internal error"); PointerObject *thisptr_obj = (PointerObject*) thisptr; Object *ffi_type_obj = obj_instance_of(OBJ_OR_NULL(load_arg(state->frame, INFO_ARGS_PTR(info)[0])), ffi_type); Value offs_val = load_arg(state->frame, INFO_ARGS_PTR(info)[1]); VM_ASSERT(IS_INT(offs_val), "offset must be integer"); int offs = AS_INT(offs_val); VM_ASSERT(ffi_type_obj, "type is not a FFI type"); char *offset_ptr = (char*) thisptr_obj->ptr + offs; Value value = load_arg(state->frame, INFO_ARGS_PTR(info)[2]); (void) offset_ptr; (void) value; if (ffi_type_obj == ffi->long_obj) { VM_ASSERT(IS_INT(value), "can only assign integer to long"); *(long*) offset_ptr = AS_INT(value); } else { fprintf(stderr, "TODO\n"); abort(); } }
static void ffi_ptr_dereference(VMState *state, CallInfo *info) { VM_ASSERT(info->args_len == 2, "wrong arity: expected 2, got %i", info->args_len); Object *root = state->root; Object *pointer_base = state->shared->vcache.pointer_base; FFIObject *ffi = (FFIObject*) AS_OBJ(OBJECT_LOOKUP(root, ffi)); Object *ffi_type = AS_OBJ(OBJECT_LOOKUP((Object*) ffi, type)); Object *thisptr = AS_OBJ(load_arg(state->frame, info->this_arg)); VM_ASSERT(thisptr->parent == pointer_base, "internal error"); PointerObject *thisptr_obj = (PointerObject*) thisptr; Object *ffi_type_obj = obj_instance_of(OBJ_OR_NULL(load_arg(state->frame, INFO_ARGS_PTR(info)[0])), ffi_type); Value offs_val = load_arg(state->frame, INFO_ARGS_PTR(info)[1]); VM_ASSERT(IS_INT(offs_val), "offset must be integer"); int offs = AS_INT(offs_val); VM_ASSERT(ffi_type_obj, "type is not a FFI type"); char *offset_ptr = (char*) thisptr_obj->ptr + offs; if (ffi_type_obj == ffi->short_obj) { short s = *(short*) offset_ptr; vm_return(state, info, INT2VAL(s)); } else if (ffi_type_obj == ffi->ushort_obj) { unsigned short us = *(unsigned short*) offset_ptr; vm_return(state, info, INT2VAL(us)); } else if (ffi_type_obj == ffi->int_obj) { int i = *(int*) offset_ptr; vm_return(state, info, INT2VAL(i)); } else if (ffi_type_obj == ffi->uint_obj) { unsigned int u = *(unsigned int*) offset_ptr; vm_return(state, info, INT2VAL(u)); } else if (ffi_type_obj == ffi->int8_obj) { int8_t i8 = *(int8_t*) offset_ptr; vm_return(state, info, INT2VAL(i8)); } else if (ffi_type_obj == ffi->uint8_obj) { uint8_t u8 = *(uint8_t*) offset_ptr; vm_return(state, info, INT2VAL(u8)); } else if (ffi_type_obj == ffi->int32_obj) { int32_t i32 = *(int32_t*) offset_ptr; vm_return(state, info, INT2VAL(i32)); } else if (ffi_type_obj == ffi->uint32_obj) { uint32_t u32 = *(uint32_t*) offset_ptr; vm_return(state, info, INT2VAL(u32)); } else if (ffi_type_obj == ffi->pointer_obj) { void *ptr = *(void**) offset_ptr; vm_return(state, info, make_ffi_pointer(state, ptr)); } else if (ffi_type_obj == ffi->char_pointer_obj) { char *ptr = *(char**) offset_ptr; vm_return(state, info, make_string_static(state, ptr)); } else if (ffi_type_obj == ffi->long_obj) { long l = *(long*) offset_ptr; if (l < INT_MIN || l > INT_MAX) { VM_ASSERT(false, "value exceeds bounds of my int type"); } vm_return(state, info, INT2VAL((int) l)); } else { fprintf(stderr, "TODO\n"); abort(); } }
static void ffi_open_fn(VMState *state, CallInfo *info) { VM_ASSERT(info->args_len == 1, "wrong arity: expected 1, got %i", info->args_len); Object *root = state->root; Object *string_base = state->shared->vcache.string_base; Object *array_base = state->shared->vcache.array_base; Object *ffi = AS_OBJ(OBJECT_LOOKUP(root, ffi)); Object *handle_base = AS_OBJ(OBJECT_LOOKUP(ffi, handle)); StringObject *sarg = (StringObject*) obj_instance_of(OBJ_OR_NULL(load_arg(state->frame, INFO_ARGS_PTR(info)[0])), string_base); VM_ASSERT(sarg, "argument to ffi.open must be string!"); Object *libmap = AS_OBJ(OBJECT_LOOKUP(ffi, library_map)); char *file = sarg->value; bool file_found = false; FastKey file_key = prepare_key(file, strlen(file)); Value mapping = object_lookup_p(libmap, &file_key, &file_found); const char **file_list_ptr = NULL; int file_list_len = 0; if (file_found) { ArrayObject *aobj = (ArrayObject*) obj_instance_of(OBJ_OR_NULL(mapping), array_base); StringObject *sobj = (StringObject*) obj_instance_of(OBJ_OR_NULL(mapping), string_base); if (aobj) { file_list_len = aobj->length; file_list_ptr = malloc(sizeof(char*) * file_list_len); for (int i = 0; i < file_list_len; i++) { StringObject *file = (StringObject*) obj_instance_of(OBJ_OR_NULL(aobj->ptr[i]), string_base); VM_ASSERT(file, "library_map sub-entries must be string"); file_list_ptr[i] = my_asprintf("%s", file->value); // outside gc, make copy } } else if (sobj) { file_list_len = 1; file_list_ptr = malloc(sizeof(char*) * 1); file_list_ptr[0] = my_asprintf("%s", sobj->value); } else VM_ASSERT(false, "library_map entries must be string or array"); } else { file_list_len = 1; file_list_ptr = malloc(sizeof(char*) * 1); file_list_ptr[0] = file; } void *dlptr = my_dlopen(file_list_len, file_list_ptr); Object *handle_obj = AS_OBJ(make_object(state, handle_base, false)); handle_obj->flags |= OBJ_FROZEN; OBJECT_SET(state, handle_obj, pointer, make_ptr(state, dlptr)); vm_return(state, info, OBJ2VAL(handle_obj)); }
static void ffi_ptr_index_assign_fn(VMState *state, CallInfo *info) { VM_ASSERT(info->args_len == 2, "wrong arity: expected 2, got %i", info->args_len); Object *pointer_base = state->shared->vcache.pointer_base; Object *thisptr = OBJ_OR_NULL(load_arg(state->frame, info->this_arg)); VM_ASSERT(thisptr && thisptr->parent == pointer_base, "invalid pointer index write on non-pointer object"); PointerObject *thisptr_obj = (PointerObject*) thisptr; Object *ffi_type_obj = AS_OBJ(OBJECT_LOOKUP(thisptr, target_type)); VM_ASSERT(ffi_type_obj, "cannot assign index on untyped pointer!"); Value offs_val = load_arg(state->frame, INFO_ARGS_PTR(info)[0]); VM_ASSERT(IS_INT(offs_val), "offset must be integer"); int offs = AS_INT(offs_val); Value sizeof_val = OBJECT_LOOKUP(ffi_type_obj, sizeof); VM_ASSERT(IS_INT(sizeof_val), "internal error: sizeof wrong type or undefined"); int elemsize = AS_INT(sizeof_val); char *offset_ptr = (char*) thisptr_obj->ptr + elemsize * offs; bool res = ffi_pointer_write(state, ffi_type_obj, (void*) offset_ptr, load_arg(state->frame, INFO_ARGS_PTR(info)[1])); if (!res) return; }
static void ffi_ptr_add(VMState *state, CallInfo *info) { VM_ASSERT(info->args_len == 1, "wrong arity: expected 1, got %i", info->args_len); Object *pointer_base = state->shared->vcache.pointer_base; Object *thisptr = OBJ_OR_NULL(load_arg(state->frame, info->this_arg)); VM_ASSERT(thisptr && thisptr->parent == pointer_base, "internal error"); PointerObject *thisptr_obj = (PointerObject*) thisptr; void *ptr = (void*) thisptr_obj->ptr; int elemsize = 1; Value target_type = OBJECT_LOOKUP(thisptr, target_type); if (!IS_NULL(target_type)) { VM_ASSERT(IS_OBJ(target_type), "target type must be ffi type"); Value sizeof_val = OBJECT_LOOKUP(AS_OBJ(target_type), sizeof); VM_ASSERT(IS_INT(sizeof_val), "internal error: sizeof wrong type or undefined"); elemsize = AS_INT(sizeof_val); }
int main(int argc, char **argv) { char *app, *server, *match, *parm, *tmp, *cmd, *extra; int i, j, c, fd; int verbose, result, status, base, run, info, reply, display, max, prefix, timeout, fmt, pos, flags, munge, show; struct katcl_line *l, *k; fd_set fsr, fsw; struct timeval tv; server = getenv("KATCP_SERVER"); if(server == NULL){ server = "localhost"; } info = 1; reply = 1; verbose = 1; i = j = 1; app = argv[0]; base = (-1); timeout = 5; fmt = FMT_TEXT; pos = (-1); k = NULL; munge = 0; show = 1; while (i < argc) { if (argv[i][0] == '-') { c = argv[i][j]; switch (c) { case 'h' : usage(app); return 0; case 'v' : verbose++; j++; break; case 'q' : verbose = 0; info = 0; reply = 0; j++; break; case 'i' : info = 1 - info; j++; break; case 'r' : reply = 1 - reply; j++; break; case 'n' : show = 0; j++; break; case 'k' : k = create_katcl(STDOUT_FILENO); if(k == NULL){ fprintf(stderr, "%s: unable to create katcp message logic\n", app); return 2; } j++; break; case 'a' : fmt = FMT_AUTO; j++; break; case 'x' : fmt = FMT_HEX; j++; break; case 'b' : fmt = FMT_BIN; j++; break; case 'm' : munge = 1; j++; break; case 's' : case 't' : case 'p' : j++; if (argv[i][j] == '\0') { j = 0; i++; } if (i >= argc) { fprintf(stderr, "%s: argument needs a parameter\n", app); return 2; } switch(c){ case 's' : server = argv[i] + j; break; case 't' : timeout = atoi(argv[i] + j); break; case 'p' : pos = atoi(argv[i] + j); if(pos < 0){ fprintf(stderr, "%s: position needs to be nonnegative, not %d\n", app, pos); return 2; } break; } i++; j = 1; break; case '-' : j++; break; case '\0': j = 1; i++; break; default: fprintf(stderr, "%s: unknown option -%c\n", app, argv[i][j]); return 2; } } else { base = i; i = argc; } } if(munge){ if(k){ reply = 1; } } if(base < 0){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, KCPCMD_NAME, "no command given"); } fprintf(stderr, "%s: need a command to send (use -h for help)\n", app); return 2; } status = 1; flags = 0; if(verbose > 0){ flags = NETC_VERBOSE_ERRORS; if(verbose > 1){ flags = NETC_VERBOSE_STATS; } } fd = net_connect(server, 0, flags); if(fd < 0){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, KCPCMD_NAME, "unable to connect to %s", server); } return 2; } l = create_katcl(fd); if(l == NULL){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, KCPCMD_NAME, "unable to create katcp parser"); } fprintf(stderr, "%s: unable to create katcp parser\n", app); return 2; } i = base; match = NULL; flags = ((i + 1) < argc) ? KATCP_FLAG_FIRST : (KATCP_FLAG_FIRST | KATCP_FLAG_LAST); switch(argv[i][0]){ case KATCP_REQUEST : match = argv[i] + 1; /* FALL */ case KATCP_INFORM : case KATCP_REPLY : append_string_katcl(l, flags, argv[i]); break; default : match = argv[i]; append_args_katcl(l, flags, "%c%s", KATCP_REQUEST, argv[i]); break; } i++; while(i < argc){ tmp = argv[i]; i++; flags = (i < argc) ? 0 : KATCP_FLAG_LAST; if(load_arg(l, tmp, fmt, flags) < 0){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, KCPCMD_NAME, "unable to load argument %d", i); } fprintf(stderr, "%s: unable to load argument %d\n", app, i); return 2; } } if(match){ for(prefix = 0; (match[prefix] != '\0') && (match[prefix] != ' '); prefix++); #ifdef DEBUG fprintf(stderr, "debug: checking prefix %d of %s\n", prefix, match); #endif } else { prefix = 0; /* pacify -Wall, prefix only used if match is set */ } /* WARNING: logic a bit intricate */ for(run = 1; run;){ FD_ZERO(&fsr); FD_ZERO(&fsw); if(match){ /* only look for data if we need it */ FD_SET(fd, &fsr); } if(flushing_katcl(l)){ /* only write data if we have some */ FD_SET(fd, &fsw); } tv.tv_sec = timeout; tv.tv_usec = 0; result = select(fd + 1, &fsr, &fsw, NULL, &tv); switch(result){ case -1 : switch(errno){ case EAGAIN : case EINTR : continue; /* WARNING */ default : if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, KCPCMD_NAME, "select failed: %s", strerror(errno)); } return 2; } break; case 0 : if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, KCPCMD_NAME, "request timed out after %d seconds", timeout); } if(verbose){ fprintf(stderr, "%s: no io activity within %d seconds\n", app, timeout); } /* could terminate cleanly here, but ... */ return 2; } if(FD_ISSET(fd, &fsw)){ result = write_katcl(l); if(result < 0){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, KCPCMD_NAME, "write failed: %s", strerror(errno)); } fprintf(stderr, "%s: write failed: %s\n", app, strerror(error_katcl(l))); return 2; } if((result > 0) && (match == NULL)){ /* if we finished writing and don't expect a match then quit */ run = 0; } } if(FD_ISSET(fd, &fsr)){ result = read_katcl(l); if(result){ if(k){ sync_message_katcl(k, KATCP_LEVEL_ERROR, KCPCMD_NAME, "read failed: %s", (result < 0) ? strerror(error_katcl(l)) : "connection terminated"); } fprintf(stderr, "%s: read failed: %s\n", app, (result < 0) ? strerror(error_katcl(l)) : "connection terminated"); return 2; } } while(have_katcl(l) > 0){ cmd = arg_string_katcl(l, 0); if(cmd){ display = 0; switch(cmd[0]){ case KATCP_INFORM : display = info; if(show == 0){ if(!strcmp(KATCP_VERSION_CONNECT_INFORM, cmd)){ display = 0; } } break; case KATCP_REPLY : display = reply; parm = arg_string_katcl(l, 1); if(match){ if(strncmp(match, cmd + 1, prefix) || ((cmd[prefix + 1] != '\0') && (cmd[prefix + 1] != ' '))){ if(k){ sync_message_katcl(k, KATCP_LEVEL_WARN, KCPCMD_NAME, "encountered unexpected reply %s", cmd); } fprintf(stderr, "%s: warning: encountered unexpected reply <%s>\n", app, cmd); } else { if(parm && !strcmp(parm, KATCP_OK)){ status = 0; } run = 0; } } break; case KATCP_REQUEST : if(k){ sync_message_katcl(k, KATCP_LEVEL_WARN, KCPCMD_NAME, "encountered unanswerable request %s", cmd); } fprintf(stderr, "%s: warning: encountered an unanswerable request <%s>\n", app, cmd); break; default : if(k){ sync_message_katcl(k, KATCP_LEVEL_WARN, KCPCMD_NAME, "read malformed message %s", cmd); } fprintf(stderr, "%s: read malformed message <%s>\n", app, cmd); break; } if(display){ #ifdef DEBUG fprintf(stderr, "need to display\n"); #endif if(k){ if(munge && parm && (cmd[0] == KATCP_REPLY)){ if(!strcmp(parm, KATCP_OK)){ sync_message_katcl(k, KATCP_LEVEL_DEBUG, cmd + 1, KATCP_OK); } else { extra = arg_string_katcl(l, 2); sync_message_katcl(k, KATCP_LEVEL_WARN, cmd + 1, "%s (%s)", parm, extra ? extra : "no extra information"); } } else { relay_katcl(l, k); } } else { max = arg_count_katcl(l); if(pos < 0){ for(i = 0; i < max; i++){ if(print_arg(l, i, fmt) < 0){ fprintf(stderr, "%s: failed to print argument %d\n", app, i); return 2; } fputc(((i + 1) == max) ? '\n' : ' ' , stdout); } } else { if(pos < max){ i = pos; if(print_arg(l, i, fmt) < 0){ fprintf(stderr, "%s: failed to print argument %d\n", app, i); return 2; } } } } } } } } destroy_katcl(l, 1); if(k){ while(write_katcl(k) == 0); destroy_katcl(k, 0); } return status; }
VM_ASSERT(thisptr && thisptr->parent == pointer_base, "internal error"); PointerObject *thisptr_obj = (PointerObject*) thisptr; void *ptr = (void*) thisptr_obj->ptr; int elemsize = 1; Value target_type = OBJECT_LOOKUP(thisptr, target_type); if (!IS_NULL(target_type)) { VM_ASSERT(IS_OBJ(target_type), "target type must be ffi type"); Value sizeof_val = OBJECT_LOOKUP(AS_OBJ(target_type), sizeof); VM_ASSERT(IS_INT(sizeof_val), "internal error: sizeof wrong type or undefined"); elemsize = AS_INT(sizeof_val); } Value offset_val = load_arg(state->frame, INFO_ARGS_PTR(info)[0]); VM_ASSERT(IS_INT(offset_val), "offset must be integer"); int offset = AS_INT(offset_val); vm_return(state, info, make_ffi_pointer(state, (void*) ((char*)ptr + offset * elemsize))); } static Value make_ffi_pointer(VMState *state, void *ptr) { Object *ptr_obj = AS_OBJ(make_ptr(state, ptr)); OBJECT_SET(state, ptr_obj, dereference, make_fn(state, ffi_ptr_dereference)); OBJECT_SET(state, ptr_obj, dereference_assign, make_fn(state, ffi_ptr_dereference_assign)); OBJECT_SET(state, ptr_obj, __add, make_fn(state, ffi_ptr_add)); OBJECT_SET(state, ptr_obj, target_type, VNULL); OBJECT_SET(state, ptr_obj, __slice, make_fn(state, ffi_ptr_index_fn)); OBJECT_SET(state, ptr_obj, __slice_assign, make_fn(state, ffi_ptr_index_assign_fn)); return OBJ2VAL(ptr_obj);