function.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  1. /* Definitions for the function types.
  2. This file is part of khipu.
  3. khipu is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Lesser General Public License as published by
  5. the Free Software Foundation; either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>. */
  13. #include <cstdio>
  14. #include "khipu.hpp"
  15. KP_DECLS_BEGIN
  16. result<object> alloc_fct (interpreter *interp, uint32_t flags)
  17. {
  18. auto eg = KP_TRY (evh_guard::make (interp));
  19. auto fp = alloch<function> ();
  20. fp->bcode = fp->vals = NIL;
  21. fp->env = fp->name = NIL;
  22. fp->vo_full |= flags;
  23. fp->min_argc = fp->max_argc = 0;
  24. interp->alval = fp->as_obj ();
  25. gc_register (interp, fp);
  26. return (interp->alval);
  27. }
  28. bool eq_xx (interpreter *interp, object x, object y)
  29. {
  30. if ((as_varobj(x)->vo_full ^ as_varobj(y)->vo_full) &
  31. function_base::native_flag)
  32. return (false);
  33. else if (as_varobj(x)->flagged_p (function_base::native_flag))
  34. return (as_native_fct(x)->fct == as_native_fct(y)->fct);
  35. else
  36. return (fct_bcode (x) == fct_bcode (y) &&
  37. fct_vals (x) == fct_vals (y) && fct_env (x) == fct_env (y));
  38. }
  39. int function::max_stack () const
  40. {
  41. const bvector *bp = as_bvector (this->bcode);
  42. auto start = bp->data, end = start + bp->nbytes;
  43. int max_sp = 0, sp = 0;
  44. while (start < end)
  45. {
  46. auto instr = bcode_get (*start++);
  47. int opc = instr->opcode ();
  48. if (instr->loadf_p () || opc == OP_BRBOUND || opc == OP_BRBOUNDL ||
  49. opc == OP_BIND || opc == OP_BINDL)
  50. ++sp;
  51. else if (opc == OP_TRYBEGIN || opc == OP_TRYBEGINL || opc == OP_VFRAME)
  52. sp += 2;
  53. else if (instr->branch_p ())
  54. switch (opc)
  55. {
  56. case OP_BRNEQ: case OP_BRNEQL:
  57. --sp;
  58. case OP_BRN: case OP_BRNL: case OP_BRT: case OP_BRTL:
  59. --sp;
  60. }
  61. else if (opc == OP_POP || opc == OP_IS || opc == OP_CONS ||
  62. opc == OP_SETAPOP || opc == OP_SETAPOPL || opc == OP_TRYEND)
  63. --sp;
  64. else if (instr->nops () > 0)
  65. {
  66. int val = instr->getsarg (start +
  67. instr->argsize () * (instr->nops () - 1));
  68. if (opc == OP_KWARGS)
  69. sp += abs (val) - instr->getsarg (start);
  70. else if (instr->callf_p () || opc == OP_SKIP || opc == OP_SKIPL)
  71. sp -= val;
  72. }
  73. if (sp > max_sp)
  74. max_sp = sp;
  75. start += instr->argsize () * instr->nops ();
  76. }
  77. return (max_sp + interpreter::frame_size + 1);
  78. }
  79. result<int64_t> write_x (interpreter *interp, stream *strm,
  80. object obj, io_info&)
  81. {
  82. int64_t ret = KP_TRY (strm->write (interp, "#<function ", 11));
  83. object name = fct_name (obj);
  84. if (name == NIL)
  85. { ret += KP_TRY (strm->write (interp, "#:fct", 5)); }
  86. else
  87. { ret += KP_TRY (xwrite (interp, strm, name)); }
  88. char buf[64];
  89. ret += KP_TRY (strm->write (interp, buf,
  90. sprintf (buf, " at %p>", unmask (obj))));
  91. return (ret);
  92. }
  93. enum
  94. {
  95. FCT_PACK_BUILTIN = 0,
  96. FCT_PACK_BCODE = 1
  97. };
  98. result<int64_t> pack_x (interpreter *interp, stream *strm,
  99. object obj, pack_info& info)
  100. {
  101. if (native_fct_p (obj))
  102. {
  103. const char *name = str_cdata (symname (fct_name (obj)));
  104. int idx = builtin_idx (interp, name) - symbol::N_SPECFORMS;
  105. int64_t ret = KP_TRY (strm->putb (interp, FCT_PACK_BUILTIN));
  106. ret += KP_TRY (strm->write (interp, &idx));
  107. return (ret);
  108. }
  109. const function *xp = as_fct (obj);
  110. int ivs[] =
  111. {
  112. (int)(xp->vo_full &
  113. (function::artificial_flag | function::kwargs_flag |
  114. function::genericfn_flag)),
  115. xp->max_sp, xp->min_argc, xp->max_argc
  116. };
  117. int64_t ret = KP_TRY (strm->putb (interp, FCT_PACK_BCODE));
  118. ret += KP_TRY (strm->write (interp, ivs, sizeof (ivs)));
  119. ret += KP_TRY (xpack (interp, strm, xp->bcode, info));
  120. ret += KP_TRY (xpack (interp, strm, xp->vals, info));
  121. ret += KP_TRY ((xp->vo_flags & function::genericfn_flag) ?
  122. NIL : xpack (interp, strm, xp->env, info));
  123. return (ret);
  124. }
  125. static result<object> mcache_make (interpreter *);
  126. result<object> unpack_x (interpreter *interp, stream *strm,
  127. pack_info& info, bool save)
  128. {
  129. valref saved_pos (interp, *info.offset);
  130. int code = KP_TRY (strm->getb (interp));
  131. bool rv;
  132. if (code == FCT_PACK_BUILTIN)
  133. {
  134. int idx = -1;
  135. rv = KP_TRY (strm->sread (interp, &idx));
  136. if (!rv || (interp->retval = builtin_fct (interp, idx)) == UNBOUND)
  137. return (info.error ("invalid builtin function index read"));
  138. return (interp->retval);
  139. }
  140. int vals[4];
  141. {
  142. auto tmp = KP_TRY (strm->read (interp, vals, sizeof (vals)));
  143. rv = tmp == sizeof (vals);
  144. }
  145. if (!rv)
  146. return (info.error ("invalid function members read"));
  147. valref fn = KP_TRY (alloc_fct (interp, vals[0]));
  148. function *fp = as_fct (*fn);
  149. fp->bcode = KP_TRY (xunpack (interp, strm, info));
  150. if (!bvector_p (fp->bcode))
  151. return (info.error ("invalid function code vector"));
  152. fp->vals = KP_TRY (xunpack (interp, strm, info));
  153. if (!array_p (fp->vals) && fp->vals != NIL)
  154. return (info.error ("invalid function values read"));
  155. fp->env = KP_TRY (xunpack (interp, strm, info));
  156. if (!array_p (fp->env) && fp->env != NIL)
  157. return (info.error ("invalid function environment read"));
  158. fp->vo_flags = vals[0];
  159. fp->max_sp = vals[1];
  160. fp->min_argc = vals[2];
  161. fp->max_argc = vals[3];
  162. if (fp->vo_flags & function::genericfn_flag)
  163. {
  164. valref tab = KP_TRY (alloc_table (interp, NIL, NIL));
  165. valref cache = KP_TRY (mcache_make (interp));
  166. fp->env = KP_TRY (alloc_array (interp, 2));
  167. xaref(fp->env, 0) = *tab;
  168. xaref(fp->env, 1) = *cache;
  169. }
  170. if (save)
  171. KP_VTRY (info.add_mapping (interp, *info.offset, *fn));
  172. kp_return (*fn);
  173. }
  174. const char* fct_sname (object obj)
  175. {
  176. object name = fct_p (obj) ? fct_name (obj) : NIL;
  177. return (symbol_p (name) ? str_cdata (symname (name)) : "#:fct");
  178. }
  179. // Generic functions and methods.
  180. /* In our multimethods implementation, each generic function is a closure that
  181. * stores 2 tables in order to compute the applicable method list.
  182. *
  183. * The first table is a basic mapping from a types array to a concrete method,
  184. * and is updated on each 'defmeth' form, or with a call to 'meth-add'.
  185. *
  186. * The second table acts as a cache, and is queried on each call to the
  187. * generic function. If there is a miss, the applicable methods list is
  188. * computed at runtime, and the cache is updated for the type list of the
  189. * arguments that were used on the call to the generic function.
  190. *
  191. * The cache has a fixed size, and uses hash chaining in order to make it
  192. * more compact. The load factor is effectively 2x, but we keep count on
  193. * the occupancy, and randomly evict entries once that limit is reached.
  194. * Much like with regular tables, we have both a single-threaded and a
  195. * multi-thread safe implementation that we pick based on the number
  196. * of running threads. */
  197. static const uint32_t MCACHE_SIZE = 64;
  198. static const uint32_t MCACHE_LIMIT = MCACHE_SIZE * 2;
  199. /* Cache entry layout:
  200. * sorted methods | next | types... */
  201. static const uint32_t TYPE_IDX = 2;
  202. static result<object>
  203. mcache_make (interpreter *interp)
  204. {
  205. object ax = KP_TRY (alloc_array (interp, MCACHE_SIZE + 1, fixint (0)));
  206. array *rv = as_array (ax);
  207. ++rv->data, --rv->len;
  208. *(rv->data - 1) = MCACHE_LIMIT; // Store limit in a hidden entry.
  209. kp_return (interp->alval);
  210. }
  211. static uint32_t
  212. mcache_hash (interpreter *interp, const array *key)
  213. {
  214. /* In order for the hash value to be stable, we compute it on the
  215. * type names of the key array. */
  216. uint32_t rv = 0;
  217. for (uint32_t idx = 0; idx < key->len; ++idx)
  218. {
  219. object obj = key->data[idx];
  220. rv = mix_hash (rv, typespec_p (obj) ?
  221. hash_S (interp, type_name (obj)) : 1);
  222. }
  223. return (rv);
  224. }
  225. static atomic_t*
  226. mcache_limit (array *ap)
  227. {
  228. return ((atomic_t *)(ap->data - 1));
  229. }
  230. static inline bool
  231. mcache_eq (const array *x, const array *y)
  232. {
  233. for (uint32_t i = TYPE_IDX; i < x->len; ++i)
  234. if (x->data[i] != y->data[i - TYPE_IDX])
  235. return (false);
  236. return (true);
  237. }
  238. static object
  239. mcache_get_at (object obj, uint32_t idx, const array *key)
  240. {
  241. // Returns the method list, or the bucket index in case of a miss.
  242. while (obj != fixint (0))
  243. {
  244. array *ap = as_array (obj);
  245. if (mcache_eq (ap, key))
  246. return (ap->data[0]);
  247. obj = ap->data[1];
  248. }
  249. return (fixint (idx));
  250. }
  251. static object
  252. mcache_get (interpreter *interp, const array *cache, const array *key)
  253. {
  254. uint32_t idx = mcache_hash (interp, key) & (cache->len - 1);
  255. kp_return (mcache_get_at (cache->data[idx], idx, key));
  256. }
  257. static uint32_t
  258. mcache_bucket_len (object bucket)
  259. {
  260. uint32_t ret = 0;
  261. for (; bucket != fixint (0); bucket = xaref (bucket, 1), ++ret) ;
  262. return (ret);
  263. }
  264. static inline void
  265. mcache_shuffle (interpreter *interp, uint32_t *idxp)
  266. {
  267. for (uint32_t i = 0; i < MCACHE_SIZE; ++i)
  268. idxp[i] = i;
  269. for (uint32_t i = 0; i < MCACHE_SIZE; ++i)
  270. swap (idxp[i], idxp[(i + interp->xrand ()) % MCACHE_SIZE]);
  271. }
  272. static void
  273. mcache_put_lk (interpreter *interp, array *cache, array *key, uint32_t idx)
  274. {
  275. // Link the new entry in the bucket.
  276. key->data[1] = cache->data[idx];
  277. cache->data[idx] = key->as_obj ();
  278. if (kp_likely (--*mcache_limit(cache)))
  279. return; // There's still room - Done.
  280. else if (key->data[1] != fixint (0))
  281. { // The new entry is chained to others - Remove them.
  282. *mcache_limit(cache) += mcache_bucket_len (key->data[1]);
  283. key->data[1] = fixint (0);
  284. return;
  285. }
  286. uint32_t ixs[MCACHE_SIZE];
  287. mcache_shuffle (interp, ixs);
  288. for (uint32_t i = 0; i < MCACHE_SIZE; ++i)
  289. {
  290. uint32_t i2 = ixs[i];
  291. if (idx == i2)
  292. continue; // Don't evict this entry.
  293. object prev = cache->data[i2];
  294. if (prev != fixint (0))
  295. {
  296. *mcache_limit(cache) += mcache_bucket_len (prev);
  297. cache->data[i2] = fixint (0);
  298. break;
  299. }
  300. }
  301. }
  302. static result<void>
  303. mcache_clr_lk (interpreter *interp, array *vec)
  304. {
  305. array *cache = as_array (vec->data[1]);
  306. memset (cache->data, 0, MCACHE_SIZE * sizeof (*cache->data));
  307. *mcache_limit(cache) = MCACHE_LIMIT;
  308. return (0);
  309. }
  310. static void
  311. mcache_put_mt (interpreter *interp, array *cache, array *key, uint32_t idx)
  312. {
  313. while (true)
  314. {
  315. object prev = cache->data[idx];
  316. key->data[1] = prev;
  317. if (!fixint_p (mcache_get_at (prev, idx, key)))
  318. return; // This very entry was added in the interim.
  319. else if (atomic_cas_bool ((atomic_t *)&cache->data[idx],
  320. prev, key->as_obj ()))
  321. break;
  322. atomic_spin_nop ();
  323. }
  324. if (atomic_add (mcache_limit (cache), -1) > 0)
  325. return;
  326. uint32_t ixs[MCACHE_SIZE];
  327. mcache_shuffle (interp, ixs);
  328. for (uint32_t i = 0; i < MCACHE_SIZE; ++i)
  329. {
  330. uint32_t i2 = ixs[i];
  331. if (idx == i2)
  332. continue; // Don't evict our own entry.
  333. object prev = cache->data[i2];
  334. if (prev != fixint (0) &&
  335. atomic_cas_bool ((atomic_t *)&cache->data[i2], prev, fixint (0)))
  336. {
  337. atomic_add (mcache_limit (cache), mcache_bucket_len (prev));
  338. break;
  339. }
  340. }
  341. }
  342. static result<void>
  343. mcache_clr_mt (interpreter *interp, array *vec)
  344. {
  345. object nval = KP_TRY (mcache_make (interp));
  346. vec->data[1] = nval;
  347. return (gc_wbarrier (interp, vec->as_obj (), nval));
  348. }
  349. struct meth_comparator : public comparator
  350. {
  351. meth_comparator (interpreter *ip) : comparator (ip)
  352. {
  353. }
  354. result<bool> operator() (object x, object y)
  355. {
  356. const array *a = as_array (x), *b = as_array (y);
  357. for (uint32_t i = 1; i < a->len; ++i)
  358. if (a->data[i] > b->data[i])
  359. return (false);
  360. return (true);
  361. }
  362. };
  363. static bool
  364. types_apply_p (interpreter *interp, object types, object key, object *dp)
  365. {
  366. for (uint32_t i = 0; i < len_a (types); ++i)
  367. {
  368. object obj = xaref (types, i);
  369. if (obj == NIL)
  370. // No type. Distance is set to the maximum.
  371. *dp++ = fixint (FIXINT_MAX);
  372. else
  373. {
  374. int dist = subtype_p (obj, xaref (key, i));
  375. if (!dist)
  376. return (false);
  377. *dp++ = fixint (dist);
  378. }
  379. }
  380. return (true);
  381. }
  382. static result<object>
  383. methods_applicable (interpreter *interp, object mtable,
  384. object types, uint32_t nargs)
  385. {
  386. tmp_allocator ta { interp };
  387. table::iterator it { interp, mtable };
  388. uint32_t nmax = as_table(mtable)->capacity ();
  389. array *dist = (array *)ta.alloc (nmax * sizeof (*dist));
  390. object *dp = (object *)ta.alloc (nmax * (nargs + 2) * sizeof (*dp));
  391. uint32_t ix = 0;
  392. for (object *tp = dp + nmax; it.valid (); ++it)
  393. {
  394. if (!types_apply_p (interp, types, it.key (), tp + 1))
  395. continue;
  396. *tp = it.val ();
  397. dist[ix].data = tp;
  398. tp += (dist[ix].len = nargs + 1);
  399. dp[ix] = dist[ix].as_obj ();
  400. ++ix;
  401. }
  402. local_varobj<array> rv;
  403. rv.local_init (dp, ix);
  404. valref tmp (interp, rv.as_obj ());
  405. meth_comparator cmp { interp };
  406. deref (nsort_a (interp, *tmp, cmp));
  407. object ret = KP_TRY (alloc_cons (interp, ix));
  408. for (uint32_t i = 0; i < ix; ++i, ret = xcdr (ret))
  409. xcar(ret) = xaref (rv.data[i], 0);
  410. kp_return (interp->alval);
  411. }
  412. static inline bool
  413. meth_vec_p (object vec)
  414. {
  415. return (array_p (vec) && len_a (vec) == 2 &&
  416. table_p (xaref (vec, 0)) && array_p (xaref (vec, 1)) &&
  417. len_a (xaref (vec, 1)) == MCACHE_SIZE);
  418. }
  419. static result<object>
  420. meth_compute (interpreter *interp, object vec, const array *types)
  421. {
  422. array *cache = as_array (xaref (vec, 1));
  423. object meths = mcache_get (interp, cache, types);
  424. if (!fixint_p (meths))
  425. // Got a cache hit.
  426. kp_return (meths);
  427. // The lookup came up empty. Compute the applicable methods.
  428. int bucket = as_int (meths);
  429. meths = KP_TRY (methods_applicable (interp, xaref (vec, 0),
  430. types->as_obj (), types->len));
  431. if (meths == NIL)
  432. return (meths);
  433. interp->aux = meths;
  434. KP_VTRY (alloc_array (interp, TYPE_IDX + types->len, fixint (0)));
  435. array *ckey = as_array (interp->alval);
  436. ckey->data[0] = meths;
  437. copy_objs (&ckey->data[TYPE_IDX], types->data, types->len);
  438. (singlethr_p () ? mcache_put_lk : mcache_put_mt)
  439. (interp, cache, ckey, bucket);
  440. kp_return (meths);
  441. }
  442. static result<object>
  443. meth_compute (interpreter *interp, object vec, object *argv, int argc)
  444. {
  445. tmp_allocator ta { interp };
  446. object *tp = (object *)ta.alloc (argc * sizeof (*tp));
  447. for (int i = 0; i < argc; ++i)
  448. tp[i] = type (argv[i]);
  449. local_varobj<array> types;
  450. types.local_init (tp, argc);
  451. return (meth_compute (interp, vec, &types));
  452. }
  453. static inline uintptr_t meth_idx ()
  454. {
  455. return (symtlidx (symbol::meth_curr) - 1);
  456. }
  457. static result<object>
  458. meth_call (interpreter *interp, bool raise, object vec, object *argv, int argc)
  459. {
  460. object meths = KP_TRY (meth_compute (interp, vec, argv, argc));
  461. if (meths == NIL)
  462. {
  463. if (raise)
  464. return (interp->raise ("dispatch-error", "no applicable method found"));
  465. return (UNBOUND);
  466. }
  467. // Install the method list and call the first one.
  468. interp->tl_syms[meth_idx ()] = meths;
  469. interp->stkend -= interpreter::frame_size;
  470. *(interp->stkend - argc - 1) = xcar (meths);
  471. return (call_n (interp, argc));
  472. }
  473. static object
  474. genericfn_env (object fn)
  475. {
  476. return (xcar (xaref (fct_env (fn), 0)));
  477. }
  478. static inline bool
  479. genericfn_p (object fn)
  480. {
  481. return (fct_p (fn) && as_fct(fn)->flagged_p (function::genericfn_flag));
  482. }
  483. static inline bool
  484. meth_types_p (object ax, uint32_t argc)
  485. {
  486. if (!array_p (ax) || len_a (ax) != argc)
  487. return (false);
  488. for (uint32_t i = 0; i < argc; ++i)
  489. if (xaref (ax, i) != NIL && !typespec_p (xaref (ax, i)))
  490. return (false);
  491. return (true);
  492. }
  493. static inline bool
  494. meth_compatible_p (const function *gf, const function *meth)
  495. {
  496. /* A generic function and a method are congruent iff:
  497. * - The number of required parameters is equal
  498. * - The generic function has at least the same number of
  499. * optional parameters. */
  500. return (gf->min_argc == meth->min_argc &&
  501. (uint32_t)gf->max_argc >= (uint32_t)meth->max_argc);
  502. }
  503. static inline result<void>
  504. mcache_clr (interpreter *interp, array *vec, bool mt)
  505. {
  506. if (*mcache_limit(as_array (vec->data[1])) != MCACHE_LIMIT)
  507. return ((mt ? mcache_clr_mt : mcache_clr_lk) (interp, vec));
  508. return (0);
  509. }
  510. static result<void>
  511. meth_add (interpreter *interp, object gfn, object types, object meth)
  512. {
  513. if (!genericfn_p (gfn))
  514. return (interp->raise ("arg-error",
  515. "second argument must be a generic function"));
  516. else if (!meth_types_p (types, as_fct(gfn)->min_argc))
  517. return (interp->raise ("arg-error",
  518. "third argument must be a valid type array"));
  519. else if (!fct_p (meth) || !meth_compatible_p (as_fct (gfn), as_fct (meth)))
  520. return (interp->raise ("arg-error",
  521. "fourth argument must be a compatible function"));
  522. bool mt = !singlethr_p ();
  523. array *vec = as_array (genericfn_env (gfn));
  524. KP_VTRY (table_put (interp, vec->data[0], types, meth, mt));
  525. return (mcache_clr (interp, vec, mt));
  526. }
  527. static result<object>
  528. meth_del (interpreter *interp, object gfn, object types)
  529. {
  530. if (!genericfn_p (gfn))
  531. return (interp->raise ("arg-error",
  532. "second argument must be a generic function"));
  533. bool mt = !singlethr_p ();
  534. array *vec = as_array (genericfn_env (gfn));
  535. object rv = KP_TRY (table_pop (interp, vec->data[0], types, NIL, mt));
  536. KP_VTRY (mcache_clr (interp, vec, mt));
  537. return (rv == NIL ? rv : symbol::t);
  538. }
  539. static object
  540. meth_test (interpreter *interp, object prev, object nval)
  541. {
  542. return (genericfn_p (prev) && fct_p (nval) &&
  543. meth_compatible_p (as_fct (prev), as_fct (nval)) ?
  544. symbol::t : NIL);
  545. }
  546. result<bool> method_call (interpreter *interp, uint32_t argc)
  547. {
  548. call_guard g { interp, argc + 1};
  549. object gfn = *(interp->stkend - argc - 1);
  550. if (!genericfn_p (gfn))
  551. return (false);
  552. object vec = genericfn_env (gfn);
  553. dbinding curr_meth { interp };
  554. KP_VTRY (curr_meth.init (meth_idx () + 1, NIL),
  555. interp->push_frame (NIL, argc, 0),
  556. meth_call (interp, false, vec, interp->stkend -
  557. argc - interpreter::frame_size, argc));
  558. return (interp->retval != UNBOUND);
  559. }
  560. result<object> p_meth_ctl (interpreter *interp, object *argv, int argc)
  561. {
  562. if (!fixint_p (*argv))
  563. return (interp->raise ("type-error", "first argument must be an integer"));
  564. switch (as_int (*argv))
  565. {
  566. case 0:
  567. if (argc != 3)
  568. return (interp->raise_nargs (3, 3, argc));
  569. kp_return (meth_test (interp, argv[1], argv[2]));
  570. case 1:
  571. return (mcache_make (interp));
  572. case 2:
  573. // (%meth-ctl 2 name method generic-fct)
  574. if (argc != 4 || !fct_p (argv[2]) || !fct_p (argv[3]) ||
  575. !array_p (fct_env (argv[3])) ||
  576. !meth_vec_p (genericfn_env (argv[3])))
  577. kp_return (NIL);
  578. else if (symbol_p (argv[1]))
  579. as_fct(argv[3])->name = argv[1];
  580. as_fct(argv[3])->set_flag (function::genericfn_flag |
  581. (as_fct(argv[2])->vo_full &
  582. function::kwargs_flag));
  583. kp_return (argv[3]);
  584. case 3:
  585. // (%meth-ctl 3 generic-fct args...)
  586. interp->cur_frame = as_int (interp->stack[interp->cur_frame - 4]);
  587. return (meth_call (interp, true, argv[1], argv + 2, argc - 2));
  588. case 4:
  589. // (%meth-ctl 4 generic-fct types-array)
  590. if (argc != 3)
  591. return (interp->raise_nargs (3, 3, argc));
  592. else if (!genericfn_p (argv[1]))
  593. return (interp->raise ("arg-error", "second argument must be a "
  594. "generic function"));
  595. else if (!meth_types_p (argv[2], as_fct(argv[1])->min_argc))
  596. return (interp->raise ("arg-error", "third argument must be a "
  597. "compatible types array"));
  598. return (meth_compute (interp, genericfn_env (argv[1]),
  599. as_array (argv[2])));
  600. case 5:
  601. // (%meth-ctl 5 generic-fct types-array method)
  602. if (argc != 4)
  603. return (interp->raise_nargs (4, 4, argc));
  604. KP_VTRY (meth_add (interp, argv[1], argv[2], argv[3]));
  605. kp_return (argv[3]);
  606. case 6:
  607. // (%meth-ctl 6 generic-fct types-array)
  608. if (argc != 3)
  609. return (interp->raise_nargs (3, 3, argc));
  610. interp->retval = KP_TRY (meth_del (interp, argv[1], argv[2]));
  611. return (interp->retval);
  612. default:
  613. return (interp->raise ("arg-error", "invalid code specified"));
  614. }
  615. }
  616. static result<void>
  617. kwargs_set (interpreter *interp, object *argv, int& argc, kwpair *px)
  618. {
  619. const char *name = px[0].name;
  620. size_t len = strlen (name);
  621. for (int i = 0; i < argc; i += 2)
  622. {
  623. if (!keyword_p (argv[i]))
  624. return (interp->raise ("arg-error",
  625. KP_SPRINTF (interp,
  626. "expected a keyword, got: %Q",
  627. argv[i])));
  628. const string *nm = as_str (symname (argv[i]));
  629. if (nm->nbytes == len && memcmp (nm->data, name, len) == 0)
  630. {
  631. *px[1].obj = argv[i + 1];
  632. swap (argv[i + 0], argv[argc - 2]);
  633. swap (argv[i + 1], argv[argc - 1]);
  634. argc -= 2;
  635. break;
  636. }
  637. }
  638. return (0);
  639. }
  640. result<void> kwargs_parse_pairs (interpreter *interp, object *argv,
  641. int argc, kwpair *pairs, int npair)
  642. {
  643. for (int i = 0; i < npair; i += 2)
  644. KP_VTRY (kwargs_set (interp, argv, argc, pairs + i));
  645. return (0);
  646. }
  647. KP_DECLS_END