Skip to content

Commit

Permalink
Merge pull request #2549 from natalie-lang/bs-no-hydrate
Browse files Browse the repository at this point in the history
Get boardslam.rb to run without hydrating any Integers
  • Loading branch information
seven1m authored Feb 1, 2025
2 parents f28178c + 8eb0e77 commit 2ca0ea6
Show file tree
Hide file tree
Showing 11 changed files with 105 additions and 31 deletions.
8 changes: 1 addition & 7 deletions include/natalie/kernel_module.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,7 @@ class KernelModule {
return other == self;
}

static Value klass_obj(Env *env, Value self) {
if (self->klass()) {
return self->klass();
} else {
return NilObject::the();
}
}
static Value klass_obj(Env *env, Value self);

static Value freeze_obj(Env *env, Value self) {
self->freeze();
Expand Down
4 changes: 2 additions & 2 deletions include/natalie/object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,10 @@ class Object : public Cell {
}

Value public_send(Env *, SymbolObject *, Args && = Args(), Block * = nullptr, Value sent_from = nullptr);
Value public_send(Env *, Args &&, Block *);
static Value public_send(Env *, Value, Args &&, Block *);

Value send(Env *, SymbolObject *, Args && = Args(), Block * = nullptr, Value sent_from = nullptr);
Value send(Env *, Args &&, Block *);
static Value send(Env *, Value, Args &&, Block *);

Value send(Env *env, SymbolObject *name, std::initializer_list<Value> args, Block *block = nullptr, Value sent_from = nullptr) {
// NOTE: sent_from is unused, but accepting it makes the SendInstruction codegen simpler. :-)
Expand Down
3 changes: 3 additions & 0 deletions include/natalie/range_object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ class RangeObject : public Object {
template <typename Function>
Value iterate_over_range(Env *env, Function &&f);

template <typename Function>
Value iterate_over_integer_range(Env *env, Function &&f);

template <typename Function>
Value iterate_over_string_range(Env *env, Function &&f);

Expand Down
5 changes: 1 addition & 4 deletions include/natalie/value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,7 @@ class Value {
bool is_boolean() const;

private:
void auto_hydrate() {
if (m_type != Type::Pointer)
hydrate();
}
void auto_hydrate();

template <typename Callback>
Value on_object_value(Callback &&callback);
Expand Down
6 changes: 3 additions & 3 deletions lib/natalie/compiler/binding_gen.rb
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ def generate_name

gen.static_binding_as_instance_method('BasicObject', '__id__', 'Object', 'object_id', argc: 0, pass_env: false, pass_block: false, return_type: :int)
gen.static_binding_as_instance_method('BasicObject', 'equal?', 'Object', 'equal', argc: 1, pass_env: false, pass_block: false, return_type: :bool)
gen.binding('BasicObject', '__send__', 'Object', 'send', argc: 1.., pass_env: true, pass_block: true, return_type: :Object)
gen.static_binding_as_instance_method('BasicObject', '__send__', 'Object', 'send', argc: 1.., pass_env: true, pass_block: true, return_type: :Object)
gen.binding('BasicObject', '!', 'Object', 'not_truthy', argc: 0, pass_env: false, pass_block: false, return_type: :bool)
gen.binding('BasicObject', '==', 'Object', 'eq', argc: 1, pass_env: true, pass_block: false, return_type: :bool)
gen.binding('BasicObject', '!=', 'Object', 'neq', argc: 1, pass_env: true, pass_block: false, return_type: :bool)
Expand Down Expand Up @@ -1010,13 +1010,13 @@ def generate_name
gen.static_binding_as_instance_method('Kernel', 'singleton_class', 'Object', 'singleton_class', argc: 0, pass_env: true, pass_block: false, return_type: :Object)
gen.static_binding_as_instance_method('Kernel', 'tap', 'KernelModule', 'tap', argc: 0, pass_env: true, pass_block: true, return_type: :Object)
gen.static_binding_as_instance_method('Kernel', 'to_s', 'KernelModule', 'inspect', argc: 0, pass_env: true, pass_block: false, return_type: :Object)
gen.static_binding_as_instance_method('Kernel', 'send', 'Object', 'send', argc: 1.., pass_env: true, pass_block: true, return_type: :Object)
gen.static_binding_as_instance_method('Kernel', 'public_send', 'Object', 'public_send', argc: 1.., pass_env: true, pass_block: true, return_type: :Object)
gen.binding('Kernel', 'clone', 'Object', 'clone', argc: 0, kwargs: [:freeze], pass_env: true, pass_block: false, return_type: :Object)
gen.binding('Kernel', 'extend', 'Object', 'extend', argc: 1.., pass_env: true, pass_block: false, return_type: :Object)
gen.binding('Kernel', 'frozen?', 'Object', 'is_frozen', argc: 0, pass_env: false, pass_block: false, return_type: :bool)
gen.binding('Kernel', 'public_send', 'Object', 'public_send', argc: 1.., pass_env: true, pass_block: true, return_type: :Object)
gen.binding('Kernel', 'respond_to?', 'Object', 'respond_to_method', argc: 1..2, pass_env: true, pass_block: false, return_type: :bool)
gen.binding('Kernel', 'respond_to_missing?', 'Object', 'respond_to_missing', argc: 2, pass_env: true, pass_block: false, return_type: :bool, visibility: :private)
gen.binding('Kernel', 'send', 'Object', 'send', argc: 1.., pass_env: true, pass_block: true, return_type: :Object)

gen.undefine_singleton_method('MatchData', 'new')
gen.undefine_singleton_method('MatchData', 'allocate')
Expand Down
2 changes: 2 additions & 0 deletions src/array_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,8 @@ Value ArrayObject::_subjoin(Env *env, Value item, Value joiner) {
auto to_str = "to_str"_s;
auto to_ary = "to_ary"_s;
auto to_s = "to_s"_s;
if (item.is_integer())
return item.send(env, to_s);
if (item->respond_to(env, to_str)) {
// Need to support nil, don't use Object::to_str
auto rval = item.send(env, to_str);
Expand Down
16 changes: 16 additions & 0 deletions src/kernel_module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,15 @@ Value KernelModule::throw_method(Env *env, Value name, Value value) {
throw new ThrowCatchException { name, value };
}

Value KernelModule::klass_obj(Env *env, Value self) {
if (self.is_integer())
return GlobalEnv::the()->Integer();
else if (self->klass())
return self->klass();
else
return NilObject::the();
}

Value KernelModule::define_singleton_method(Env *env, Value self, Value name, Block *block) {
env->ensure_block_given(block);
SymbolObject *name_obj = name->to_symbol(env, Object::Conversion::Strict);
Expand Down Expand Up @@ -703,6 +712,9 @@ Value KernelModule::dup_better(Env *env, Value self) {
}

Value KernelModule::hash(Env *env, Value self) {
if (self.is_integer())
return Value::integer(IntegerObject::to_s(self.integer()).djb2_hash());

switch (self->type()) {
// NOTE: string "foo" and symbol :foo will get the same hash.
// That's probably ok, but maybe worth revisiting.
Expand Down Expand Up @@ -781,6 +793,10 @@ Value KernelModule::instance_variables(Env *env, Value self) {
bool KernelModule::is_a(Env *env, Value self, Value module) {
if (!module.is_module())
env->raise("TypeError", "class or module required");

if (self.is_integer())
return GlobalEnv::the()->Integer()->ancestors_includes(env, module->as_module());

return self->is_a(env, module->as_module());
}

Expand Down
6 changes: 3 additions & 3 deletions src/natalie.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -607,10 +607,10 @@ void print_exception_with_backtrace(Env *env, ExceptionObject *exception, Thread

void handle_top_level_exception(Env *env, ExceptionObject *exception, bool run_exit_handlers) {
if (exception->is_a(env, find_top_level_const(env, "SystemExit"_s)->as_class())) {
Value status_obj = exception->ivar_get(env, "@status"_s);
auto status = exception->ivar_get(env, "@status"_s);
if (run_exit_handlers) run_at_exit_handlers(env);
if (status_obj->type() == Object::Type::Integer) {
auto val = status_obj.integer().to_nat_int_t();
if (status.is_integer()) {
auto val = status.integer().to_nat_int_t();
if (val >= 0 && val <= 255) {
clean_up_and_exit(val);
} else {
Expand Down
28 changes: 18 additions & 10 deletions src/object.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include "natalie.hpp"
#include "natalie/forward.hpp"
#include <cctype>
#include <climits>

namespace Natalie {

Expand Down Expand Up @@ -756,7 +755,7 @@ Value Object::ivar_set(Env *env, SymbolObject *name, Value val) {
if (!m_ivars)
m_ivars = new TM::Hashmap<SymbolObject *, Value> {};

m_ivars->put(name, val.object(), env);
m_ivars->put(name, val, env);
return val;
}

Expand Down Expand Up @@ -928,18 +927,24 @@ Value Object::public_send(Env *env, SymbolObject *name, Args &&args, Block *bloc
return send(env, name, std::move(args), block, MethodVisibility::Public, sent_from);
}

Value Object::public_send(Env *env, Args &&args, Block *block) {
Value Object::public_send(Env *env, Value self, Args &&args, Block *block) {
auto name = args.shift()->to_symbol(env, Object::Conversion::Strict);
return public_send(env->caller(), name, std::move(args), block);
if (self.is_integer())
return self.integer_send(env, name, std::move(args), block, nullptr, MethodVisibility::Public);

return self->public_send(env->caller(), name, std::move(args), block);
}

Value Object::send(Env *env, SymbolObject *name, Args &&args, Block *block, Value sent_from) {
return send(env, name, std::move(args), block, MethodVisibility::Private, sent_from);
}

Value Object::send(Env *env, Args &&args, Block *block) {
Value Object::send(Env *env, Value self, Args &&args, Block *block) {
auto name = args.shift()->to_symbol(env, Object::Conversion::Strict);
return send(env->caller(), name, std::move(args), block);
if (self.is_integer())
return self.integer_send(env, name, std::move(args), block, nullptr, MethodVisibility::Private);

return self->send(env->caller(), name, std::move(args), block);
}

Value Object::send(Env *env, SymbolObject *name, Args &&args, Block *block, MethodVisibility visibility_at_least, Value sent_from) {
Expand Down Expand Up @@ -1105,12 +1110,15 @@ Value Object::clone(Env *env, Value freeze) {
}

void Object::copy_instance_variables(const Value other) {
auto other_obj = other.object_or_null();
assert(other_obj);
assert(other);
if (m_ivars)
delete m_ivars;
if (other_obj->m_ivars)
m_ivars = new TM::Hashmap<SymbolObject *, Value> { *other_obj->m_ivars };
if (other.is_integer())
return;

auto ivars = other.object_pointer()->m_ivars;
if (ivars)
m_ivars = new TM::Hashmap<SymbolObject *, Value> { *ivars };
}

bool Object::is_a(Env *env, Value val) const {
Expand Down
43 changes: 41 additions & 2 deletions src/range_object.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "natalie.hpp"
#include "natalie/bsearch.hpp"
#include "natalie/integer_object.hpp"

namespace Natalie {

Expand All @@ -11,7 +12,7 @@ RangeObject *RangeObject::create(Env *env, Value begin, Value end, bool exclude_
}

void RangeObject::assert_no_bad_value(Env *env, Value begin, Value end) {
if (!begin.is_nil() && !end.is_nil() && begin->send(env, "<=>"_s, { end }).is_nil())
if (!begin.is_nil() && !end.is_nil() && begin.send(env, "<=>"_s, { end }).is_nil())
env->raise("ArgumentError", "bad value for range");
}

Expand All @@ -30,7 +31,8 @@ Value RangeObject::initialize(Env *env, Value begin, Value end, Value exclude_en

template <typename Function>
Value RangeObject::iterate_over_range(Env *env, Function &&func) {
Value item = m_begin;
if (m_begin.is_integer())
return iterate_over_integer_range(env, func);

auto succ = "succ"_s;
if (!m_begin->respond_to(env, succ))
Expand All @@ -41,6 +43,8 @@ Value RangeObject::iterate_over_range(Env *env, Function &&func) {
else if (m_begin.is_symbol() && m_end.is_symbol())
return iterate_over_symbol_range(env, func);

Value item = m_begin;

auto cmp = "<=>"_s;
// If we exclude the end, the loop should not be entered if m_begin (item) == m_end.
bool done = m_exclude_end ? item.send(env, "=="_s, { m_end }).is_truthy() : false;
Expand Down Expand Up @@ -71,6 +75,41 @@ Value RangeObject::iterate_over_range(Env *env, Function &&func) {
return nullptr;
}

template <typename Function>
Value RangeObject::iterate_over_integer_range(Env *env, Function &&func) {
auto end = m_end;
if (end.is_float()) {
if (end->as_float()->is_infinity())
end = NilObject::the();
else
end = Object::to_int(env, end);
}

if (!end.is_nil()) {
assert(end.is_integer());
if (!m_exclude_end)
end = end.integer() + Integer(1);
for (auto i = m_begin.integer(); i < end.integer(); ++i) {
if constexpr (std::is_void_v<std::invoke_result_t<Function, Value>>) {
func(i);
} else {
if (Value ptr = func(i))
return ptr;
}
}
} else {
for (auto i = m_begin.integer();; ++i) {
if constexpr (std::is_void_v<std::invoke_result_t<Function, Value>>) {
func(i);
} else {
if (Value ptr = func(i))
return ptr;
}
}
}
return nullptr;
}

template <typename Function>
Value RangeObject::iterate_over_string_range(Env *env, Function &&func) {
if (Object::equal(m_begin.send(env, "<=>"_s, { m_end }), Value::integer(1)))
Expand Down
15 changes: 15 additions & 0 deletions src/value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,21 @@ bool Value::is_falsey() const { return !is_truthy(); }
bool Value::is_numeric() const { return is_integer() || is_float(); }
bool Value::is_boolean() const { return is_true() || is_false(); }

void Value::auto_hydrate() {
switch (m_type) {
case Type::Integer: {
#ifdef NAT_NO_HYDRATE
printf("Fatal: integer hydration is disabled.\n");
abort();
#else
hydrate();
#endif
}
case Type::Pointer:
break;
}
}

#undef PROFILED_SEND

}

0 comments on commit 2ca0ea6

Please sign in to comment.