String* MatchData::matched_string(STATE) { Fixnum* beg = try_as<Fixnum>(full_->at(state, 0)); Fixnum* fin = try_as<Fixnum>(full_->at(state, 1)); native_int max = source_->byte_size(); native_int f = fin->to_native(); native_int b = beg->to_native(); String* string; if(!beg || !fin || f > max || b > max || b < 0) { string = String::create(state, 0, 0); } else { const char* str = (char*)source_->byte_address(); native_int sz = fin->to_native() - beg->to_native(); string = String::create(state, str + beg->to_native(), sz); } source_->infect(state, string); string->encoding_from(state, source_); string->klass(state, source_->class_object(state)); return string; }
Object* MatchData::nth_capture(STATE, native_int which) { if(region_->num_fields() <= which) return cNil; Tuple* sub = try_as<Tuple>(region_->at(state, which)); if(!sub) return cNil; Fixnum* beg = try_as<Fixnum>(sub->at(state, 0)); Fixnum* fin = try_as<Fixnum>(sub->at(state, 1)); native_int b = beg->to_native(); native_int f = fin->to_native(); native_int max = source_->byte_size(); if(!beg || !fin || f > max || b < 0) { return cNil; } const char* str = (char*)source_->byte_address(); native_int sz = f - b; if(sz > max) sz = max; String* string = String::create(state, str + b, sz); string->encoding(state, source_->encoding()); return string; }
String* MatchData::pre_matched(STATE) { Fixnum* beg = try_as<Fixnum>(full_->at(state, 0)); if(!beg || beg->to_native() <= 0) { return String::create(state, 0, 0); } const char* str = source_->c_str(); native_int sz = beg->to_native(); return String::create(state, str, sz); }
String* MatchData::post_matched(STATE) { Fixnum* fin = try_as<Fixnum>(full_->at(state, 1)); if(!fin || fin->to_native() >= source_->size()) { return String::create(state, 0, 0); } const char* str = source_->c_str(); native_int sz = (native_int)source_->size() - fin->to_native(); return String::create(state, str + fin->to_native(), sz); }
String* MatchData::matched_string(STATE) { Fixnum* beg = try_as<Fixnum>(full_->at(state, 0)); Fixnum* fin = try_as<Fixnum>(full_->at(state, 1)); if(!beg || !fin || fin->to_native() > source_->size() || beg->to_native() < 0) { return String::create(state, 0, 0); } const char* str = source_->c_str(); native_int sz = fin->to_native() - beg->to_native(); return String::create(state, str + beg->to_native(), sz); }
void test_add() { Fixnum* one = Fixnum::from(1); Fixnum* two = as<Fixnum>(one->add(state, one)); TS_ASSERT_EQUALS(Fixnum::from(2), two); TS_ASSERT_EQUALS(2, two->to_native()); }
void test_sub() { Fixnum* one = Fixnum::from(1); Fixnum* zero = as<Fixnum>(one->sub(state, one)); TS_ASSERT_EQUALS(0, zero->to_native()); TS_ASSERT_EQUALS(Fixnum::from(0), zero); }
String* MatchData::post_matched(STATE) { Fixnum* fin = try_as<Fixnum>(full_->at(state, 1)); native_int f = fin->to_native(); native_int max = source_->byte_size(); String* string; if(!fin || f >= max) { string = String::create(state, 0, 0); } else { const char* str = (char*)source_->byte_address(); native_int sz = max - f; if(sz > max) sz = max; string = String::create(state, str + f, sz); } source_->infect(state, string); string->encoding_from(state, source_); string->klass(state, source_->class_object(state)); return string; }
Object* PackedObject::packed_ivar_delete(STATE, Symbol* sym, bool* removed) { LookupTable* tbl = this->reference_class()->packed_ivar_info(); bool found = false; Fixnum* which = try_as<Fixnum>(tbl->fetch(state, sym, &found)); if(!found) { return del_table_ivar(state, sym, removed); } if(removed) *removed = true; Object* val = body_as_array()[which->to_native()]; body_as_array()[which->to_native()] = Qundef; return val; }
String* MatchData::matched_string(STATE) { Fixnum* beg = try_as<Fixnum>(full_->at(state, 0)); Fixnum* fin = try_as<Fixnum>(full_->at(state, 1)); native_int max = source_->size(); native_int f = fin->to_native(); native_int b = beg->to_native(); if(!beg || !fin || f > max || b > max || b < 0) { return String::create(state, 0, 0); } const char* str = (char*)source_->byte_address(); native_int sz = fin->to_native() - beg->to_native(); return String::create(state, str + beg->to_native(), sz); }
void test_div() { Fixnum* one = as<Fixnum>(Fixnum::from(4)); Fixnum* two = as<Fixnum>(one->div(state, one)); TS_ASSERT_EQUALS(two->to_native(), 1); Fixnum* zero = Fixnum::from(0); TS_ASSERT_THROWS_ASSERT(one->div(state, zero), const RubyException &e, TS_ASSERT(Exception::zero_division_error_p(state, e.exception))); }
/** @todo Is it a valid assumption that the position always increases? */ void test_control_tells_current_position() { char *dir = make_directory(); String* path = String::create(state, dir); d->open(state, path); Fixnum* pos = (Fixnum*)d->control(state, Fixnum::from(2), Fixnum::from(0)); d->read(state); Fixnum* pos2 = (Fixnum*)d->control(state, Fixnum::from(2), Fixnum::from(0)); TS_ASSERT_LESS_THAN(pos->to_native(), pos2->to_native()); remove_directory(dir); }
Object* MatchData::nth_capture(STATE, native_int which) { if(region_->num_fields() <= which) return Qnil; Tuple* sub = try_as<Tuple>(region_->at(state, which)); if(!sub) return Qnil; Fixnum* beg = try_as<Fixnum>(sub->at(state, 0)); Fixnum* fin = try_as<Fixnum>(sub->at(state, 1)); if(!beg || !fin || fin->to_native() > source_->size() || beg->to_native() < 0) { return Qnil; } const char* str = source_->c_str(); native_int sz = fin->to_native() - beg->to_native(); return String::create(state, str + beg->to_native(), sz); }
Object* PackedObject::get_packed_ivar(STATE, Symbol* sym) { LookupTable* tbl = this->reference_class()->packed_ivar_info(); assert(tbl && !tbl->nil_p()); Fixnum* which = try_as<Fixnum>(tbl->fetch(state, sym)); if(!which) { return get_table_ivar(state, sym); } Object* obj = body_as_array()[which->to_native()]; if(obj == Qundef) return Qnil; return obj; }
virtual Object* immediate() { native_int id = id_->to_native(); if(id & TAG_REF_MASK) { Object* obj = reinterpret_cast<Object*>(id); // Be sure to not leak a bad reference leak out here. if(obj->reference_p()) return cNil; return obj; } return 0; }
Object* PackedObject::packed_ivar_defined(STATE, Symbol* sym) { LookupTable* tbl = this->reference_class()->packed_ivar_info(); bool found = false; Fixnum* which = try_as<Fixnum>(tbl->fetch(state, sym, &found)); if(!found) { return table_ivar_defined(state, sym); } Object* obj = body_as_array()[which->to_native()]; if(obj == Qundef) return Qfalse; return Qtrue; }
Object* PackedObject::set_packed_ivar(STATE, Symbol* sym, Object* val) { LookupTable* tbl = this->reference_class()->packed_ivar_info(); bool found = false; Fixnum* which = try_as<Fixnum>(tbl->fetch(state, sym, &found)); if(!found) { return set_table_ivar(state, sym, val); } body_as_array()[which->to_native()] = val; if(val->reference_p()) write_barrier(state, val); return val; }
int Encoding::find_index(STATE, const char* name) { Object* obj = encoding_map(state)->fetch(state, encoding_symbol(state, name)); if(Tuple* ref = try_as<Tuple>(obj)) { Fixnum* index = try_as<Fixnum>(ref->at(1)); if(index) { return index->to_native(); } else { return -1; } } else { return -1; } }
String* MatchData::pre_matched(STATE) { Fixnum* beg = try_as<Fixnum>(full_->at(state, 0)); native_int max = source_->size(); native_int sz = beg->to_native(); if(!beg || sz <= 0) { return String::create(state, 0, 0); } if(sz > max) sz = max; const char* str = (char*)source_->byte_address(); return String::create(state, str, sz); }
String* MatchData::post_matched(STATE) { Fixnum* fin = try_as<Fixnum>(full_->at(state, 1)); native_int f = fin->to_native(); native_int max = source_->size(); if(!fin || f >= max) { return String::create(state, 0, 0); } const char* str = (char*)source_->byte_address(); native_int sz = max - f; if(sz > max) sz = max; return String::create(state, str + f, sz); }
String* MatchData::pre_matched(STATE) { Fixnum* beg = try_as<Fixnum>(full_->at(state, 0)); native_int max = source_->byte_size(); native_int sz = beg->to_native(); String* string; if(!beg || sz <= 0) { string = String::create(state, 0, 0); } else { if(sz > max) sz = max; const char* str = (char*)source_->byte_address(); string = String::create(state, str, sz); } source_->infect(state, string); string->encoding_from(state, source_); string->klass(state, source_->class_object(state)); return string; }
bool BytecodeVerification::verify_from(STATE, int sp, int ip, std::list<Section>& ips) { if(sp < 0) { fail("internal error", ip); return false; } for(;;) { int old_sp = stack_[ip]; if(old_sp < 0) { stack_[ip] = sp; } else if(old_sp != sp) { fail("inconsistent stack depth", ip); return false; } else { // Already been here and stack is consistent, done. return true; } Fixnum* obj = try_as<Fixnum>(ops_->at(ip++)); if(!obj) { fail("corrupt instruction sequence", ip); return false; } opcode op = obj->to_native(); size_t width = InstructionSequence::instruction_width(op); opcode arg1 = 0; opcode arg2 = 0; switch(width) { case 1: // nothing, done. break; case 2: if(ip >= total_) { fail("truncated instruction sequence", ip); return false; } obj = try_as<Fixnum>(ops_->at(ip++)); if(!obj) { fail("corrupt instruction sequence", ip); return false; } arg1 = obj->to_native(); break; case 3: if(ip >= total_) { fail("truncated instruction sequence", ip); return false; } obj = try_as<Fixnum>(ops_->at(ip++)); if(!obj) { fail("corrupt instruction sequence", ip); return false; } arg1 = obj->to_native(); if(ip >= total_) { fail("truncated instruction sequence", ip); return false; } obj = try_as<Fixnum>(ops_->at(ip++)); if(!obj) { fail("corrupt instruction sequence", ip); return false; } arg2 = obj->to_native(); break; default: fail("invalid instruction", ip); return false; } int read=0, write=0; int effect = stack_difference(op, arg1, arg2, &read, &write); // Check for under read if(sp - read < 0) { fail("stack underflow on read", ip); return false; } // Apply the total effect to propagate it. sp += effect; if(sp < 0) { fail("stack underflow on effect", ip); return false; } // Make sure we don't use more than the declared stack size. if(sp > max_stack_allowed_) { fail("stack overflow", ip); return false; } // Keep track of the max stack depth seen if(sp > max_stack_seen_) max_stack_seen_ = sp; switch(op) { case InstructionSequence::insn_push_local: case InstructionSequence::insn_set_local: if((native_int)arg1 < 0 || (native_int)arg1 >= locals_) { fail("invalid local variable access", ip); return false; } break; case InstructionSequence::insn_goto: if((native_int)arg1 < 0 || (native_int)arg1 >= total_) { fail("invalid goto location", ip); return false; } // Only handle forward branches. if((int)arg1 > ip) { ip = (int)arg1; } else { return true; } break; case InstructionSequence::insn_push_stack_local: case InstructionSequence::insn_set_stack_local: if((int)arg1 > max_stack_local_) { max_stack_local_ = (int)arg1; } break; case InstructionSequence::insn_goto_if_false: case InstructionSequence::insn_goto_if_true: case InstructionSequence::insn_setup_unwind: if((native_int)arg1 < 0 || (native_int)arg1 >= total_) { fail("invalid goto location", ip); return false; } if((int)arg1 > ip) { ips.push_back(Section(sp, arg1)); } break; case InstructionSequence::insn_ret: case InstructionSequence::insn_raise_exc: case InstructionSequence::insn_raise_return: case InstructionSequence::insn_ensure_return: case InstructionSequence::insn_raise_break: case InstructionSequence::insn_reraise: return true; } // Detect falling off the end of the stream if(ip >= total_) { fail("unterminated instruction sequence", ip); return false; } } }
bool BytecodeVerification::verify(STATE) { // Do this setup here instead of the constructor so we can do // some validation of the CompiledCode's fields we read them. // Double check the method itself, since it might be a nil if(!kind_of<CompiledCode>(method_)) { fail("invalid method", -1); return false; } if(Fixnum* fix = try_as<Fixnum>(method_->local_count())) { locals_ = fix->to_native(); } else { fail("method not initialized properly", -1); return false; } InstructionSequence* iseq = try_as<InstructionSequence>(method_->iseq()); if(!iseq) { fail("method not initialized properly", -1); return false; } if(Tuple* tup = try_as<Tuple>(iseq->opcodes())) { ops_ = tup; } else { fail("method not initialized properly", -1); return false; } if(Fixnum* fix = try_as<Fixnum>(method_->stack_size())) { max_stack_allowed_ = fix->to_native(); } else { fail("method not initialized properly", -1); return false; } if(Fixnum* fix = try_as<Fixnum>(method_->splat())) { if(fix->to_native() >= locals_) { fail("invalid splat position", -1); return false; } } Fixnum* tot = try_as<Fixnum>(method_->total_args()); Fixnum* req = try_as<Fixnum>(method_->required_args()); Fixnum* post = try_as<Fixnum>(method_->post_args()); if(!tot || !req || !post) { fail("method not initialized properly (missing arg counts)", -1); return false; } if(tot->to_native() > locals_) { fail("more arguments than local slots", -1); return false; } if(req->to_native() > tot->to_native()) { fail("more required arguments than total", -1); return false; } if(post->to_native() > req->to_native()) { fail("more post arguments than required", -1); return false; } if(post->to_native() > tot->to_native()) { fail("more post arguments than total", -1); return false; } total_ = ops_->num_fields(); stack_ = new int32_t[total_]; for(native_int i = 0; i < total_; i++) { stack_[i] = -1; } std::list<Section> ips; ips.push_back(Section(0, 0)); while(!ips.empty()) { Section& section = ips.front(); int ip = section.ip; int sp = section.sp; ips.pop_front(); if(!verify_from(state, sp, ip, ips)) return false; } // Now, check there is a enough space for the stack locals. if(max_stack_seen_ + max_stack_local_ >= max_stack_allowed_) { fail("not enough space for stack locals", -1); return false; } return true; }
Integer* Integer::from_cstr(STATE, const char* str, const char* end, int base, Object* strict) { bool negative = false; 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(CBOOL(strict)) { return nil<Integer>(); } else { return Fixnum::from(0); } } str++; } if(*str == '-') { str++; negative = true; } else if(*str == '+') { str++; } 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(*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 fits = true; Fixnum* small = from_cstr_fixnum(state, &str, end, negative, base, strict, &fits); if(fits) return small; return from_cstr_bignum(state, str, end, small->to_native(), negative, base, strict); }
void test_mod() { Fixnum* one = as<Fixnum>(Fixnum::from(4)); Fixnum* two = as<Fixnum>(one->mod(state, one)); TS_ASSERT_EQUALS(two->to_native(), 0); }