Coverage Report

Created: 2026-08-26 07:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mruby/mrbgems/mruby-binding/src/binding.c
Line
Count
Source
1
#include <mruby.h>
2
#include <mruby/array.h>
3
#include <mruby/class.h>
4
#include <mruby/hash.h>
5
#include <mruby/proc.h>
6
#include <mruby/variable.h>
7
#include <mruby/opcode.h>
8
#include <mruby/debug.h>
9
#include <mruby/internal.h>
10
11
0
#define BINDING_UPPER_DEFAULT  20
12
#define BINDING_UPPER_MINIMUM  10
13
#define BINDING_UPPER_MAXIMUM 100
14
15
#ifndef MRB_BINDING_UPPER_MAX
16
0
# define BINDING_UPPER_MAX BINDING_UPPER_DEFAULT
17
#else
18
# if (MRB_BINDING_UPPER_MAX) > BINDING_UPPER_MAXIMUM
19
#  define BINDING_UPPER_MAX BINDING_UPPER_MAXIMUM
20
# elif (MRB_BINDING_UPPER_MAX) < BINDING_UPPER_MINIMUM
21
#  define BINDING_UPPER_MAX BINDING_UPPER_MINIMUM
22
# else
23
#  define BINDING_UPPER_MAX MRB_BINDING_UPPER_MAX
24
# endif
25
#endif
26
27
static mrb_int
28
binding_extract_pc(mrb_state *mrb, mrb_value binding)
29
0
{
30
0
  mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(pc));
31
0
  if (mrb_nil_p(obj)) {
32
0
    return -1;
33
0
  }
34
0
  else {
35
0
    mrb_check_type(mrb, obj, MRB_TT_INTEGER);
36
0
    return mrb_int(mrb, obj);
37
0
  }
38
0
}
39
40
const struct RProc *
41
mrb_binding_extract_proc(mrb_state *mrb, mrb_value binding)
42
2
{
43
2
  mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(proc));
44
2
  mrb_check_type(mrb, obj, MRB_TT_PROC);
45
2
  return mrb_proc_ptr(obj);
46
2
}
47
48
struct REnv *
49
mrb_binding_extract_env(mrb_state *mrb, mrb_value binding)
50
2
{
51
2
  mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(env));
52
2
  if (mrb_nil_p(obj)) {
53
0
    return NULL;
54
0
  }
55
2
  else {
56
2
    mrb_check_type(mrb, obj, MRB_TT_ENV);
57
2
    return (struct REnv*)mrb_obj_ptr(obj);
58
2
  }
59
2
}
60
61
static mrb_irep *
62
binding_irep_new_lvspace(mrb_state *mrb)
63
4
{
64
4
  static const mrb_code iseq_dummy[] = { OP_RETURN, 0 };
65
66
4
  mrb_irep *irep = mrb_add_irep(mrb);
67
4
  irep->flags = MRB_ISEQ_NO_FREE;
68
4
  irep->iseq = iseq_dummy;
69
4
  irep->ilen = sizeof(iseq_dummy) / sizeof(iseq_dummy[0]);
70
4
  irep->lv = NULL;
71
4
  irep->nlocals = 1;
72
4
  irep->nregs = 1;
73
4
  return irep;
74
4
}
75
76
static struct RProc *
77
binding_proc_new_lvspace(mrb_state *mrb, const struct RProc *upper, struct REnv *env)
78
4
{
79
4
  struct RProc *lvspace = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class);
80
4
  lvspace->body.irep = binding_irep_new_lvspace(mrb);
81
4
  lvspace->upper = upper;
82
4
  if (env && env->tt == MRB_TT_ENV) {
83
4
    lvspace->e.env = env;
84
4
    lvspace->flags |= MRB_PROC_ENVSET;
85
4
  }
86
4
  return lvspace;
87
4
}
88
89
static struct REnv *
90
binding_env_new_lvspace(mrb_state *mrb, const struct REnv *e)
91
4
{
92
4
  struct REnv *env = MRB_OBJ_ALLOC(mrb, MRB_TT_ENV, NULL);
93
4
  mrb_value *stacks = (mrb_value*)mrb_calloc(mrb, 1, sizeof(mrb_value));
94
4
  env->mid = 0;
95
4
  env->stack = stacks;
96
4
  if (e && e->stack && MRB_ENV_LEN(e) > 0) {
97
4
    env->stack[0] = e->stack[0];
98
4
  }
99
0
  else {
100
0
    env->stack[0] = mrb_nil_value();
101
0
  }
102
4
  MRB_ENV_SET_LEN(env, 1);
103
4
  return env;
104
4
}
105
106
static void
107
binding_check_proc_upper_count(mrb_state *mrb, const struct RProc *proc)
108
0
{
109
0
  for (size_t count = 0; proc && !MRB_PROC_CFUNC_P(proc); proc = proc->upper) {
110
0
    count++;
111
0
    if (count > BINDING_UPPER_MAX) {
112
0
      mrb_raise(mrb, E_RUNTIME_ERROR,
113
0
                "too many upper procs for local variables (mruby limitation; maximum is " MRB_STRINGIZE(BINDING_UPPER_MAX) ")");
114
0
    }
115
0
    if (MRB_PROC_SCOPE_P(proc)) break;
116
0
  }
117
0
}
118
119
mrb_bool
120
mrb_binding_p(mrb_state *mrb, mrb_value obj)
121
2
{
122
2
  if (!mrb_obj_is_kind_of(mrb, obj, mrb_class_get_id(mrb, MRB_SYM(Binding)))) return FALSE;
123
2
  if (mrb_type(obj) != MRB_TT_OBJECT) return FALSE;
124
2
  if (!mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), MRB_SYM(proc))) return FALSE;
125
2
  if (!mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), MRB_SYM(recv))) return FALSE;
126
2
  if (!mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), MRB_SYM(env))) return FALSE;
127
2
  return TRUE;
128
2
}
129
130
static void
131
binding_type_ensure(mrb_state *mrb, mrb_value obj)
132
2
{
133
2
  if (mrb_binding_p(mrb, obj)) return;
134
0
  mrb_raise(mrb, E_TYPE_ERROR, "not a binding");
135
2
}
136
137
static struct RProc*
138
binding_wrap_lvspace(mrb_state *mrb, const struct RProc *proc, struct REnv **envp)
139
4
{
140
  /*
141
   * local variable space: It is a space to hold the top-level variable of
142
   * binding.eval and binding.local_variable_set.
143
   */
144
145
4
  struct RProc *lvspace = binding_proc_new_lvspace(mrb, proc, *envp);
146
4
  *envp = binding_env_new_lvspace(mrb, *envp);
147
4
  return lvspace;
148
4
}
149
150
static mrb_value
151
binding_initialize_copy(mrb_state *mrb, mrb_value binding)
152
2
{
153
2
  mrb_value src = mrb_get_arg1(mrb);
154
2
  binding_type_ensure(mrb, src);
155
2
  const struct RProc *src_proc = mrb_binding_extract_proc(mrb, src);
156
2
  struct REnv *src_env = mrb_binding_extract_env(mrb, src);
157
158
2
  mrb_check_frozen(mrb, mrb_obj_ptr(binding));
159
160
2
  struct RProc *lvspace;
161
2
  struct REnv *env;
162
2
  if (MRB_ENV_LEN(src_env) < 2) {
163
    /* when local variables of src are self only */
164
2
    env = src_proc->e.env;
165
2
    lvspace = binding_wrap_lvspace(mrb, src_proc->upper, &env);
166
2
  }
167
0
  else {
168
0
    binding_check_proc_upper_count(mrb, src_proc);
169
170
0
    env = src_env;
171
0
    lvspace = binding_wrap_lvspace(mrb, src_proc, &env);
172
173
    // The reason for using the mrb_obj_iv_set_force() function is to allow local
174
    // variables to be modified even if src is frozen. This behavior is CRuby imitation.
175
0
    src_proc = binding_wrap_lvspace(mrb, src_proc, &src_env);
176
0
    struct RObject *o = mrb_obj_ptr(src);
177
0
    mrb_obj_iv_set_force(mrb, o, MRB_SYM(proc), mrb_obj_value((struct RProc*)src_proc));
178
0
    mrb_obj_iv_set_force(mrb, o, MRB_SYM(env), mrb_obj_value(src_env));
179
0
  }
180
2
  mrb_iv_set(mrb, binding, MRB_SYM(proc), mrb_obj_value(lvspace));
181
2
  mrb_iv_set(mrb, binding, MRB_SYM(env), mrb_obj_value(env));
182
183
2
  return binding;
184
2
}
185
186
static mrb_noreturn void
187
badname_error(mrb_state *mrb, mrb_sym id)
188
0
{
189
0
  mrb_raisef(mrb, E_NAME_ERROR, "wrong local variable name %!n for binding", id);
190
0
}
191
192
static void
193
binding_local_variable_name_check(mrb_state *mrb, mrb_sym id)
194
0
{
195
0
  if (id == 0) {
196
0
    badname_error(mrb, id);
197
0
  }
198
199
0
  mrb_int len;
200
0
  const char *name = mrb_sym_name_len(mrb, id, &len);
201
0
  if (len == 0) {
202
0
    badname_error(mrb, id);
203
0
  }
204
205
0
  if (ISASCII(*name) && !(*name == '_' || ISLOWER(*name))) {
206
0
    badname_error(mrb, id);
207
0
  }
208
0
  len--;
209
0
  name++;
210
211
0
  for (; len > 0; len--, name++) {
212
0
    if (ISASCII(*name) && !(*name == '_' || ISALNUM(*name))) {
213
0
      badname_error(mrb, id);
214
0
    }
215
0
  }
216
0
}
217
218
static mrb_value *
219
binding_local_variable_search(mrb_state *mrb, const struct RProc *proc, struct REnv *env, mrb_sym varname)
220
0
{
221
0
  binding_local_variable_name_check(mrb, varname);
222
223
0
  while (proc) {
224
0
    if (MRB_PROC_CFUNC_P(proc)) break;
225
226
0
    const mrb_irep *irep = proc->body.irep;
227
0
    const mrb_sym *lv;
228
0
    if (irep && (lv = irep->lv)) {
229
0
      for (int i = 0; i + 1 < irep->nlocals; i++, lv++) {
230
0
        if (varname == *lv) {
231
0
          return (env && MRB_ENV_LEN(env) > i) ? &env->stack[i + 1] : NULL;
232
0
        }
233
0
      }
234
0
    }
235
236
0
    if (MRB_PROC_SCOPE_P(proc)) break;
237
0
    env = MRB_PROC_ENV(proc);
238
0
    proc = proc->upper;
239
0
  }
240
241
0
  return NULL;
242
0
}
243
244
/*
245
 * call-seq:
246
 *  local_variable_defined?(symbol) -> bool
247
 *
248
 * Returns true if a local variable with the given name is defined
249
 * in the binding's context, false otherwise.
250
 *
251
 *   def foo
252
 *     a = 1
253
 *     b = binding
254
 *     b.local_variable_defined?(:a)  #=> true
255
 *     b.local_variable_defined?(:c)  #=> false
256
 *   end
257
 *
258
 *   x = 10
259
 *   bind = binding
260
 *   bind.local_variable_defined?(:x)     #=> true
261
 *   bind.local_variable_defined?(:y)     #=> false
262
 *   bind.local_variable_set(:y, 20)
263
 *   bind.local_variable_defined?(:y)     #=> true
264
 */
265
static mrb_value
266
binding_local_variable_defined_p(mrb_state *mrb, mrb_value self)
267
0
{
268
0
  mrb_sym varname;
269
0
  mrb_get_args(mrb, "n", &varname);
270
271
0
  const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
272
0
  struct REnv *env = mrb_binding_extract_env(mrb, self);
273
0
  mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
274
0
  if (e) {
275
0
    return mrb_true_value();
276
0
  }
277
0
  else {
278
0
    return mrb_false_value();
279
0
  }
280
0
}
281
282
/*
283
 * call-seq:
284
 *  local_variable_get(symbol) -> object
285
 *
286
 * Returns the value of the local variable with the given name
287
 * in the binding's context. Raises NameError if the variable
288
 * is not defined.
289
 *
290
 *   def foo
291
 *     a = 42
292
 *     b = "hello"
293
 *     bind = binding
294
 *     bind.local_variable_get(:a)  #=> 42
295
 *     bind.local_variable_get(:b)  #=> "hello"
296
 *     bind.local_variable_get(:c)  #=> NameError
297
 *   end
298
 *
299
 *   x = [1, 2, 3]
300
 *   bind = binding
301
 *   bind.local_variable_get(:x)      #=> [1, 2, 3]
302
 *   x = "modified"
303
 *   bind.local_variable_get(:x)      #=> "modified"
304
 */
305
static mrb_value
306
binding_local_variable_get(mrb_state *mrb, mrb_value self)
307
0
{
308
0
  mrb_sym varname;
309
0
  mrb_get_args(mrb, "n", &varname);
310
311
0
  const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
312
0
  struct REnv *env = mrb_binding_extract_env(mrb, self);
313
0
  mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
314
0
  if (!e) {
315
0
    mrb_raisef(mrb, E_NAME_ERROR, "local variable %!n is not defined", varname);
316
0
  }
317
318
0
  return *e;
319
0
}
320
321
/*
322
 * call-seq:
323
 *   binding.local_variable_set(symbol, obj) -> obj
324
 *
325
 * Set local variable named symbol as obj in binding's context.
326
 * If the variable is not defined in the binding, it will be created.
327
 *
328
 *   def foo
329
 *     a = 1
330
 *     binding.local_variable_set(:a, 2)
331
 *     binding.local_variable_set(:b, 3)
332
 *     [a, b]  #=> [2, 3]
333
 *   end
334
 */
335
static mrb_value
336
binding_local_variable_set(mrb_state *mrb, mrb_value self)
337
0
{
338
0
  mrb_sym varname;
339
0
  mrb_value obj;
340
0
  mrb_get_args(mrb, "no", &varname, &obj);
341
342
0
  const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
343
0
  struct REnv *env = mrb_binding_extract_env(mrb, self);
344
0
  mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
345
0
  if (e) {
346
0
    *e = obj;
347
0
    if (!mrb_immediate_p(obj)) {
348
0
      mrb_field_write_barrier(mrb, (struct RBasic*)env, (struct RBasic*)mrb_obj_ptr(obj));
349
0
    }
350
0
  }
351
0
  else {
352
0
    mrb_proc_merge_lvar(mrb, (mrb_irep*)proc->body.irep, env, 1, &varname, &obj);
353
0
  }
354
355
0
  return obj;
356
0
}
357
358
/*
359
 * call-seq:
360
 *   binding.local_variables -> array
361
 *
362
 * Returns an array of symbols representing the names of the local variables
363
 * in the binding.
364
 *
365
 *   def foo
366
 *     a = 1
367
 *     b = 2
368
 *     binding.local_variables  #=> [:a, :b]
369
 *   end
370
 */
371
static mrb_value
372
binding_local_variables(mrb_state *mrb, mrb_value self)
373
0
{
374
0
  const struct RProc *proc = mrb_proc_ptr(mrb_iv_get(mrb, self, MRB_SYM(proc)));
375
0
  return mrb_proc_local_variables(mrb, proc);
376
0
}
377
378
/*
379
 * call-seq:
380
 *   binding.receiver -> object
381
 *
382
 * Returns the bound receiver of the binding object.
383
 *
384
 *   class Demo
385
 *     def get_binding
386
 *       binding
387
 *     end
388
 *   end
389
 *   Demo.new.get_binding.receiver  #=> #<Demo:0x...>
390
 */
391
static mrb_value
392
binding_receiver(mrb_state *mrb, mrb_value self)
393
0
{
394
0
  return mrb_iv_get(mrb, self, MRB_SYM(recv));
395
0
}
396
397
/*
398
 * call-seq:
399
 *  source_location -> [String, Integer]
400
 */
401
static mrb_value
402
binding_source_location(mrb_state *mrb, mrb_value self)
403
0
{
404
0
  if (mrb_iv_defined(mrb, self, MRB_SYM(source_location))) {
405
0
    return mrb_iv_get(mrb, self, MRB_SYM(source_location));
406
0
  }
407
408
0
  mrb_value srcloc;
409
0
  const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
410
0
  if (!proc || MRB_PROC_CFUNC_P(proc) ||
411
0
      !proc->upper || MRB_PROC_CFUNC_P(proc->upper)) {
412
0
    srcloc = mrb_nil_value();
413
0
  }
414
0
  else {
415
0
    const mrb_irep *irep = proc->upper->body.irep;
416
0
    mrb_int pc = binding_extract_pc(mrb, self);
417
0
    if (pc < 0) {
418
0
      srcloc = mrb_nil_value();
419
0
    }
420
0
    else {
421
0
      const char *fname;
422
0
      int32_t line;
423
424
0
      if (!mrb_debug_get_position(mrb, irep, (uint32_t)pc, &line, &fname)) {
425
0
        srcloc  = mrb_nil_value();
426
0
      }
427
0
      else {
428
0
        srcloc = mrb_assoc_new(mrb, mrb_str_new_cstr(mrb, fname), mrb_fixnum_value(line));
429
0
      }
430
0
    }
431
0
  }
432
433
0
  if (!mrb_frozen_p(mrb_obj_ptr(self))) {
434
0
    mrb_iv_set(mrb, self, MRB_SYM(source_location), srcloc);
435
0
  }
436
0
  return srcloc;
437
0
}
438
439
mrb_value
440
mrb_binding_new(mrb_state *mrb, const struct RProc *proc, mrb_value recv, struct REnv *env)
441
2
{
442
2
  struct RObject *binding = MRB_OBJ_ALLOC(mrb, MRB_TT_OBJECT, mrb_class_get_id(mrb, MRB_SYM(Binding)));
443
444
2
  if (proc && !MRB_PROC_CFUNC_P(proc)) {
445
2
    const mrb_irep *irep = proc->body.irep;
446
2
    mrb_obj_iv_set(mrb, binding, MRB_SYM(pc), mrb_fixnum_value(mrb->c->ci[-1].pc - irep->iseq - 1 /* step back */));
447
2
  }
448
2
  proc = binding_wrap_lvspace(mrb, proc, &env);
449
450
2
  mrb_obj_iv_set(mrb, binding, MRB_SYM(proc), mrb_obj_value((void*)proc));
451
2
  mrb_obj_iv_set(mrb, binding, MRB_SYM(recv), recv);
452
2
  mrb_obj_iv_set(mrb, binding, MRB_SYM(env), mrb_obj_value(env));
453
454
2
  return mrb_obj_value(binding);
455
2
}
456
457
/*
458
 * call-seq:
459
 *   binding -> binding
460
 *
461
 * Returns a Binding object, describing the variable and method bindings
462
 * at the point of call. This object can be used when calling eval to
463
 * execute the evaluated command in this environment.
464
 *
465
 *   def get_binding(param)
466
 *     binding
467
 *   end
468
 *   b = get_binding("hello")
469
 *   b.eval("param")  #=> "hello"
470
 */
471
static mrb_noreturn void
472
caller_error(mrb_state *mrb)
473
0
{
474
0
  mrb_raise(mrb, E_RUNTIME_ERROR, "Cannot create Binding object for non-Ruby caller");
475
0
}
476
477
static mrb_value
478
mrb_f_binding(mrb_state *mrb, mrb_value self)
479
2
{
480
2
  struct RProc *proc;
481
2
  struct REnv *env;
482
483
2
  if (mrb->c->ci->cci != 0) {
484
0
    caller_error(mrb);
485
0
  }
486
2
  proc = (struct RProc*)mrb_proc_get_caller(mrb, &env);
487
2
  if (!env || MRB_PROC_CFUNC_P(proc)) {
488
0
    caller_error(mrb);
489
0
  }
490
2
  return mrb_binding_new(mrb, proc, self, env);
491
2
}
492
493
void
494
mrb_mruby_binding_gem_init(mrb_state *mrb)
495
15.3k
{
496
15.3k
  struct RClass *binding = mrb_define_class_id(mrb, MRB_SYM(Binding), mrb->object_class);
497
15.3k
  MRB_SET_INSTANCE_TT(binding, MRB_TT_OBJECT);
498
15.3k
  MRB_UNDEF_ALLOCATOR(binding);
499
15.3k
  mrb_undef_class_method_id(mrb, binding, MRB_SYM(new));
500
15.3k
  mrb_undef_class_method_id(mrb, binding, MRB_SYM(allocate));
501
502
15.3k
  mrb_define_module_function_id(mrb, mrb->kernel_module, MRB_SYM(binding), mrb_f_binding, MRB_ARGS_NONE());
503
504
15.3k
  mrb_define_private_method_id(mrb, binding, MRB_SYM(initialize_copy), binding_initialize_copy, MRB_ARGS_REQ(1));
505
15.3k
  mrb_define_method_id(mrb, binding, MRB_SYM_Q(local_variable_defined), binding_local_variable_defined_p, MRB_ARGS_REQ(1));
506
15.3k
  mrb_define_method_id(mrb, binding, MRB_SYM(local_variable_get), binding_local_variable_get, MRB_ARGS_REQ(1));
507
15.3k
  mrb_define_method_id(mrb, binding, MRB_SYM(local_variable_set), binding_local_variable_set, MRB_ARGS_REQ(2));
508
15.3k
  mrb_define_method_id(mrb, binding, MRB_SYM(local_variables), binding_local_variables, MRB_ARGS_NONE());
509
15.3k
  mrb_define_method_id(mrb, binding, MRB_SYM(receiver), binding_receiver, MRB_ARGS_NONE());
510
15.3k
  mrb_define_method_id(mrb, binding, MRB_SYM(source_location), binding_source_location, MRB_ARGS_NONE());
511
15.3k
  mrb_define_method_id(mrb, binding, MRB_SYM(inspect), mrb_any_to_s, MRB_ARGS_NONE());
512
15.3k
}
513
514
void
515
mrb_mruby_binding_gem_final(mrb_state *mrb)
516
15.3k
{
517
15.3k
}