/* Copyright (c) 2022 Contrast Security, Inc. See * https://www.contrastsecurity.com/enduser-terms-0317a for more details. */ #include "cs__scope.h" #include "../cs__common/cs__common.h" #include /* Calls to Contrast modules and classes */ VALUE contrast, agent, components; VALUE scope_interface, scope_inst_methods; VALUE scope_mod, scope_klass; /* helpers */ /* get scope for ec or create new * * [Ruby definition] * * EXECUTION_CONTEXT[Fiber.current] ||= Contrast::Agent::Scope.new */ VALUE get_ec() { VALUE ec, scope_inst, new_inst, keys, fiber; ec = rb_const_get(scope_mod, rb_intern(rb_const_ec)); scope_inst = rb_hash_aref(ec, rb_fiber_current()); keys = rb_const_get(scope_mod, rb_intern(rb_const_ec_keys)); fiber = rb_fiber_current(); if (RB_TYPE_P(scope_inst, T_NIL)) { new_inst = rb_new_c_scope(); rb_ary_push(keys, fiber); rb_hash_aset(ec, fiber, new_inst); return new_inst; } else { return scope_inst; } } /* create new Scope class instance */ VALUE rb_new_c_scope() { return rb_class_new_instance(0, 0, scope_klass); } int scope_increase(int scope) { int inc = scope; inc = inc + 1; return INT2FIX(inc); } int scope_decrease(int scope) { int inc = scope; inc = inc - 1; return INT2FIX(inc); } VALUE is_in_scope(int scope) { if (scope > 0) { return Qtrue; } else if (scope <= 0) { return Qfalse; } } /* default: raise NoMethodError, "Scope '#{ name.inspect }' is not registered as * a scope." */ void rb_raise_scope_no_method_err(const VALUE method_scope_sym) { rb_raise(rb_eNoMethodError, "Scope ':%" PRIsVALUE "' is not registered as a scope.", rb_sym_to_s(method_scope_sym)); } /* * @class Contrast::Agent::Scope */ /* * @method initialize * * sets scope instance variables. * def initialize * @contrast_scope = 0 * @deserialization_scope = 0 * @split_scope = 0 * end */ VALUE contrast_scope_klass_init(VALUE self, VALUE args) { rb_iv_set(self, rb_iv_cntr_scope, INT2FIX(0)); rb_iv_set(self, rb_iv_dslr_scope, INT2FIX(0)); rb_iv_set(self, rb_iv_split_scope, INT2FIX(0)); return self; } /* * @method in_contrast_scope? * * Check if we are in contrast scope. * * @return [Boolean] check if we are in contrast scope * if the scope is above 0 return true. * def in_contrast_scope? * @contrast_scope.positive? * end */ VALUE in_cntr_scope(VALUE self, VALUE args) { return is_in_scope(FIX2INT(rb_iv_get(self, rb_iv_cntr_scope))); } /* * @method enter_contrast_scope! * * Enters contrast scope. * * @return @contrast_scope [Integer] contrast scope increased. * def enter_contrast_scope! * @contrast_scope += 1 * end */ VALUE enter_cntr_scope(VALUE self, VALUE args) { int scope = FIX2INT(rb_iv_get(self, rb_iv_cntr_scope)); rb_iv_set(self, rb_iv_cntr_scope, scope_increase(scope)); return rb_iv_get(self, rb_iv_cntr_scope); } /* * @method exit_contrast_scope! * * Exits contrast scope. * * @return @contrast_scope [Integer] contrast scope decreased. * def exit_contrast_scope! * @contrast_scope -= 1 * end */ VALUE exit_cntr_scope(VALUE self, VALUE args) { int scope = FIX2INT(rb_iv_get(self, rb_iv_cntr_scope)); rb_iv_set(self, rb_iv_cntr_scope, scope_decrease(scope)); return rb_iv_get(self, rb_iv_cntr_scope); } /* * @method in_deserialization_scope? * * Check if we are in deserialization scope. * * @return [Boolean] check if we are in deserialization scope * if the scope is above 0 return true. * def in_deserialization_scope? * @deserialization_scope.positive? * end */ VALUE in_dslr_scope(VALUE self, VALUE args) { return is_in_scope(FIX2INT(rb_iv_get(self, rb_iv_dslr_scope))); } /* * @method enter_deserialization_scope! * * Enters deserialization scope. * * @return @deserialization_scope [Integer] deserialization scope increased. * def enter_deserialization_scope! * @deserialization_scope += 1 * end */ VALUE enter_dsrl_scope(VALUE self, VALUE args) { int scope = FIX2INT(rb_iv_get(self, rb_iv_dslr_scope)); rb_iv_set(self, rb_iv_dslr_scope, scope_increase(scope)); return rb_iv_get(self, rb_iv_dslr_scope); } /* * @method exit_deserialization_scope! * * Exits deserialization scope. * * @return @deserialization_scope [Integer] deserialization scope decreased. * def enter_deserialization_scope! * @deserialization_scope += 1 * end */ VALUE exit_dsrl_scope(VALUE self, VALUE args) { int scope = FIX2INT(rb_iv_get(self, rb_iv_dslr_scope)); rb_iv_set(self, rb_iv_dslr_scope, scope_decrease(scope)); return rb_iv_get(self, rb_iv_dslr_scope); } /* * @method in_split_scope? * * Check if we are in split scope. * * @return [Boolean] check if we are in split scope * if the scope is above 0 return true. * def in_split_scope? * @split_scope.positive? * end */ VALUE in_split_scope(VALUE self, VALUE args) { return is_in_scope(FIX2INT(rb_iv_get(self, rb_iv_split_scope))); } /* * @method enter_split_scope! * * Enters split scope. * * @return @split_scope [Integer] split scope increased. * def enter_split_scope! * @split_scope += 1 * end */ VALUE enter_split_scope(VALUE self, VALUE args) { int scope = FIX2INT(rb_iv_get(self, rb_iv_split_scope)); rb_iv_set(self, rb_iv_split_scope, scope_increase(scope)); return rb_iv_get(self, rb_iv_split_scope); } /* * @method exit_split_scope! * * Exits split scope. * * @return @split_scope [Integer] split scope decreased. * def enter_split_scope! * @split_scope -= 1 * end */ VALUE exit_split_scope(VALUE self, VALUE args) { int scope = FIX2INT(rb_iv_get(self, rb_iv_split_scope)); rb_iv_set(self, rb_iv_split_scope, scope_decrease(scope)); return rb_iv_get(self, rb_iv_split_scope); } /* * @method split_scope_depth * * Returns split scope current depth. * * @return @split_scope [Integer] * def split_scope_depth * @split_scope * end */ VALUE split_scope_depth(VALUE self, VALUE args) { return rb_iv_get(self, rb_iv_split_scope); } /* * Static methods to be used, the cases are defined by the usage from the above * methods if more methods are added - please extend the case statements as they * are no longed dynamic */ /* * @method in_scope? * * Check if we are in specific scope. * * @param name [Symbol] scope symbol representing scope to check. * @return [Boolean] check if we are in passed scope. * def in_scope? name * case name * when :contrast * in_contrast_scope? * when :deserialization * in_deserialization_scope? * when :split * in_split_scope? * else * raise NoMethodError, "Scope '#{ name.inspect }' is not registered as a * scope." end end */ VALUE scope_klass_in_scope(VALUE self, VALUE method_scope_sym) { VALUE in_scope = Qnil; if (method_scope_sym == rb_sym_contrast) { /* in_contrast_scope? */ in_scope = in_cntr_scope(self, 0); } else if (method_scope_sym == rb_sym_deserialization) { /* in_deserialization_scope? */ in_scope = in_dslr_scope(self, 0); } else if (method_scope_sym == rb_sym_split) { /* in_split_scope? */ in_scope = in_split_scope(self, 0); } else { rb_raise_scope_no_method_err(method_scope_sym); } return in_scope; } /* * @method enter_scope! * * Enters specific scope. * * @param name [Symbol] scope symbol representing scope to enter. * @return [Boolean] entered scope value increased. * def enter_scope! name * case name * when :contrast * enter_contrast_scope! * when :deserialization * enter_deserialization_scope! * when :split * enter_split_scope! * else * raise NoMethodError, "Scope '#{ name.inspect }' is not registered as a * scope." end end */ VALUE scope_klass_enter_scope(VALUE self, VALUE method_scope_sym) { VALUE enter_scope = Qnil; if (method_scope_sym == rb_sym_contrast) { enter_scope = enter_cntr_scope(self, 0); } else if (method_scope_sym == rb_sym_deserialization) { enter_scope = enter_dsrl_scope(self, 0); } else if (method_scope_sym == rb_sym_split) { enter_scope = enter_split_scope(self, 0); } else { rb_raise_scope_no_method_err(method_scope_sym); } return enter_scope; } /* * @method exit_scope! * * Exits specific scope. * * @param name [Symbol] scope symbol representing scope to exit. * @return [Boolean] entered scope value decreased. * def exit_scope! name * case name * when :contrast * exit_contrast_scope! * when :deserialization * exit_deserialization_scope! * when :split * exit_split_scope! * else * raise NoMethodError, "Scope '#{ name.inspect }' is not registered as a * scope." end end */ VALUE scope_klass_exit_scope(VALUE self, VALUE method_scope_sym) { VALUE exit_scope = Qnil; if (method_scope_sym == rb_sym_contrast) { exit_scope = exit_cntr_scope(self, 0); } else if (method_scope_sym == rb_sym_deserialization) { exit_scope = exit_dsrl_scope(self, 0); } else if (method_scope_sym == rb_sym_split) { exit_scope = exit_split_scope(self, 0); } else { rb_raise_scope_no_method_err(method_scope_sym); } return exit_scope; } /* * @class Contrast::Components::Interface */ /* @method #initialize * * init set new scope for current fiber * @return ec [Hash Scope>] * * [Ruby definition] * * EXECUTION_CONTEXT[Fiber.current] = Contrast::Agent::Scope.new */ VALUE contrast_scope_interface_init(VALUE self, VALUE args) { VALUE ec = rb_const_get(scope_mod, rb_intern(rb_const_ec)); rb_hash_aset(ec, rb_fiber_current(), rb_new_c_scope()); return ec; } /* * @method #scope_for_current_ec * * This returns the scope governing the current execution context. Use this * sparingly, preferring the instance & class methods to access and query scope, * rather than interacting with the scope object directly. * * Alternative to Monitor => mutex.synchronize * rb_mutex_new(void) * rb_mutex_synchronize (VALUE mutex, VALUE(*func)(VALUE arg), VALUE arg) * Mutex.synchronize do ... end goes here: * * * [ruby definition] * * def scope_for_current_ec * MONITOR.synchronize do * return EXECUTION_CONTEXT[Fiber.current] ||= Contrast::Agent::Scope.new * end * end */ VALUE contrast_scope_for_current_ec(VALUE self, VALUE args) { /* synchronize */ VALUE mutex = rb_const_get(scope_mod, rb_intern(rb_const_mon)); return rb_mutex_synchronize(mutex, get_ec, 0); } /* * @module Contrast::Components::Scope::InstanceMethods */ /* * @method #enter_method_scope! * * Iterates over the method policy's scopes and enters in each one. * * @param scopes_to_enter [Array] Scopes form * method_policy#scopes_to_enter for the scope current method policy * @return scopes_to_enter [Array] * * [Ruby definition] * * def enter_method_scope! scopes_to_enter * scopes_to_enter.each do |scope| * enter_scope!(scope) * end * end */ VALUE inst_methods_enter_method_scope(VALUE self, VALUE scopes_to_enter) { VALUE scopes_ary, scope; scopes_ary = rb_ary_dup(scopes_to_enter); scope = rb_ary_pop(scopes_ary); while (!RB_TYPE_P(scope, T_NIL)) { inst_methods_enter_scope(self, scope); scope = rb_ary_pop(scopes_ary); } return scopes_to_enter; } /* * @method #exit_method_scope! * * Iterates over the method policy's scopes and exits each one. * * @param scopes_to_exit [Array] Scopes form * method_policy#scopes_to_exit for the scope current method policy * @return scopes_to_exit [Array] * * [Ruby definition] * * def enter_method_scope! scopes_to_exit * scopes_to_exit.each do |scope| * enter_scope!(scope) * end * end */ VALUE inst_methods_exit_method_scope(VALUE self, VALUE scopes_to_exit) { VALUE scopes_ary, scope; scopes_ary = rb_ary_dup(scopes_to_exit); scope = rb_ary_pop(scopes_ary); while (!RB_TYPE_P(scope, T_NIL)) { inst_methods_exit_scope(self, scope); scope = rb_ary_pop(scopes_ary); } return scopes_to_exit; } /* For the InstanceMethods we need to call all the scope methods from the * current ec context All methods bellow are with same names as * Contrast::Agent::Scope class with the difference that they act as forwarders: * exp: * def in_contrast_scope? * scope_for_current_ec.in_contrast_scope? * end */ VALUE inst_methods_in_cntr_scope(VALUE self, VALUE args) { return is_in_scope(FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope))); } VALUE inst_methods_enter_cntr_scope(VALUE self, VALUE args) { int scope = FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope)); rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope, scope_increase(scope)); return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope); } VALUE inst_methods_exit_cntr_scope(VALUE self, VALUE args) { int scope = FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope)); rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope, scope_decrease(scope)); return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope); } VALUE inst_methods_in_split_scope(VALUE self, VALUE args) { return is_in_scope(FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope))); } VALUE inst_methods_enter_split_scope(VALUE self, VALUE args) { int scope = FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope)); return rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope, scope_increase(scope)); } VALUE inst_methods_exit_split_scope(VALUE self, VALUE args) { int scope = FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope)); rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope, scope_decrease(scope)); return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope); } VALUE inst_methods_split_scope_depth(VALUE self, VALUE args) { return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope); } VALUE inst_methods_in_dsrl_scope(VALUE self, VALUE args) { return is_in_scope(FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope))); } VALUE inst_methods_enter_dsrl_scope(VALUE self, VALUE args) { int scope = FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope)); rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope, scope_increase(scope)); return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope); } VALUE inst_methods_exit_dsrl_scope(VALUE self, VALUE args) { int scope = FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope)); rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope, scope_decrease(scope)); return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope); } VALUE inst_methods_in_scope(VALUE self, VALUE method_scope_sym) { if (method_scope_sym == rb_sym_contrast) { inst_methods_in_cntr_scope(self, 0); } else if (method_scope_sym == rb_sym_deserialization) { inst_methods_in_dsrl_scope(self, 0); } else if (method_scope_sym == rb_sym_split) { inst_methods_in_split_scope(self, 0); } else { rb_raise_scope_no_method_err(method_scope_sym); } } VALUE inst_methods_enter_scope(VALUE self, VALUE method_scope_sym) { if (method_scope_sym == rb_sym_contrast) { inst_methods_enter_cntr_scope(self, 0); } else if (method_scope_sym == rb_sym_deserialization) { inst_methods_enter_dsrl_scope(self, 0); } else if (method_scope_sym == rb_sym_split) { inst_methods_enter_split_scope(self, 0); } else { rb_raise_scope_no_method_err(method_scope_sym); } } VALUE inst_methods_exit_scope(VALUE self, VALUE method_scope_sym) { if (method_scope_sym == rb_sym_contrast) { inst_methods_exit_cntr_scope(self, 0); } else if (method_scope_sym == rb_sym_deserialization) { inst_methods_exit_dsrl_scope(self, 0); } else if (method_scope_sym == rb_sym_split) { inst_methods_exit_split_scope(self, 0); } else { rb_raise_scope_no_method_err(method_scope_sym); } } /* * @module Contrast::Components::Scope * * TODO: RUBY-534, #sweep_dead_ecs compensates for a lack of weak tables. when * we can use WeakRef, we should investigate removing this call and instead use * the WeakRef for the Execution Context's Keys or using our Finalizers Hash for * Fibers * * [Ruby definition] * * MONITOR.synchronize do * EXECUTION_CONTEXT.delete_if do |ec, _scope| * !ec.alive? * end * end * end */ VALUE scope_mod_sweep_dead_ecs(VALUE self, VALUE args) { VALUE mutex, ec, ec_keys, key, test; mutex = rb_const_get(scope_mod, rb_intern(rb_const_mon)); ec = rb_const_get(scope_mod, rb_intern(rb_const_ec)); ec_keys = rb_const_get(scope_mod, rb_intern(rb_const_ec_keys)); /* Check if the key is dead (terminated fiber) and delete if true. */ int i = 0; int size = rb_hash_size(ec_keys); for (i = 0; i < size; ++i) { key = rb_ary_entry(ec_keys, i); test = key; if (!RB_TYPE_P(key, T_NIL)) { if (!rb_fiber_alive_p(key)) { rb_hash_delete(ec, key); } } } return ec; } void Init_cs__scope() { /* ivs */ rb_iv_cntr_scope = "@contrast_scope"; rb_iv_dslr_scope = "@deserialization_scope"; rb_iv_split_scope = "@split_scope"; /* constants */ rb_const_mon = "MONITOR"; rb_const_ec = "EXECUTION_CONTEXT"; rb_const_ec_keys = "EC_KEYS"; /* Symbols */ rb_sym_scope_mod = rb_intern("Scope"); rb_sym_contrast = ID2SYM(rb_intern("contrast")); rb_sym_deserialization = ID2SYM(rb_intern("deserialization")); rb_sym_split = ID2SYM(rb_intern("split")); /* method names */ rb_method_name_init = "initialize"; rb_method_name_in_scope = "in_scope?"; rb_method_name_enter_scope = "enter_scope!"; rb_method_name_exit_scope = "exit_scope!"; rb_method_name_scope_for_current_ec = "scope_for_current_ec"; rb_method_name_in_cntr_scope = "in_contrast_scope?"; rb_method_name_enter_cntr_scope = "enter_contrast_scope!"; rb_method_name_exit_cntr_scope = "exit_contrast_scope!"; rb_method_name_in_dslr_scope = "in_deserialization_scope?"; rb_method_name_enter_dslr_scope = "enter_deserialization_scope!"; rb_method_name_exit_dslr_scope = "exit_deserialization_scope!"; rb_method_name_in_split_scope = "in_split_scope?"; rb_method_name_enter_split_scope = "enter_split_scope!"; rb_method_name_exit_split_scope = "exit_split_scope!"; rb_method_name_split_scope_depth = "split_scope_depth"; /* Define the new scope modules and objects */ contrast = rb_define_module("Contrast"); agent = rb_define_module_under(contrast, "Agent"); /* components => Contrast::Components */ components = rb_define_module_under(contrast, "Components"); /* scope_mod => Contrast::Components::Scope */ scope_mod = rb_define_module_under(components, "Scope"); /* scope_interface => Contrast::Components::Scope::Interface */ scope_interface = rb_define_class_under(scope_mod, "Interface", rb_cObject); /* scope_inst_methods => Contrast::Components::Scope::InstanceMethods */ scope_inst_methods = rb_define_module_under(scope_mod, "InstanceMethods"); /* scope_klass => Contrast::Agent::Scope */ scope_klass = rb_define_class_under(agent, "Scope", rb_cObject); /* * @class Contrast::Agent::Scope */ /* Instance methods: */ rb_define_method(scope_klass, rb_method_name_init, contrast_scope_klass_init, 0); rb_define_method(scope_klass, rb_method_name_in_cntr_scope, in_cntr_scope, 0); rb_define_method(scope_klass, rb_method_name_enter_cntr_scope, enter_cntr_scope, 0); rb_define_method(scope_klass, rb_method_name_exit_cntr_scope, exit_cntr_scope, 0); rb_define_method(scope_klass, rb_method_name_in_dslr_scope, in_dslr_scope, 0); rb_define_method(scope_klass, rb_method_name_enter_dslr_scope, enter_dsrl_scope, 0); rb_define_method(scope_klass, rb_method_name_exit_dslr_scope, exit_dsrl_scope, 0); rb_define_method(scope_klass, rb_method_name_in_split_scope, in_split_scope, 0); rb_define_method(scope_klass, rb_method_name_enter_split_scope, enter_split_scope, 0); rb_define_method(scope_klass, rb_method_name_exit_split_scope, exit_split_scope, 0); rb_define_method(scope_klass, rb_method_name_split_scope_depth, split_scope_depth, 0); rb_define_method(scope_klass, rb_method_name_in_scope, scope_klass_in_scope, 1); rb_define_method(scope_klass, rb_method_name_enter_scope, scope_klass_enter_scope, 1); rb_define_method(scope_klass, rb_method_name_exit_scope, scope_klass_exit_scope, 1); /* * @class Contrast::Components::Interface */ /* Interface#initialize */ rb_define_method(scope_interface, rb_method_name_init, contrast_scope_interface_init, 0); /* Interface#scope_for_current_ec */ rb_define_method(scope_interface, rb_method_name_scope_for_current_ec, contrast_scope_for_current_ec, 0); /* * @module Contrast::Components::Scope */ /* Constants */ /* Contrast::Components::Scope::EC_KEYS */ rb_const_set(scope_mod, rb_intern(rb_const_ec_keys), rb_ary_new()); /* Contrast::Components::Scope::EXECUTION_CONTEXT => {} */ rb_define_const(scope_mod, rb_const_ec, rb_hash_new()); /* Contrast::Components::Scope::MONITOR => Mutex.new */ rb_define_const(scope_mod, rb_const_mon, rb_mutex_new()); /* * @module Contrast::Components::Scope::InstanceMethods */ /* InstanceMethods#scope_for_current_ec */ rb_define_method(scope_inst_methods, rb_method_name_scope_for_current_ec, contrast_scope_for_current_ec, 0); /* Forwarders */ rb_define_method(scope_inst_methods, rb_method_name_in_cntr_scope, inst_methods_in_cntr_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_enter_cntr_scope, inst_methods_enter_cntr_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_exit_cntr_scope, inst_methods_exit_cntr_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_in_dslr_scope, inst_methods_in_dsrl_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_enter_dslr_scope, inst_methods_enter_dsrl_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_exit_dslr_scope, inst_methods_exit_dsrl_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_in_split_scope, inst_methods_in_split_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_enter_split_scope, inst_methods_enter_split_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_exit_split_scope, inst_methods_exit_split_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_split_scope_depth, inst_methods_split_scope_depth, 0); rb_define_method(scope_inst_methods, rb_method_name_in_scope, inst_methods_in_scope, 1); rb_define_method(scope_inst_methods, rb_method_name_enter_scope, inst_methods_enter_scope, 1); rb_define_method(scope_inst_methods, rb_method_name_exit_scope, inst_methods_exit_scope, 1); /* * @module Contrast::Components::Scope */ rb_define_singleton_method(scope_mod, "sweep_dead_ecs", scope_mod_sweep_dead_ecs, 0); /* * @module Contrast::Components::Scope::InstanceMethods */ rb_define_method(scope_inst_methods, "contrast_enter_method_scopes!", inst_methods_enter_method_scope, 1); rb_define_method(scope_inst_methods, "contrast_exit_method_scopes!", inst_methods_exit_method_scope, 1); }