1 /*
2 ** proc.c - Proc class
3 **
4 ** See Copyright Notice in mruby.h
5 */
6 
7 #include <mruby.h>
8 #include <mruby/class.h>
9 #include <mruby/proc.h>
10 #include <mruby/opcode.h>
11 
12 static mrb_code call_iseq[] = {
13   OP_CALL,
14 };
15 
16 struct RProc*
mrb_proc_new(mrb_state * mrb,mrb_irep * irep)17 mrb_proc_new(mrb_state *mrb, mrb_irep *irep)
18 {
19   struct RProc *p;
20   mrb_callinfo *ci = mrb->c->ci;
21 
22   p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
23   if (ci) {
24     struct RClass *tc = NULL;
25 
26     if (ci->proc) {
27       tc = MRB_PROC_TARGET_CLASS(ci->proc);
28     }
29     if (tc == NULL) {
30       tc = ci->target_class;
31     }
32     p->upper = ci->proc;
33     p->e.target_class = tc;
34   }
35   p->body.irep = irep;
36   mrb_irep_incref(mrb, irep);
37 
38   return p;
39 }
40 
41 static struct REnv*
env_new(mrb_state * mrb,mrb_int nlocals)42 env_new(mrb_state *mrb, mrb_int nlocals)
43 {
44   struct REnv *e;
45   mrb_callinfo *ci = mrb->c->ci;
46   int bidx;
47 
48   e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, NULL);
49   MRB_ENV_SET_STACK_LEN(e, nlocals);
50   bidx = ci->argc;
51   if (ci->argc < 0) bidx = 2;
52   else bidx += 1;
53   MRB_ENV_SET_BIDX(e, bidx);
54   e->mid = ci->mid;
55   e->stack = mrb->c->stack;
56   e->cxt = mrb->c;
57 
58   return e;
59 }
60 
61 static void
closure_setup(mrb_state * mrb,struct RProc * p)62 closure_setup(mrb_state *mrb, struct RProc *p)
63 {
64   mrb_callinfo *ci = mrb->c->ci;
65   struct RProc *up = p->upper;
66   struct REnv *e = NULL;
67 
68   if (ci && ci->env) {
69     e = ci->env;
70   }
71   else if (up) {
72     struct RClass *tc = MRB_PROC_TARGET_CLASS(p);
73 
74     e = env_new(mrb, up->body.irep->nlocals);
75     ci->env = e;
76     if (tc) {
77       e->c = tc;
78       mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc);
79     }
80   }
81   if (e) {
82     p->e.env = e;
83     p->flags |= MRB_PROC_ENVSET;
84     mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
85   }
86 }
87 
88 struct RProc*
mrb_closure_new(mrb_state * mrb,mrb_irep * irep)89 mrb_closure_new(mrb_state *mrb, mrb_irep *irep)
90 {
91   struct RProc *p = mrb_proc_new(mrb, irep);
92 
93   closure_setup(mrb, p);
94   return p;
95 }
96 
97 MRB_API struct RProc*
mrb_proc_new_cfunc(mrb_state * mrb,mrb_func_t func)98 mrb_proc_new_cfunc(mrb_state *mrb, mrb_func_t func)
99 {
100   struct RProc *p;
101 
102   p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
103   p->body.func = func;
104   p->flags |= MRB_PROC_CFUNC_FL;
105   p->upper = 0;
106   p->e.target_class = 0;
107 
108   return p;
109 }
110 
111 MRB_API struct RProc*
mrb_proc_new_cfunc_with_env(mrb_state * mrb,mrb_func_t func,mrb_int argc,const mrb_value * argv)112 mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const mrb_value *argv)
113 {
114   struct RProc *p = mrb_proc_new_cfunc(mrb, func);
115   struct REnv *e;
116   int i;
117 
118   p->e.env = e = env_new(mrb, argc);
119   p->flags |= MRB_PROC_ENVSET;
120   mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
121   MRB_ENV_UNSHARE_STACK(e);
122   e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc);
123   if (argv) {
124     for (i = 0; i < argc; ++i) {
125       e->stack[i] = argv[i];
126     }
127   }
128   else {
129     for (i = 0; i < argc; ++i) {
130       SET_NIL_VALUE(e->stack[i]);
131     }
132   }
133   return p;
134 }
135 
136 MRB_API struct RProc*
mrb_closure_new_cfunc(mrb_state * mrb,mrb_func_t func,int nlocals)137 mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals)
138 {
139   return mrb_proc_new_cfunc_with_env(mrb, func, nlocals, NULL);
140 }
141 
142 MRB_API mrb_value
mrb_proc_cfunc_env_get(mrb_state * mrb,mrb_int idx)143 mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx)
144 {
145   struct RProc *p = mrb->c->ci->proc;
146   struct REnv *e;
147 
148   if (!p || !MRB_PROC_CFUNC_P(p)) {
149     mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc.");
150   }
151   e = MRB_PROC_ENV(p);
152   if (!e) {
153     mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv.");
154   }
155   if (idx < 0 || MRB_ENV_STACK_LEN(e) <= idx) {
156     mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %S (expected: 0 <= index < %S)",
157                mrb_fixnum_value(idx), mrb_fixnum_value(MRB_ENV_STACK_LEN(e)));
158   }
159 
160   return e->stack[idx];
161 }
162 
163 void
mrb_proc_copy(struct RProc * a,struct RProc * b)164 mrb_proc_copy(struct RProc *a, struct RProc *b)
165 {
166   if (a->body.irep) {
167     /* already initialized proc */
168     return;
169   }
170   a->flags = b->flags;
171   a->body = b->body;
172   if (!MRB_PROC_CFUNC_P(a) && a->body.irep) {
173     a->body.irep->refcnt++;
174   }
175   a->upper = b->upper;
176   a->e.env = b->e.env;
177   /* a->e.target_class = a->e.target_class; */
178 }
179 
180 static mrb_value
mrb_proc_s_new(mrb_state * mrb,mrb_value proc_class)181 mrb_proc_s_new(mrb_state *mrb, mrb_value proc_class)
182 {
183   mrb_value blk;
184   mrb_value proc;
185   struct RProc *p;
186 
187   mrb_get_args(mrb, "&", &blk);
188   if (mrb_nil_p(blk)) {
189     /* Calling Proc.new without a block is not implemented yet */
190     mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block");
191   }
192   p = (struct RProc *)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb_class_ptr(proc_class));
193   mrb_proc_copy(p, mrb_proc_ptr(blk));
194   proc = mrb_obj_value(p);
195   mrb_funcall_with_block(mrb, proc, mrb_intern_lit(mrb, "initialize"), 0, NULL, proc);
196   if (!MRB_PROC_STRICT_P(p) &&
197       mrb->c->ci > mrb->c->cibase && MRB_PROC_ENV(p) == mrb->c->ci[-1].env) {
198     p->flags |= MRB_PROC_ORPHAN;
199   }
200   return proc;
201 }
202 
203 static mrb_value
mrb_proc_init_copy(mrb_state * mrb,mrb_value self)204 mrb_proc_init_copy(mrb_state *mrb, mrb_value self)
205 {
206   mrb_value proc;
207 
208   mrb_get_args(mrb, "o", &proc);
209   if (mrb_type(proc) != MRB_TT_PROC) {
210     mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc");
211   }
212   mrb_proc_copy(mrb_proc_ptr(self), mrb_proc_ptr(proc));
213   return self;
214 }
215 
216 int
mrb_proc_cfunc_p(struct RProc * p)217 mrb_proc_cfunc_p(struct RProc *p)
218 {
219   return MRB_PROC_CFUNC_P(p);
220 }
221 
222 /* 15.2.17.4.2 */
223 static mrb_value
mrb_proc_arity(mrb_state * mrb,mrb_value self)224 mrb_proc_arity(mrb_state *mrb, mrb_value self)
225 {
226   struct RProc *p = mrb_proc_ptr(self);
227   struct mrb_irep *irep;
228   mrb_code *pc;
229   mrb_aspec aspec;
230   int ma, op, ra, pa, arity;
231 
232   if (MRB_PROC_CFUNC_P(p)) {
233     /* TODO cfunc aspec not implemented yet */
234     return mrb_fixnum_value(-1);
235   }
236 
237   irep = p->body.irep;
238   if (!irep) {
239     return mrb_fixnum_value(0);
240   }
241 
242   pc = irep->iseq;
243   /* arity is depend on OP_ENTER */
244   if (*pc != OP_ENTER) {
245     return mrb_fixnum_value(0);
246   }
247 
248   aspec = PEEK_W(pc+1);
249   ma = MRB_ASPEC_REQ(aspec);
250   op = MRB_ASPEC_OPT(aspec);
251   ra = MRB_ASPEC_REST(aspec);
252   pa = MRB_ASPEC_POST(aspec);
253   arity = ra || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa;
254 
255   return mrb_fixnum_value(arity);
256 }
257 
258 /* 15.3.1.2.6  */
259 /* 15.3.1.3.27 */
260 /*
261  * call-seq:
262  *   lambda { |...| block }  -> a_proc
263  *
264  * Equivalent to <code>Proc.new</code>, except the resulting Proc objects
265  * check the number of parameters passed when called.
266  */
267 static mrb_value
proc_lambda(mrb_state * mrb,mrb_value self)268 proc_lambda(mrb_state *mrb, mrb_value self)
269 {
270   mrb_value blk;
271   struct RProc *p;
272 
273   mrb_get_args(mrb, "&", &blk);
274   if (mrb_nil_p(blk)) {
275     mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block");
276   }
277   if (mrb_type(blk) != MRB_TT_PROC) {
278     mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc");
279   }
280   p = mrb_proc_ptr(blk);
281   if (!MRB_PROC_STRICT_P(p)) {
282     struct RProc *p2 = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, p->c);
283     mrb_proc_copy(p2, p);
284     p2->flags |= MRB_PROC_STRICT;
285     return mrb_obj_value(p2);
286   }
287   return blk;
288 }
289 
290 void
mrb_init_proc(mrb_state * mrb)291 mrb_init_proc(mrb_state *mrb)
292 {
293   struct RProc *p;
294   mrb_method_t m;
295   mrb_irep *call_irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep));
296   static const mrb_irep mrb_irep_zero = { 0 };
297 
298   *call_irep = mrb_irep_zero;
299   call_irep->flags = MRB_ISEQ_NO_FREE;
300   call_irep->iseq = call_iseq;
301   call_irep->ilen = 1;
302   call_irep->nregs = 2;         /* receiver and block */
303 
304   mrb_define_class_method(mrb, mrb->proc_class, "new", mrb_proc_s_new, MRB_ARGS_NONE()|MRB_ARGS_BLOCK());
305   mrb_define_method(mrb, mrb->proc_class, "initialize_copy", mrb_proc_init_copy, MRB_ARGS_REQ(1));
306   mrb_define_method(mrb, mrb->proc_class, "arity", mrb_proc_arity, MRB_ARGS_NONE());
307 
308   p = mrb_proc_new(mrb, call_irep);
309   MRB_METHOD_FROM_PROC(m, p);
310   mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "call"), m);
311   mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "[]"), m);
312 
313   mrb_define_class_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.2.6  */
314   mrb_define_method(mrb, mrb->kernel_module,       "lambda", proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.3.27 */
315 }
316