static SLID sl_intern2_no_check(sl_vm_t* vm, SLVAL str) { SLID id; if(sl_st_lookup(vm->intern.name_to_id, (sl_st_data_t)sl_get_ptr(str), (sl_st_data_t*)&id)) { return id; } id.id = vm->intern.id_to_name_size++; sl_st_insert(vm->intern.name_to_id, (sl_st_data_t)sl_get_ptr(str), (sl_st_data_t)id.id); if(vm->intern.id_to_name_size >= vm->intern.id_to_name_cap) { vm->intern.id_to_name_cap *= 2; vm->intern.id_to_name = sl_realloc(vm->arena, vm->intern.id_to_name, sizeof(SLVAL) * vm->intern.id_to_name_cap); } vm->intern.id_to_name[id.id] = str; return id; }
static sl_node_base_t* lambda_expression(sl_parse_state_t* ps) { sl_node_base_t* body; sl_token_t* tok; size_t arg_count = 0, arg_cap = 2; sl_string_t** args = sl_alloc(ps->vm->arena, sizeof(sl_string_t*) * arg_cap); sl_parse_scope_t scope; expect_token(ps, SL_TOK_LAMBDA); if(peek_token(ps)->type == SL_TOK_IDENTIFIER) { tok = next_token(ps); args[arg_count++] = (sl_string_t*)sl_get_ptr( sl_make_string(ps->vm, tok->as.str.buff, tok->as.str.len)); } else if(peek_token(ps)->type != SL_TOK_OPEN_BRACE && peek_token(ps)->type != SL_TOK_DOT) { expect_token(ps, SL_TOK_OPEN_PAREN); while(peek_token(ps)->type != SL_TOK_CLOSE_PAREN) { if(arg_count >= arg_cap) { arg_cap *= 2; args = sl_realloc(ps->vm->arena, args, sizeof(sl_string_t*) * arg_cap); } tok = expect_token(ps, SL_TOK_IDENTIFIER); args[arg_count++] = (sl_string_t*)sl_get_ptr( sl_make_string(ps->vm, tok->as.str.buff, tok->as.str.len)); if(peek_token(ps)->type != SL_TOK_CLOSE_PAREN) { expect_token(ps, SL_TOK_COMMA); } } expect_token(ps, SL_TOK_CLOSE_PAREN); } scope.prev = ps->scope; scope.flags = SL_PF_CAN_RETURN; ps->scope = &scope; if(peek_token(ps)->type == SL_TOK_DOT) { next_token(ps); body = expression(ps); } else { body = body_expression(ps); } ps->scope = scope.prev; ps->scope->flags |= SL_PF_SCOPE_CLOSURE; return sl_make_lambda_node(ps, arg_count, args, body); }
SLVAL sl_regexp_match_index(sl_vm_t* vm, SLVAL self, SLVAL i) { sl_regexp_match_t* match = get_regexp_match(vm, self); int index = cap_index(vm, self, i); if(index < 0) { return vm->lib.nil; } sl_string_t* str = (sl_string_t*)sl_get_ptr(match->match_string); return sl_make_string(vm, str->buff + match->captures[index], match->captures[index + 1] - match->captures[index]); }
static sl_regexp_match_t* get_regexp_match(sl_vm_t* vm, SLVAL matchv) { sl_regexp_match_t* match; sl_expect(vm, matchv, vm->lib.Regexp_Match); match = (sl_regexp_match_t*)sl_get_ptr(matchv); if(!match->re) { sl_throw_message2(vm, vm->lib.TypeError, "Invalid Regexp::Match"); } return match; }
static SLVAL method_inspect(sl_vm_t* vm, SLVAL method) { sl_method_t* methp = (sl_method_t*)sl_get_ptr(method); if(!(methp->base.user_flags & SL_FLAG_METHOD_INITIALIZED)) { return sl_object_inspect(vm, method); } return sl_make_formatted_string(vm, "#<Method: %V#%I(%d)>", methp->extra->klass, methp->extra->name, methp->arity); }
SLVAL sl_method_bind(sl_vm_t* vm, SLVAL method, SLVAL receiver) { sl_method_t* methp = (sl_method_t*)sl_get_ptr(method); sl_bound_method_t* bmethp = (sl_bound_method_t*)sl_get_ptr(sl_allocate(vm, vm->lib.BoundMethod)); if(!methp->initialized) { sl_throw_message2(vm, vm->lib.TypeError, "Can't bind uninitialized Method"); } bmethp->method.initialized = 1; bmethp->method.name = methp->name; bmethp->method.klass = methp->klass; bmethp->method.is_c_func = methp->is_c_func; bmethp->method.arity = methp->arity; bmethp->method.as = methp->as; bmethp->self = sl_expect(vm, receiver, methp->klass); return sl_make_ptr((sl_object_t*)bmethp); }
SLVAL sl_method_bind(sl_vm_t* vm, SLVAL method, SLVAL receiver) { sl_method_t* methp = (sl_method_t*)sl_get_ptr(method); if(!(methp->base.user_flags & SL_FLAG_METHOD_INITIALIZED)) { sl_throw_message2(vm, vm->lib.TypeError, "Can't bind uninitialized Method"); } sl_method_t* bmethp = method_dup(vm, methp); bmethp->extra->bound_self = sl_expect(vm, receiver, methp->extra->klass); bmethp->base.klass = vm->lib.BoundMethod; return sl_make_ptr((sl_object_t*)bmethp); }
static SLVAL bound_method_unbind(sl_vm_t* vm, SLVAL bmethod) { sl_method_t* bmethp = (sl_method_t*)sl_get_ptr(bmethod); if(!(bmethp->base.user_flags & SL_FLAG_METHOD_INITIALIZED)) { sl_throw_message2(vm, vm->lib.TypeError, "Can't unbind uninitalized BoundMethod"); } sl_method_t* methp = method_dup(vm, bmethp); methp->extra->bound_self = vm->lib.nil; methp->base.klass = vm->lib.Method; return sl_make_ptr((sl_object_t*)methp); }
SLVAL sl_string_eq(sl_vm_t* vm, SLVAL self, SLVAL other) { if(!sl_is_a(vm, other, vm->lib.String)) { return vm->lib._false; } sl_string_t* a = sl_get_string(vm, self); sl_string_t* b = sl_get_string(vm, other); if(a->encoding == b->encoding) { if(str_cmp((sl_string_t*)sl_get_ptr(self), (sl_string_t*)sl_get_ptr(other)) == 0) { return vm->lib._true; } else { return vm->lib._false; } } sl_vm_frame_t frame; SLVAL err; volatile SLVAL retn; SL_TRY(frame, SL_UNWIND_EXCEPTION, { retn = sl_string_eq(vm, self, sl_string_encode(vm, other, a->encoding)); }, err, {
static SLVAL sl_class_own_instance_method(sl_vm_t* vm, SLVAL self, SLVAL method_name) { sl_class_t* klass = get_class(vm, self); SLVAL method; method_name = sl_to_s(vm, method_name); if(st_lookup(klass->instance_methods, (st_data_t)sl_get_ptr(method_name), (st_data_t*)&method)) { return method; } else { return vm->lib.nil; } }
SLVAL sl_response_flush(sl_vm_t* vm) { sl_response_internal_opts_t* resp = response(vm); size_t i, total_size = 0, offset = 0; char* output_buffer; sl_string_t* str; for(i = 0; i < resp->output_len; i++) { str = (sl_string_t*)sl_get_ptr(resp->output[i]); total_size += str->buff_len; } output_buffer = sl_alloc_buffer(vm->arena, total_size + 1); for(i = 0; i < resp->output_len; i++) { str = (sl_string_t*)sl_get_ptr(resp->output[i]); memcpy(output_buffer + offset, str->buff, str->buff_len); offset += str->buff_len; } resp->write(vm, output_buffer, total_size); resp->output_len = 0; return vm->lib.nil; }
SLVAL sl_make_c_func(sl_vm_t* vm, SLVAL klass, SLID name, int arity, SLVAL(*c_func)()) { SLVAL method = sl_allocate(vm, vm->lib.Method); sl_method_t* methp = (sl_method_t*)sl_get_ptr(method); methp->extra->name = name; methp->arity = arity; methp->extra->klass = sl_expect(vm, klass, vm->lib.Class); methp->as.c.func = c_func; methp->base.user_flags |= SL_FLAG_METHOD_INITIALIZED | SL_FLAG_METHOD_IS_C_FUNC; return method; }
SLVAL sl_make_c_func(sl_vm_t* vm, SLVAL klass, SLID name, int arity, SLVAL(*c_func)()) { SLVAL method = sl_allocate(vm, vm->lib.Method); sl_method_t* methp = (sl_method_t*)sl_get_ptr(method); methp->name = name; methp->is_c_func = 1; methp->arity = arity; methp->klass = sl_expect(vm, klass, vm->lib.Class); methp->as.c.func = c_func; methp->initialized = 1; return method; }
SLVAL sl_make_string_no_copy(sl_vm_t* vm, uint8_t* buff, size_t buff_len) { ensure_utf8(vm, buff, buff_len); SLVAL vstr = sl_allocate(vm, vm->lib.String); sl_string_t* str = (sl_string_t*)sl_get_ptr(vstr); str->char_len = sl_utf8_strlen(vm, buff, buff_len); str->buff = buff; str->buff_len = buff_len; str->hash_set = 0; return vstr; }
static SLVAL sl_class_instance_method(sl_vm_t* vm, SLVAL self, SLVAL method_name) { sl_class_t* klass = get_class(vm, self); SLVAL method; method_name = sl_to_s(vm, method_name); if(st_lookup(klass->instance_methods, (st_data_t)sl_get_ptr(method_name), (st_data_t*)&method)) { return method; } else if(sl_get_primitive_type(klass->super) == SL_T_CLASS) { return sl_class_instance_method(vm, klass->super, method_name); } return vm->lib.nil; }
void sl_init_nil(sl_vm_t* vm) { sl_object_t* nil = sl_get_ptr(vm->lib.nil); vm->lib.Nil = sl_define_class(vm, "Nil", vm->lib.Object); sl_class_set_allocator(vm, vm->lib.Nil, allocate_nil); nil->klass = vm->lib.Nil; nil->primitive_type = SL_T_NIL; nil->instance_variables = NULL; sl_define_method(vm, vm->lib.Nil, "to_s", 0, nil_to_s); sl_define_method(vm, vm->lib.Nil, "inspect", 0, nil_inspect); sl_define_method(vm, vm->lib.Nil, "==", 1, nil_eq); }
static sl_method_t* lookup_method_rec(sl_vm_t* vm, SLVAL klass, SLID id) { sl_class_t* klassp = (sl_class_t*)sl_get_ptr(klass); if(klassp->base.primitive_type == SL_T_NIL) { return NULL; } sl_method_t* method; if(sl_st_lookup(klassp->instance_methods, (sl_st_data_t)id.id, (sl_st_data_t*)&method)) { return method; } return lookup_method_rec(vm, klassp->super, id); }
SLVAL sl_make_method(sl_vm_t* vm, SLVAL klass, SLID name, sl_vm_section_t* section, sl_vm_exec_ctx_t* parent_ctx) { SLVAL method = sl_allocate(vm, vm->lib.Method); sl_method_t* methp = (sl_method_t*)sl_get_ptr(method); methp->name = name; methp->is_c_func = 0; if(section->req_registers < section->arg_registers) { methp->arity = -section->req_registers - 1; } else { methp->arity = (int)section->arg_registers; } methp->klass = sl_expect(vm, klass, vm->lib.Class); methp->as.sl.section = section; methp->as.sl.parent_ctx = parent_ctx; methp->initialized = 1; return method; }
SLVAL sl_make_method(sl_vm_t* vm, SLVAL klass, SLID name, sl_vm_section_t* section, sl_vm_exec_ctx_t* parent_ctx) { SLVAL method = sl_allocate(vm, vm->lib.Method); sl_method_t* methp = (sl_method_t*)sl_get_ptr(method); methp->extra->name = name; if(section->req_registers < section->arg_registers) { methp->arity = -section->req_registers - 1; } else if(section->has_extra_rest_arg) { methp->arity = -(int)section->req_registers - 1; } else { methp->arity = (int)section->req_registers; } methp->extra->klass = sl_expect(vm, klass, vm->lib.Class); methp->as.sl.section = section; methp->as.sl.parent_ctx = parent_ctx; methp->base.user_flags |= SL_FLAG_METHOD_INITIALIZED; return method; }
SLVAL sl_make_string(sl_vm_t* vm, uint8_t* buff, size_t buff_len) { SLVAL vstr = sl_allocate(vm, vm->lib.String); sl_string_t* str = (sl_string_t*)sl_get_ptr(vstr); if(sl_is_valid_utf8(buff, buff_len)) { str->encoding = "UTF-8"; str->char_len = sl_utf8_strlen(vm, buff, buff_len); } else { str->encoding = "CP1252"; str->char_len = buff_len; } str->buff = sl_alloc_buffer(vm->arena, buff_len + 1); memcpy(str->buff, buff, buff_len); str->buff[buff_len] = 0; str->buff_len = buff_len; str->hash_set = 0; return vstr; }
SLVAL sl_imc_setup_call(sl_vm_t* vm, sl_vm_inline_method_cache_t* imc, SLVAL recv, SLVAL* argv) { SLVAL klass = SL_IS_INT(recv) ? vm->lib.Int : sl_get_ptr(recv)->klass; sl_method_t* method = lookup_method_rec(vm, klass, imc->id); if(method) { imc->state = vm->state_method; imc->klass = klass; imc->method = method; if(method->base.user_flags & SL_FLAG_METHOD_IS_C_FUNC) { if(method->arity < 0) { if(sl_likely(-imc->argc - 1 <= method->arity)) { imc->call = dispatch_c_call_variadic; } else { imc->call = sl_imc_cached_call; // cop out } } else { if(sl_likely(imc->argc == method->arity)) { switch(method->arity) { case 0: imc->call = dispatch_c_call_0; break; case 1: imc->call = dispatch_c_call_1; break; case 2: imc->call = dispatch_c_call_2; break; case 3: imc->call = dispatch_c_call_3; break; case 4: imc->call = dispatch_c_call_4; break; default: sl_throw_message(vm, "Too many arguments for C function"); } } else { imc->call = sl_imc_cached_call; // cop out } } } else { if(method->arity >= 0 && imc->argc >= method->arity && method->as.sl.section->can_stack_alloc_frame && !method->as.sl.section->opt_skip) { imc->call = dispatch_slash_call_stack_regs; } else { imc->call = sl_imc_cached_call; // cop out } } return imc->call(vm, imc, recv, argv); } else { return sl_send_missing(vm, recv, imc->id, imc->argc, argv); } }
void sl_request_set_opts(sl_vm_t* vm, sl_request_opts_t* opts) { size_t i; SLVAL n, v, cookies; sl_string_t* str; sl_request_internal_opts_t* req = sl_alloc(vm->arena, sizeof(sl_request_internal_opts_t)); req->method = sl_make_cstring(vm, opts->method); req->uri = sl_make_cstring(vm, opts->uri); req->path_info = sl_make_cstring(vm, opts->path_info ? opts->path_info : ""); req->query_string = sl_make_cstring(vm, opts->query_string ? opts->query_string : ""); req->remote_addr = sl_make_cstring(vm, opts->remote_addr); req->headers = sl_make_dict(vm, 0, NULL); req->env = sl_make_dict(vm, 0, NULL); req->get = sl_make_dict(vm, 0, NULL); req->post = sl_make_dict(vm, 0, NULL); req->post_data = sl_make_string(vm, (uint8_t*)opts->post_data, opts->post_length); req->cookies = sl_make_dict(vm, 0, NULL); for(i = 0; i < opts->header_count; i++) { n = sl_make_cstring(vm, opts->headers[i].name); v = sl_make_cstring(vm, opts->headers[i].value); sl_dict_set(vm, req->headers, n, v); } for(i = 0; i < opts->env_count; i++) { n = sl_make_cstring(vm, opts->env[i].name); v = sl_make_cstring(vm, opts->env[i].value); sl_dict_set(vm, req->env, n, v); } if(opts->query_string) { parse_query_string(vm, req->get, strlen(opts->query_string), (uint8_t*)opts->query_string); } if(opts->content_type && strcmp(opts->content_type, "application/x-www-form-urlencoded") == 0) { parse_query_string(vm, req->post, opts->post_length, (uint8_t*)opts->post_data); } cookies = sl_dict_get(vm, req->headers, sl_make_cstring(vm, "Cookie")); if(sl_is_a(vm, cookies, vm->lib.String)) { str = (sl_string_t*)sl_get_ptr(cookies); parse_cookie_string(vm, req->cookies, str->buff_len, str->buff); } req->params = sl_dict_merge(vm, req->get, req->post); sl_vm_store_put(vm, &Request_opts, sl_make_ptr((sl_object_t*)req)); }
SLVAL sl_regexp_match(sl_vm_t* vm, SLVAL self, size_t argc, SLVAL* argv) { sl_regexp_t* re = get_regexp_check(vm, self); sl_string_t* str = sl_get_string(vm, argv[0]); int offset = 0, rc, ncaps; int* caps; char err_buff[256]; sl_regexp_match_t* match; if(argc > 1) { offset = sl_get_int(sl_expect(vm, argv[1], vm->lib.Int)); } offset = sl_string_byte_offset_for_index(vm, argv[0], offset); if(offset < 0) { return vm->lib.nil; } pcre_fullinfo(re->re, re->study, PCRE_INFO_CAPTURECOUNT, &ncaps); ncaps += 1; ncaps *= 3; caps = sl_alloc(vm->arena, sizeof(int) * ncaps); rc = pcre_exec(re->re, re->study, (char*)str->buff, str->buff_len, offset, PCRE_NEWLINE_LF, caps, ncaps); if(rc < 0) { if(rc == PCRE_ERROR_NOMATCH) { return vm->lib.nil; } if(rc == PCRE_ERROR_BADUTF8) { sl_throw_message2(vm, vm->lib.EncodingError, "Invalid UTF-8 in regular expression or match text"); } sprintf(err_buff, "PCRE error (%d)", rc); sl_throw_message2(vm, vm->lib.Error, err_buff); } match = (sl_regexp_match_t*)sl_get_ptr(sl_allocate(vm, vm->lib.Regexp_Match)); match->re = re; match->match_string = argv[0]; match->capture_count = ncaps / 3; match->captures = caps; return sl_make_ptr((sl_object_t*)match); }
static SLVAL sl_class_instance_method(sl_vm_t* vm, SLVAL self, SLVAL method_name) { sl_class_t* klass = get_class(vm, self); SLVAL method; SLID mid = sl_intern2(vm, method_name); method_name = sl_to_s(vm, method_name); if(sl_st_lookup(klass->instance_methods, (sl_st_data_t)mid.id, (sl_st_data_t*)&method)) { if(sl_get_primitive_type(method) == SL_T_CACHED_METHOD_ENTRY) { sl_cached_method_entry_t* cme = (void*)sl_get_ptr(method); // TODO - improve cache invalidation. this is too coarse if(cme->state == vm->state_method) { return sl_make_ptr((sl_object_t*)cme->method); } } else { return method; } } if(sl_get_primitive_type(klass->super) == SL_T_CLASS) { return sl_class_instance_method(vm, klass->super, method_name); } return vm->lib.nil; }
static SLVAL response_write(sl_vm_t* vm, SLVAL self, size_t argc, SLVAL* argv) { sl_response_internal_opts_t* resp = response(vm); size_t i; sl_string_t* str; (void)self; if(resp->buffered) { for(i = 0; i < argc; i++) { if(resp->output_len >= resp->output_cap) { resp->output_cap *= 2; resp->output = sl_realloc(vm->arena, resp->output, sizeof(SLVAL) * resp->output_cap); } resp->output[resp->output_len++] = sl_to_s(vm, argv[i]); } } else { for(i = 0; i < argc; i++) { str = (sl_string_t*)sl_get_ptr(sl_to_s(vm, argv[i])); resp->write(vm, (char*)str->buff, str->buff_len); } } return vm->lib.nil; }
static sl_range_enumerator_t* get_range_enumerator(sl_vm_t* vm, SLVAL obj) { sl_expect(vm, obj, vm->lib.Range_Enumerator); return (sl_range_enumerator_t*)sl_get_ptr(obj); }
static sl_range_t* get_range(sl_vm_t* vm, SLVAL obj) { sl_expect(vm, obj, vm->lib.Range); return (sl_range_t*)sl_get_ptr(obj); }
static sl_float_t* get_float(sl_vm_t* vm, SLVAL val) { sl_expect(vm, val, vm->lib.Float); return (sl_float_t*)sl_get_ptr(val); }
static sl_class_t* get_class(sl_vm_t* vm, SLVAL klass) { sl_expect(vm, klass, vm->lib.Class); return (sl_class_t*)sl_get_ptr(klass); }
static sl_node_base_t* def_expression(sl_parse_state_t* ps) { SLID name; sl_node_base_t* on = NULL; sl_node_base_t* body; sl_token_t* tok; size_t req_arg_count = 0, req_arg_cap = 2; sl_string_t** req_args = sl_alloc(ps->vm->arena, sizeof(sl_string_t*) * req_arg_cap); size_t opt_arg_count = 0, opt_arg_cap = 2; sl_node_opt_arg_t* opt_args = sl_alloc(ps->vm->arena, sizeof(sl_node_opt_arg_t) * opt_arg_cap); sl_parse_scope_t scope; expect_token(ps, SL_TOK_DEF); switch(peek_token(ps)->type) { case SL_TOK_IDENTIFIER: if(peek_token_n(ps, 2)->type == SL_TOK_DOT) { on = sl_make_var_node(ps, SL_NODE_VAR, next_token(ps)->str); next_token(ps); name = def_expression_method_name(ps); } else { on = NULL; name = def_expression_method_name(ps); } break; case SL_TOK_SELF: case SL_TOK_IVAR: case SL_TOK_CVAR: case SL_TOK_CONSTANT: on = primary_expression(ps); expect_token(ps, SL_TOK_DOT); name = def_expression_method_name(ps); break; default: name = def_expression_method_name(ps); } if(peek_token(ps)->type == SL_TOK_EQUALS) { next_token(ps); name = sl_intern2(ps->vm, sl_string_concat(ps->vm, sl_id_to_string(ps->vm, name), sl_make_cstring(ps->vm, "="))); } int at_opt_args = 0; if(peek_token(ps)->type != SL_TOK_OPEN_BRACE) { expect_token(ps, SL_TOK_OPEN_PAREN); if(peek_token(ps)->type == SL_TOK_SELF) { error(ps, sl_make_cstring(ps->vm, "not a chance"), peek_token(ps)); } while(peek_token(ps)->type != SL_TOK_CLOSE_PAREN) { tok = expect_token(ps, SL_TOK_IDENTIFIER); if(peek_token(ps)->type == SL_TOK_EQUALS) { at_opt_args = 1; } if(at_opt_args) { expect_token(ps, SL_TOK_EQUALS); if(opt_arg_count >= opt_arg_cap) { opt_arg_cap *= 2; opt_args = sl_realloc(ps->vm->arena, opt_args, sizeof(sl_node_opt_arg_t) * opt_arg_cap); } opt_args[opt_arg_count].name = (sl_string_t*)sl_get_ptr( sl_make_string(ps->vm, tok->as.str.buff, tok->as.str.len)); opt_args[opt_arg_count++].default_value = expression(ps); } else { if(req_arg_count >= req_arg_cap) { req_arg_cap *= 2; req_args = sl_realloc(ps->vm->arena, req_args, sizeof(sl_string_t*) * req_arg_cap); } req_args[req_arg_count++] = (sl_string_t*)sl_get_ptr( sl_make_string(ps->vm, tok->as.str.buff, tok->as.str.len)); } if(peek_token(ps)->type != SL_TOK_CLOSE_PAREN) { expect_token(ps, SL_TOK_COMMA); } } expect_token(ps, SL_TOK_CLOSE_PAREN); } scope.prev = ps->scope; scope.flags = SL_PF_CAN_RETURN; ps->scope = &scope; body = body_expression(ps); ps->scope = scope.prev; ps->scope->flags |= SL_PF_SCOPE_CLOSURE; return sl_make_def_node(ps, name, on, req_arg_count, req_args, opt_arg_count, opt_args, body); }