Array* Array::concat(STATE, Array* other) { if(!LANGUAGE_18_ENABLED(state)) { if(is_frozen_p()) return force_as<Array>(Primitives::failure()); } native_int osize = other->size(); if(osize == 0) return this; if(LANGUAGE_18_ENABLED(state)) { if(is_frozen_p()) return force_as<Array>(Primitives::failure()); } if(osize == 1) { set(state, size(), other->get(state, 0)); return this; } native_int new_size = size() + osize; Tuple* nt = Tuple::create(state, new_size); nt->copy_from(state, tuple_, start_, total_, Fixnum::from(0)); nt->copy_from(state, other->tuple(), other->start(), other->total(), total_); tuple(state, nt); start(state, Fixnum::from(0)); total(state, Fixnum::from(new_size)); return this; }
Time* Time::specific(STATE, Object* self, Integer* sec, Integer* nsec, Object* gmt) { Time* tm = state->new_object<Time>(as<Class>(self)); if(sizeof(time_t) == sizeof(long long)) { tm->seconds_ = sec->to_long_long(); tm->nanoseconds_ = nsec->to_long_long(); } else { tm->seconds_ = sec->to_native(); tm->nanoseconds_ = nsec->to_native(); } // Do a little overflow cleanup. if(tm->nanoseconds_ >= 1000000000) { tm->seconds_ += tm->nanoseconds_ / 1000000000; tm->nanoseconds_ %= 1000000000; } if(tm->nanoseconds_ < 0) { tm->seconds_ += NDIV(tm->nanoseconds_, 1000000000); tm->nanoseconds_ = NMOD(tm->nanoseconds_, 1000000000); } if(LANGUAGE_18_ENABLED(state)) { tm->nanoseconds_ -= (tm->nanoseconds_ % 1000); } tm->is_gmt(state, CBOOL(gmt) ? cTrue : cFalse); return tm; }
Symbol* SymbolTable::lookup(STATE, const char* str, size_t length) { if(length == 0 && LANGUAGE_18_ENABLED(state)) { Exception::argument_error(state, "Cannot create a symbol from an empty string"); return NULL; } return lookup(str, length); }
void test_object() { TS_ASSERT_EQUALS(G(object)->class_object(state), G(klass)); if(!LANGUAGE_18_ENABLED(state)) { TS_ASSERT_EQUALS(G(object)->superclass(), G(basicobject)); } else { TS_ASSERT_EQUALS(G(object)->superclass(), cNil); } check_const(object, "Object"); }
String* Module::get_name(STATE) { if(module_name()->nil_p()) { if(LANGUAGE_18_ENABLED(state)) { return String::create(state, ""); } else { return nil<String>(); } } else { return module_name()->to_str(state); } }
Fixnum* Regexp::options(STATE) { if(unlikely(!onig_data)) { Exception::argument_error(state, "Not properly initialized Regexp"); } int result = ((int)onig_get_options(onig_data) & OPTION_MASK); if(LANGUAGE_18_ENABLED(state)) { if(fixed_encoding_) { result |= get_kcode_from_enc(onig_get_encoding(onig_data)); } } else { if(fixed_encoding_) { result |= OPTION_FIXEDENCODING; } if(no_encoding_) { result |= OPTION_NOENCODING; } } return Fixnum::from(result); }
Symbol* SymbolTable::lookup(STATE, String* str) { if(str->nil_p()) { Exception::argument_error(state, "Cannot look up Symbol from nil"); return NULL; } // Since we also explicitly use the size, we can safely // use byte_address() here. const char* bytes = (const char*) str->byte_address(); size_t size = str->byte_size(); if(LANGUAGE_18_ENABLED(state)) { for(size_t i = 0; i < size; i++) { if(bytes[i] == 0) { Exception::argument_error(state, "cannot create a symbol from a string containing `\\0'"); return NULL; } } } return lookup(state, bytes, size); }
Object* Proc::call(STATE, CallFrame* call_frame, Arguments& args) { bool lambda_style = RTEST(lambda_); int flags = 0; // Check the arity in lambda mode if(lambda_style) { flags = CallFrame::cIsLambda; int total = block_->code()->total_args()->to_native(); int required = block_->code()->required_args()->to_native(); bool arity_ok = false; if(Fixnum* fix = try_as<Fixnum>(block_->code()->splat())) { switch(fix->to_native()) { case -2: arity_ok = true; break; case -4: // splat = -4 means { |(a, b)| } if(args.total() == 1) { Array* ary = 0; Object* obj = args.get_argument(0); if(!(ary = try_as<Array>(obj))) { if(RTEST(obj->respond_to(state, state->symbol("to_ary"), Qfalse))) { obj = obj->send(state, call_frame, state->symbol("to_ary")); if(!(ary = try_as<Array>(obj))) { Exception::type_error(state, "to_ary must return an Array", call_frame); return 0; } } } if(ary) args.use_argument(ary); } // fall through for arity check case -3: // splat = -3 is used to distinguish { |a, | } from { |a| } if(args.total() == (size_t)required) arity_ok = true; break; default: if(args.total() >= (size_t)required) { arity_ok = true; } } /* For blocks taking one argument { |a| }, in 1.8, there is a warning * issued but no exception raised when less than or more than one * argument is passed. If more than one is passed, 'a' receives an Array * of all the arguments. */ } else if(required == 1 && LANGUAGE_18_ENABLED(state)) { arity_ok = true; } else { arity_ok = args.total() <= (size_t)total && args.total() >= (size_t)required; } if(!arity_ok) { Exception* exc = Exception::make_argument_error(state, required, args.total(), state->symbol("__block__")); exc->locations(state, Location::from_call_stack(state, call_frame)); state->thread_state()->raise_exception(exc); return NULL; } } Object* ret; if(bound_method_->nil_p()) { ret = block_->call(state, call_frame, args, flags); } else if(NativeMethod* nm = try_as<NativeMethod>(bound_method_)) { ret = nm->execute(state, call_frame, nm, G(object), args); } else { Dispatch dis(state->symbol("__yield__")); ret = dis.send(state, call_frame, args); } return ret; }
/* * This is a primitive so #initialize_copy can work. */ Regexp* Regexp::initialize(STATE, String* pattern, Fixnum* options) { const UChar *pat; const UChar *end; OnigErrorInfo err_info; OnigEncoding enc; OnigOptionType opts = options->to_native(); if(LANGUAGE_18_ENABLED(state)) { int kcode = opts & KCODE_MASK; pat = (UChar*)pattern->byte_address(); end = pat + pattern->byte_size(); if(kcode == 0) { enc = pattern->get_encoding_kcode_fallback(state); } else { // Don't attempt to fix the encoding later, it's been specified by the // user. enc = get_enc_from_kcode(kcode); fixed_encoding_ = true; } } else { fixed_encoding_ = opts & OPTION_FIXEDENCODING; no_encoding_ = opts & OPTION_NOENCODING; Encoding* source_enc = pattern->encoding(state); switch(opts & KCODE_MASK) { case KCODE_NONE: source_enc = 0; no_encoding_ = true; break; case KCODE_EUC: source_enc = Encoding::find(state, "EUC-JP"); fixed_encoding_ = true; break; case KCODE_SJIS: source_enc = Encoding::find(state, "Windows-31J"); fixed_encoding_ = true; break; case KCODE_UTF8: source_enc = Encoding::utf8_encoding(state); fixed_encoding_ = true; break; } String* converted = pattern->convert_escaped(state, source_enc, fixed_encoding_); pat = (UChar*)converted->byte_address(); end = pat + converted->byte_size(); enc = source_enc->get_encoding(); pattern = pattern->string_dup(state); pattern->encoding(state, source_enc); } utilities::thread::Mutex::LockGuard lg(state->shared().onig_lock()); int err = onig_new(&this->onig_data, pat, end, opts & OPTION_MASK, enc, ONIG_SYNTAX_RUBY, &err_info); if(err != ONIG_NORMAL) { UChar onig_err_buf[ONIG_MAX_ERROR_MESSAGE_LEN]; char err_buf[1024]; onig_error_code_to_str(onig_err_buf, err, &err_info); snprintf(err_buf, 1024, "%s: %s", onig_err_buf, pat); Exception::regexp_error(state, err_buf); return 0; } this->source(state, pattern); int num_names = onig_number_of_names(this->onig_data); if(num_names == 0) { this->names(state, nil<LookupTable>()); } else { struct _gather_data gd; gd.state = state; LookupTable* tbl = LookupTable::create(state); gd.tbl = tbl; onig_foreach_name(this->onig_data, (int (*)(const OnigUChar*, const OnigUChar*,int,int*,OnigRegex,void*))_gather_names, (void*)&gd); this->names(state, tbl); } make_managed(state); return this; }
// Installed by default in BlockEnvironment::execute, it runs the bytecodes // for the block in the interpreter. // // Future code will detect hot blocks and queue them in the JIT, whereby the // JIT will install a newly minted machine function into ::execute. Object* BlockEnvironment::execute_interpreter(STATE, CallFrame* previous, BlockEnvironment* env, Arguments& args, BlockInvocation& invocation) { // Don't use env->machine_code() because it might lock and the work should // already be done. MachineCode* const mcode = env->compiled_code_->machine_code(); if(!mcode) { Exception::internal_error(state, previous, "invalid bytecode method"); return 0; } #ifdef ENABLE_LLVM if(mcode->call_count >= 0) { if(mcode->call_count >= state->shared().config.jit_call_til_compile) { LLVMState* ls = LLVMState::get(state); GCTokenImpl gct; OnStack<1> os(state, env); ls->compile_soon(state, gct, env->compiled_code(), previous, invocation.self->lookup_begin(state), env, true); } else { mcode->call_count++; } } #endif StackVariables* scope = ALLOCA_STACKVARIABLES(mcode->number_of_locals); Module* mod = invocation.module; if(!mod) mod = env->module(); scope->initialize(invocation.self, env->top_scope_->block(), mod, mcode->number_of_locals); scope->set_parent(env->scope_); InterpreterCallFrame* frame = ALLOCA_CALLFRAME(mcode->stack_size); frame->prepare(mcode->stack_size); frame->previous = previous; frame->constant_scope_ = invocation.constant_scope; frame->arguments = &args; frame->dispatch_data = env; frame->compiled_code = env->compiled_code_; frame->scope = scope; frame->top_scope_ = env->top_scope_; frame->flags = invocation.flags | CallFrame::cCustomConstantScope | CallFrame::cMultipleScopes | CallFrame::cBlock; // TODO: this is a quick hack to process block arguments in 1.9. if(!LANGUAGE_18_ENABLED(state)) { if(!GenericArguments::call(state, frame, mcode, scope, args, invocation.flags)) { return NULL; } } #ifdef RBX_PROFILER if(unlikely(state->vm()->tooling())) { Module* mod = scope->module(); if(SingletonClass* sc = try_as<SingletonClass>(mod)) { if(Module* ma = try_as<Module>(sc->attached_instance())) { mod = ma; } } OnStack<2> os(state, env, mod); // Check the stack and interrupts here rather than in the interpreter // loop itself. GCTokenImpl gct; if(!state->check_interrupts(gct, frame, frame)) return NULL; state->checkpoint(gct, frame); tooling::BlockEntry method(state, env, mod); return (*mcode->run)(state, mcode, frame); } else { // Check the stack and interrupts here rather than in the interpreter // loop itself. GCTokenImpl gct; if(!state->check_interrupts(gct, frame, frame)) return NULL; state->checkpoint(gct, frame); return (*mcode->run)(state, mcode, frame); } #else // Check the stack and interrupts here rather than in the interpreter // loop itself. GCTokenImpl gct; if(!state->check_interrupts(gct, frame, frame)) return NULL; state->checkpoint(gct, frame); return (*mcode->run)(state, mcode, frame); #endif }
// Installed by default in BlockEnvironment::execute, it runs the bytecodes // for the block in the interpreter. // // Future code will detect hot blocks and queue them in the JIT, whereby the // JIT will install a newly minted machine function into ::execute. Object* BlockEnvironment::execute_interpreter(STATE, CallFrame* previous, BlockEnvironment* env, Arguments& args, BlockInvocation& invocation) { // Don't use env->vmmethod() because it mighc lock and the work should already // be done. VMMethod* const vmm = env->code_->backend_method(); if(!vmm) { Exception::internal_error(state, previous, "invalid bytecode method"); return 0; } #ifdef ENABLE_LLVM if(vmm->call_count >= 0) { if(vmm->call_count >= state->shared().config.jit_call_til_compile) { LLVMState* ls = LLVMState::get(state); ls->compile_soon(state, env->code(), env, true); } else { vmm->call_count++; } } #endif size_t scope_size = sizeof(StackVariables) + (vmm->number_of_locals * sizeof(Object*)); StackVariables* scope = reinterpret_cast<StackVariables*>(alloca(scope_size)); Module* mod = invocation.module; if(!mod) mod = env->module(); scope->initialize(invocation.self, env->top_scope_->block(), mod, vmm->number_of_locals); scope->set_parent(env->scope_); InterpreterCallFrame* frame = ALLOCA_CALLFRAME(vmm->stack_size); frame->prepare(vmm->stack_size); frame->previous = previous; frame->static_scope_ = invocation.static_scope; frame->arguments = &args; frame->dispatch_data = reinterpret_cast<BlockEnvironment*>(env); frame->cm = env->code_; frame->scope = scope; frame->top_scope_ = env->top_scope_; frame->flags = invocation.flags | CallFrame::cCustomStaticScope | CallFrame::cMultipleScopes | CallFrame::cBlock; frame->stack_top_ptr_ptr = NULL; // TODO: this is a quick hack to process block arguments in 1.9. if(!LANGUAGE_18_ENABLED(state)) { if(!GenericArguments::call(state, frame, vmm, scope, args, invocation.flags)) { return NULL; } } // Check the stack and interrupts here rather than in the interpreter // loop itself. GCTokenImpl gct; if(state->detect_stack_condition(frame)) { if(!state->check_interrupts(gct, frame, frame)) return NULL; } state->checkpoint(gct, frame); #ifdef RBX_PROFILER if(unlikely(state->vm()->tooling())) { Module* mod = scope->module(); if(SingletonClass* sc = try_as<SingletonClass>(mod)) { if(Module* ma = try_as<Module>(sc->attached_instance())) { mod = ma; } } tooling::BlockEntry method(state, env, mod); return (*vmm->run)(state, vmm, frame); } else { return (*vmm->run)(state, vmm, frame); } #else return (*vmm->run)(state, vmm, frame); #endif }
Integer* Integer::from_cstr(STATE, const char* str, int base, Object* strict) { bool negative = false; Integer* value = Fixnum::from(0); if(base == 1 || base > 36) return nil<Integer>(); // Skip any combination of leading whitespace and underscores. // Leading whitespace is OK in strict mode, but underscores are not. while(isspace(*str) || *str == '_') { if(*str == '_') { if(strict == cTrue) { return nil<Integer>(); } else if(!LANGUAGE_18_ENABLED(state)) { return value; } } str++; } if(*str == '-') { str++; negative = true; } else if(*str == '+') { str++; } char chr; int detected_base = 0; const char* str_start = str; // Try and detect a base prefix on the front. We have to do this // even though we might have been told the base, because we have // to know if we should discard the bytes that make up the prefix // if it's redundant with passed in base. // // For example, if base == 16 and str == "0xa", we return // to return 10. But if base == 10 and str == "0xa", we fail // because we rewind and try to process 0x as part of the // base 10 string. // if(*str == '0') { str++; switch(chr = *str++) { case 'b': case 'B': detected_base = 2; break; case 'o': case 'O': detected_base = 8; break; case 'd': case 'D': detected_base = 10; break; case 'x': case 'X': detected_base = 16; break; default: // If passed "017" and a base of 0, that is octal 15. // Otherwise, it is whatever those digits would be in the // specified base. str--; detected_base = 8; } } // If base is less than 0, then it's just a hint for how to process it // if there is no base detected. if(base < 0) { if(detected_base == 0) { // Ok, no detected because, use the base hint and start over. base = -base; str = str_start; } else { base = detected_base; } // If 0 was passed in as the base, we use the detected base. } else if(base == 0) { // Default to 10 if there is no input and no detected base. if(detected_base == 0) { base = 10; str = str_start; } else { base = detected_base; } // If the passed in base and the detected base contradict // each other, then rewind and process the whole string as // digits of the passed in base. } else if(base != detected_base) { // rewind the stream, and try and consume the prefix as // digits in the number. str = str_start; } bool underscore = false; while(*str) { chr = *str++; // If we see space characters if(chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r') { // Eat them all while(chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r') { chr = *str++; } // If there is more stuff after the spaces, get out of dodge. if(chr) { if(strict == cTrue) { return nil<Integer>(); } else { goto return_value; } } break; } // If it's an underscore, remember that. An underscore is valid iff // it followed by a valid character for this base. if(chr == '_') { if(underscore) { // Double underscore is forbidden in strict mode. if(strict == cTrue) { return nil<Integer>(); } else { // Stop parse number after two underscores in a row goto return_value; } } underscore = true; continue; } else { underscore = false; } // We use A-Z (and a-z) here so we support up to base 36. if(chr >= '0' && chr <= '9') { chr -= '0'; } else if(chr >= 'A' && chr <= 'Z') { chr -= ('A' - 10); } else if(chr >= 'a' && chr <= 'z') { chr -= ('a' - 10); } else { //Invalid character, stopping right here. if(strict == cTrue) { return nil<Integer>(); } else { break; } } // Bail if the current chr is greater or equal to the base, // mean it's invalid. if(chr >= base) { if(strict == cTrue) { return nil<Integer>(); } else { break; } } if(value != Fixnum::from(0)) { if(Fixnum *fix = try_as<Fixnum>(value)) { value = fix->mul(state, Fixnum::from(base)); } else { value = as<Bignum>(value)->mul(state, Fixnum::from(base)); } } if(Fixnum *fix = try_as<Fixnum>(value)) { value = fix->add(state, Fixnum::from(chr)); } else { value = as<Bignum>(value)->add(state, Fixnum::from(chr)); } } // If we last saw an underscore and we're strict, bail. if(underscore && strict == cTrue) { return nil<Integer>(); } return_value: if(negative) { if(Fixnum* fix = try_as<Fixnum>(value)) { value = fix->neg(state); } else { value = as<Bignum>(value)->neg(state); } } return value; }
Object* BlockAsMethod::block_executor(STATE, CallFrame* call_frame, Executable* exec, Module* mod, Arguments& args) { BlockAsMethod* bm = as<BlockAsMethod>(exec); Fixnum* splat = bm->block_env()->compiled_code()->splat(); size_t required = bm->block_env()->compiled_code()->required_args()->to_native(); size_t total_args = bm->block_env()->compiled_code()->total_args()->to_native(); /* * These are the block shapes, required args, and splat that we may see, * along with the arity check that we must perform: * * block shape required args splat check * -------------|---------------|-------|------- * { || } | 0 | nil | == * { } | 0 | -2 | none * { |a| } | 1 | nil | none (1.8), == (>= 1.9) * { |*a| } | 0 | 0 | none * { |a, b| } | 2 | nil | == * { |a, *b| } | 1 | 1 | >= * * NOTE that when taking one argument, any arguments passed are put * into an array and the local gets the array (or an empty array if * no arguments are passed). This is handled by the bytecode prologue * of the block. */ bool exception = false; size_t expected = 0; if(splat->nil_p()) { if((!LANGUAGE_18_ENABLED(state) || required != 1)) { if(args.total() > total_args) { exception = true; expected = total_args; } if(args.total() < required) { exception = true; expected = required; } } } else { if(required > args.total()) { exception = true; expected = required; } } if(exception) { Exception* exc = Exception::make_argument_error(state, expected, args.total(), args.name()); exc->locations(state, Location::from_call_stack(state, call_frame)); state->raise_exception(exc); return NULL; } BlockInvocation invocation(args.recv(), bm->block_env()->compiled_code()->scope(), CallFrame::cIsLambda | CallFrame::cBlockAsMethod); invocation.module = mod; return bm->block_env()->invoke(state, call_frame, bm->block_env(), args, invocation); }