1 /*  Part of SWI-Prolog
2 
3     Author:        Jan Wielemaker
4     E-mail:        J.Wielemaker@vu.nl
5     WWW:           http://www.swi-prolog.org
6     Copyright (c)  1985-2020, University of Amsterdam
7                               VU University Amsterdam
8 			      CWI, Amsterdam
9     All rights reserved.
10 
11     Redistribution and use in source and binary forms, with or without
12     modification, are permitted provided that the following conditions
13     are met:
14 
15     1. Redistributions of source code must retain the above copyright
16        notice, this list of conditions and the following disclaimer.
17 
18     2. Redistributions in binary form must reproduce the above copyright
19        notice, this list of conditions and the following disclaimer in
20        the documentation and/or other materials provided with the
21        distribution.
22 
23     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27     COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34     POSSIBILITY OF SUCH DAMAGE.
35 */
36 
37 /*#define O_DEBUG 1*/
38 #include "pl-incl.h"
39 #include "pl-comp.h"
40 #include "pl-dbref.h"
41 #include "pl-event.h"
42 #include "pl-tabling.h"
43 
44 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
45 General  handling  of  procedures:  creation;  adding/removing  clauses;
46 finding source files, etc.
47 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
48 
49 #undef LD
50 #define LD LOCAL_LD
51 
52 static void	resetProcedure(Procedure proc, bool isnew);
53 static atom_t	autoLoader(Definition def);
54 static Procedure visibleProcedure(functor_t f, Module m ARG_LD);
55 static void	freeClauseRef(ClauseRef cref);
56 static int	setDynamicDefinition_unlocked(Definition def, bool isdyn);
57 static void	registerDirtyDefinition(Definition def ARG_LD);
58 static void	unregisterDirtyDefinition(Definition def);
59 static gen_t	ddi_oldest_generation(DirtyDefInfo ddi);
60 
61 #ifdef O_DEBUG
62 static int	isProtectedCRef(ClauseRef cref);
63 #else
64 #define unprotectCRef(cref) (void)0
65 #define protectCRef(cref)   (void)0
66 #endif
67 
68 /* Enforcing this limit demands we propagate NULL from lookupProcedure()
69    through the whole system.  This is not done
70 */
71 #define O_PROGLIMIT_INCL_PRED 0
72 #define SIZEOF_PROC (sizeof(struct procedure) + sizeof(struct definition))
73 
74 Procedure
lookupProcedure(functor_t f,Module m)75 lookupProcedure(functor_t f, Module m)
76 { GET_LD
77   Procedure proc, oproc;
78   Definition def;
79 
80   if ( (proc = lookupHTable(m->procedures, (void *)f)) )
81   { DEBUG(MSG_PROC, Sdprintf("lookupProcedure(%s) --> %s\n",
82 			     PL_atom_chars(m->name),
83 			     procedureName(proc)));
84     return proc;
85   }
86 
87 #if O_PROGLIMIT_INCL_PRED
88   if ( m->code_limit &&
89        m->code_size + SIZEOF_PROC > m->code_limit )
90   { PL_error(NULL, 0, NULL, ERR_RESOURCE, ATOM_program_space);
91     return NULL;
92   }
93 #endif
94 
95   proc = (Procedure)  allocHeapOrHalt(sizeof(struct procedure));
96   def  = (Definition) allocHeapOrHalt(sizeof(struct definition));
97   proc->definition = def;
98   proc->flags      = 0;
99   proc->source_no  = 0;
100 
101   memset(def, 0, sizeof(*def));
102   def->functor = valueFunctor(f);
103   def->module  = m;
104   def->shared  = 1;
105   if ( def->functor->arity > 0 )
106   { def->impl.any.args = allocHeapOrHalt(sizeof(arg_info)*def->functor->arity);
107     memset(def->impl.any.args, 0, sizeof(arg_info)*def->functor->arity);
108   } else
109   { def->impl.any.args = NULL;
110   }
111   resetProcedure(proc, TRUE);
112 
113   DEBUG(MSG_PROC_COUNT, Sdprintf("Created %s\n", procedureName(proc)));
114   ATOMIC_INC(&GD->statistics.predicates);
115   ATOMIC_ADD(&m->code_size, SIZEOF_PROC);
116 
117   if ( (oproc=addHTable(m->procedures, (void *)f, proc)) == proc )
118   { return proc;
119   } else
120   { unallocProcedure(proc);
121     return oproc;
122   }
123 }
124 
125 
126 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
127 lingerDefinition() deals with (undefined) definitions  that are replaced
128 due to importing. These definitions can be   in  use with other threads.
129 This needs be be improved, possibly using a technique similar to the RDF
130 database. For now, we merely collect them in  a single place, so we know
131 what is going on. In addition, we can collect lingering definitions when
132 destroying a module, resulting in leak-free temporary modules.
133 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
134 
135 void
lingerDefinition(Definition def)136 lingerDefinition(Definition def)
137 { ListCell c = allocHeapOrHalt(sizeof(*c));
138   Module m = def->module;
139   ListCell o;
140 
141   c->value     = def;
142   do
143   { o            = m->lingering;
144     c->next      = o;
145   } while( !COMPARE_AND_SWAP_PTR(&m->lingering, o, c) );
146 
147   DEBUG(MSG_PROC_COUNT, Sdprintf("Linger %s\n", predicateName(def)));
148   ATOMIC_SUB(&m->code_size, sizeof(*def));
149   ATOMIC_DEC(&GD->statistics.predicates);
150 
151   /*GC_LINGER(def);*/
152 }
153 
154 
155 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
156 destroyDefinition() is called to destroy predicates from destroyModule()
157 as well as destroying thread-local  instantiations   while  a  thread is
158 being terminated. In both  cases  is  the   predicate  known  to  be not
159 referenced.
160 
161 However, we cannot simply discard  everything   as  the predicate may be
162 involved in clause-GC. Therefore we need to leave the entire cleaning to
163 clause-GC. This is somewhat slower than  the   old  way around. The good
164 news is the it works towards more   general garbage collection for code,
165 e.g., eventually we may be able  to   destroy  modules even if we cannot
166 guarantee they are not in use.
167 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
168 
169 void
destroyDefinition(Definition def)170 destroyDefinition(Definition def)
171 { ATOMIC_DEC(&GD->statistics.predicates);
172   ATOMIC_SUB(&def->module->code_size, sizeof(*def));
173 
174   DEBUG(MSG_CGC_PRED,
175 	Sdprintf("destroyDefinition(%s)\n", predicateName(def)));
176 
177   freeCodesDefinition(def, FALSE);
178 
179   if ( false(def, P_FOREIGN|P_THREAD_LOCAL) )	/* normal Prolog predicate */
180   { GET_LD
181 
182     deleteIndexesDefinition(def);
183     removeClausesPredicate(def, 0, FALSE);
184     registerDirtyDefinition(def PASS_LD);
185     DEBUG(MSG_PROC_COUNT, Sdprintf("Erased %s\n", predicateName(def)));
186     def->module = NULL;
187     set(def, P_ERASED);
188 
189     return;
190   } else					/* foreign and thread-local */
191   { DEBUG(MSG_PROC_COUNT, Sdprintf("Unalloc foreign/thread-local: %s\n",
192 				   predicateName(def)));
193 #ifdef O_PLMT
194     if ( true(def, P_THREAD_LOCAL) )
195       destroyLocalDefinitions(def);
196 #endif
197   }
198 
199   if ( def->tabling )
200     freeHeap(def->tabling, sizeof(*def->tabling));
201 
202   DEBUG(MSG_PROC_COUNT, Sdprintf("Unalloc %s\n", predicateName(def)));
203   freeHeap(def, sizeof(*def));
204 }
205 
206 
207 void
unallocProcedure(Procedure proc)208 unallocProcedure(Procedure proc)
209 { Definition def = proc->definition;
210   Module m = def->module;
211 
212   if ( unshareDefinition(def) == 0 )
213   { DEBUG(MSG_PROC, Sdprintf("Reclaiming %s\n", predicateName(def)));
214     destroyDefinition(def);
215   }
216   if ( proc->source_no )
217     releaseSourceFileNo(proc->source_no);
218   freeHeap(proc, sizeof(*proc));
219   if ( m )
220     ATOMIC_SUB(&m->code_size, sizeof(*proc));
221 }
222 
223 
224 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
225 Add (import) a definition to a module.  Used by loadImport() for loading
226 states and QLF files. Must be merged with import/1.
227 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
228 
229 int
importDefinitionModule(Module m,Definition def,int flags)230 importDefinitionModule(Module m, Definition def, int flags)
231 { GET_LD
232   functor_t functor = def->functor->functor;
233   Procedure proc;
234   int rc = TRUE;
235 
236   LOCKMODULE(m);
237   if ( (proc = lookupHTable(m->procedures, (void *)functor)) )
238   { if ( proc->definition != def )
239     { if ( !isDefinedProcedure(proc) )
240       { Definition odef = proc->definition;
241 
242 	shareDefinition(def);
243 	proc->definition = def;
244 	if ( unshareDefinition(odef) == 0 )
245 	  lingerDefinition(odef);
246       } else
247       { if ( !(flags&PROC_WEAK) )
248 	  rc = warning("Failed to import %s into %s",
249 		       predicateName(def), PL_atom_chars(m->name));
250       }
251     }
252   } else
253   { proc = (Procedure) allocHeapOrHalt(sizeof(struct procedure));
254     shareDefinition(def);
255     proc->definition = def;
256     proc->flags      = flags;
257     proc->source_no  = 0;
258     addNewHTable(m->procedures, (void *)functor, proc);
259   }
260   UNLOCKMODULE(m);
261 
262   return rc;
263 }
264 
265 
266 
267 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
268 resetProcedure() is called  by  lookupProcedure()   for  new  ones,  and
269 abolishProcedure() by abolish/2.
270 
271 There are two cases where a  complete  reset   is  safe:  if  this is an
272 unreferenced dynamic predicate and if this is   a  predicate that has no
273 clause-list. Such predicates can't be active  and can't become active as
274 that requires clauses which, even under  MT,   can  only  be added after
275 locking the L_PREDICATE mutex.
276 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
277 
278 static void
resetProcedure(Procedure proc,bool isnew)279 resetProcedure(Procedure proc, bool isnew)
280 { Definition def = proc->definition;
281 
282   if ( (true(def, P_DYNAMIC) /*&& def->references == 0*/) ||
283        !def->impl.any.defined )
284     isnew = TRUE;
285 
286   if ( true(def, P_DIRTYREG) )
287     ATOMIC_SUB(&GD->clauses.dirty, def->impl.clauses.number_of_clauses);
288 
289   def->flags ^= def->flags & ~(SPY_ME|P_DIRTYREG);
290   if ( stringAtom(def->functor->name)[0] != '$' )
291     set(def, TRACE_ME);
292   def->impl.clauses.number_of_clauses = 0;
293   if ( def->events )
294     destroy_event_list(&def->events);
295   if ( def->tabling )
296     tbl_reset_tabling_attributes(def);
297 
298   if ( isnew )
299   { deleteIndexes(&def->impl.clauses, TRUE);
300     freeCodesDefinition(def, FALSE);
301   } else
302     freeCodesDefinition(def, TRUE);	/* carefully sets to S_VIRGIN */
303 }
304 
305 
306 Procedure
isCurrentProcedure__LD(functor_t f,Module m ARG_LD)307 isCurrentProcedure__LD(functor_t f, Module m ARG_LD)
308 { return lookupHTable(m->procedures, (void *)f);
309 }
310 
311 
312 ClauseRef
hasClausesDefinition(Definition def)313 hasClausesDefinition(Definition def)
314 { if ( false(def, P_FOREIGN|P_THREAD_LOCAL) &&
315        def->impl.clauses.first_clause )
316   { GET_LD
317     ClauseRef c;
318     gen_t generation = global_generation();
319 
320     acquire_def(def);
321     for(c = def->impl.clauses.first_clause; c; c = c->next)
322     { Clause cl = c->value.clause;
323 
324       if ( visibleClauseCNT(cl, generation) )
325 	break;
326     }
327     release_def(def);
328 
329     return c;
330   }
331 
332   return NULL;
333 }
334 
335 
336 bool
isDefinedProcedure(Procedure proc)337 isDefinedProcedure(Procedure proc)
338 { Definition def = proc->definition;
339 
340   if ( true(def, PROC_DEFINED) )
341     succeed;
342 
343   return hasClausesDefinition(def) ? TRUE : FALSE;
344 }
345 
346 
347 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
348 Find a procedure for defining it.  Here   we check whether the procedure
349 to be defined is a system predicate.
350 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
351 
352 Procedure
isStaticSystemProcedure(functor_t fd)353 isStaticSystemProcedure(functor_t fd)
354 { GET_LD
355   Procedure proc;
356 
357   if ( !SYSTEM_MODE &&
358        MODULE_system &&
359        (proc=isCurrentProcedure(fd, MODULE_system)) &&
360        true(proc->definition, P_LOCKED) &&
361        false(proc->definition, P_DYNAMIC) )
362     return proc;
363 
364   return NULL;
365 }
366 
367 
368 int
checkModifySystemProc(functor_t fd)369 checkModifySystemProc(functor_t fd)
370 { Procedure proc;
371 
372   if ( (proc = isStaticSystemProcedure(fd)) &&
373        true(proc->definition, P_ISO) )
374     return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, proc);
375 
376   succeed;
377 }
378 
379 
380 int
overruleImportedProcedure(Procedure proc,Module target)381 overruleImportedProcedure(Procedure proc, Module target)
382 { GET_LD
383   Definition def = proc->definition;	/* we do *not* want a thread-local version */
384 
385   if ( true(def, P_AUTOLOAD) )
386     return PL_error(NULL, 0, NULL, ERR_PERMISSION_PROC,
387 		    ATOM_redefine, ATOM_imported_procedure, proc);
388 
389   if ( def->module == target )
390     return TRUE;
391 
392   if ( true(def->module, M_SYSTEM) )
393   { return PL_error(NULL, 0, NULL, ERR_PERMISSION_PROC,
394 		    ATOM_redefine, ATOM_built_in_procedure, proc);
395   } else
396   { if ( proc->flags & PROC_WEAK )
397     { if ( truePrologFlag(PLFLAG_WARN_OVERRIDE_IMPLICIT_IMPORT) )
398       { term_t pi;
399 
400 	if ( !(pi=PL_new_term_ref()) ||
401 	     !PL_unify_predicate(pi, proc, GP_NAMEARITY) ||
402 	     !printMessage(ATOM_warning,
403 			   PL_FUNCTOR_CHARS, "ignored_weak_import", 2,
404 			     PL_ATOM, target->name,
405 			     PL_TERM, pi) )
406 	  return FALSE;
407       }
408 
409       abolishProcedure(proc, target);
410       return TRUE;
411     }
412   }
413 
414   return PL_error(NULL, 0, NULL, ERR_PERMISSION_PROC,
415 		  ATOM_redefine, ATOM_imported_procedure, proc);
416 }
417 
418 
419 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
420 lookupProcedureToDefine() locates the proc for  a   functor  in a module
421 with the aim of providing a  definition   for  this  procedure, e.g., to
422 declare it as a meta-predicate, dynamic, etc.
423 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
424 
425 Procedure
lookupProcedureToDefine(functor_t def,Module m)426 lookupProcedureToDefine(functor_t def, Module m)
427 { GET_LD
428   Procedure proc;
429 
430   if ( (proc = isCurrentProcedure(def, m)) )
431   { if ( !overruleImportedProcedure(proc, m) )
432       return NULL;
433 
434     return proc;
435   }
436 
437   if ( checkModifySystemProc(def) )
438     return lookupProcedure(def, m);
439 
440   return NULL;
441 }
442 
443 
444 Procedure
getDefinitionProc(Definition def)445 getDefinitionProc(Definition def)
446 { GET_LD
447   Procedure proc = isCurrentProcedure(def->functor->functor, def->module);
448   assert(proc);
449   return proc;
450 }
451 
452 
453 void
shareDefinition(Definition def)454 shareDefinition(Definition def)
455 { int shared = ATOMIC_INC(&def->shared);
456   assert(shared > 0);
457   (void)shared;
458 }
459 
460 
461 int
unshareDefinition(Definition def)462 unshareDefinition(Definition def)
463 { return ATOMIC_DEC(&def->shared);
464 }
465 
466 
467 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
468 get_functor() translates term  of  the   format  +Name/+Arity  into  the
469 internal functor represenation. It fails and  raises an exception on the
470 various possible format or representation errors.  ISO compliant.
471 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
472 
473 static int
get_arity(term_t t,int extra,int maxarity,int * arity)474 get_arity(term_t t, int extra, int maxarity, int *arity)
475 { int a;
476 
477   if ( !PL_get_integer_ex(t, &a) )
478     fail;
479   if ( a < 0 )
480     return PL_error(NULL, 0, NULL, ERR_DOMAIN,
481 		    ATOM_not_less_than_zero, t);
482   a += extra;
483   if ( maxarity >= 0 && a > maxarity )
484   { char buf[100];
485 
486     Ssprintf(buf, "limit is %d, request = %d", maxarity, a);
487 
488     return PL_error(NULL, 0, buf,
489 		    ERR_REPRESENTATION, ATOM_max_arity);
490   }
491 
492   *arity = a;
493 
494   return TRUE;
495 }
496 
497 
498 int
get_functor(term_t descr,functor_t * fdef,Module * m,term_t h,int how)499 get_functor(term_t descr, functor_t *fdef, Module *m, term_t h, int how)
500 { GET_LD
501   term_t head;
502   int dcgpi=FALSE;
503 
504   if ( !(how&GP_NOT_QUALIFIED) )
505   { head = PL_new_term_ref();
506     if ( !PL_strip_module(descr, m, head) )
507       return FALSE;
508   } else
509   { head = descr;
510   }
511 
512   if ( PL_is_functor(head, FUNCTOR_divide2) ||
513        (dcgpi=PL_is_functor(head, FUNCTOR_gdiv2)) )
514   { term_t a = PL_new_term_ref();
515     atom_t name;
516     int arity = 0;
517 
518     _PL_get_arg(1, head, a);
519     if ( !PL_get_atom_ex(a, &name) )
520       fail;
521     _PL_get_arg(2, head, a);
522     if ( !get_arity(a,
523 		    (dcgpi ? 2 : 0),
524 		    (how&GF_PROCEDURE) ? MAXARITY : -1,
525 		    &arity ) )
526       fail;
527     *fdef = PL_new_functor(name, arity);
528     if ( h )
529       PL_put_term(h, head);
530 
531     succeed;
532   } else if ( !(how&GF_NAMEARITY) && PL_get_functor(head, fdef) )
533   { if ( h )
534       PL_put_term(h, head);
535 
536     succeed;
537   } else
538   { if ( how & GP_TYPE_QUIET )
539       fail;
540     else
541       return PL_error(NULL, 0, NULL, ERR_TYPE,
542 		      ATOM_predicate_indicator, head);
543   }
544 }
545 
546 
547 int
get_head_functor(term_t head,functor_t * fdef,int how ARG_LD)548 get_head_functor(term_t head, functor_t *fdef, int how ARG_LD)
549 { FunctorDef fd;
550 
551   if ( !PL_get_functor(head, fdef) )
552   { if ( how&GP_TYPE_QUIET )
553       fail;
554     else
555       return PL_error(NULL, 0, NULL, ERR_TYPE, ATOM_callable, head);
556   }
557 
558   fd = valueFunctor(*fdef);
559 
560   if ( fd->arity > MAXARITY )
561   { if ( how&GP_TYPE_QUIET )
562     { fail;
563     } else
564     { char buf[100];
565 
566       Ssprintf(buf, "limit is %d, request = %d", MAXARITY, fd->arity);
567 
568       return PL_error(NULL, 0, buf,
569 		      ERR_REPRESENTATION, ATOM_max_arity);
570     }
571   }
572 
573   if ( !isCallableAtom(fd->name) )
574   { if ( how&GP_TYPE_QUIET )
575     { fail;
576     } else
577     { return PL_error(NULL, 0, NULL,
578 		      ERR_TYPE, ATOM_callable, head);
579     }
580   }
581 
582   succeed;
583 }
584 
585 
586 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
587 Get the specified procedure from a   Prolog  argument.  This argument is
588 either a head or a term of the form module:head.  If `create' is TRUE, a
589 procedure is created in the module.  Otherwise, the system traverses the
590 module-inheritance chain to find the existing procedure.
591 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
592 
593 static Module
get_module(atom_t mname,int how ARG_LD)594 get_module(atom_t mname, int how ARG_LD)
595 { if ( mname )
596   { switch(how&GP_HOW_MASK)
597     { case GP_CREATE:
598       case GP_DEFINE:
599 	return lookupModule(mname);
600       case GP_FIND:
601       case GP_FINDHERE:
602       case GP_RESOLVE:
603       { Module m;
604 	if ( (m=isCurrentModule(mname)) )
605 	  return m;
606 	return MODULE_user;
607       }
608     }
609   }
610 
611   return (environment_frame ? contextModule(environment_frame)
612 			    : MODULE_user);
613 }
614 
615 
616 int
get_procedure(term_t descr,Procedure * proc,term_t h,int how)617 get_procedure(term_t descr, Procedure *proc, term_t h, int how)
618 { GET_LD
619   atom_t mname = 0;
620   Module m = NULL;
621   functor_t fdef;
622   Procedure p;
623 
624   if ( (how&GP_NAMEARITY) )
625   { if ( !get_functor(descr, &fdef, &m, h,
626 		      GF_PROCEDURE|(how&GP_TYPE_QUIET)) )
627       fail;
628   } else
629   { term_t head = PL_new_term_ref();
630     Word p;
631 
632     if ( !(p=stripModuleName(valTermRef(descr), &mname PASS_LD)) )
633       return FALSE;
634     *valTermRef(head) = linkVal(p);
635 
636     if ( !(m = get_module(mname, how PASS_LD)) )
637       return FALSE;
638 
639     if ( h )
640       PL_put_term(h, head);
641 
642     if ( !get_head_functor(head, &fdef, how PASS_LD) )
643       fail;
644   }
645 
646   switch( how & GP_HOW_MASK )
647   { case GP_CREATE:
648       *proc = lookupBodyProcedure(fdef, m);
649       break;
650     case GP_FINDHERE:
651       if ( (p = isCurrentProcedure(fdef, m)) )
652       { *proc = p;
653         break;
654       }
655       goto notfound;
656     case GP_FIND:
657       if ( (p = visibleProcedure(fdef, m PASS_LD)) )
658       { *proc = p;
659         goto out;
660       }
661       goto notfound;
662     case GP_DEFINE:
663       if ( (p = lookupProcedureToDefine(fdef, m)) )
664       { *proc = p;
665         break;
666       }
667       fail;				/* permission error */
668     case GP_RESOLVE:
669       if ( (p = resolveProcedure(fdef, m)) )
670       { *proc = p;
671         break;
672       }
673       goto notfound;
674     default:
675       assert(0);
676   }
677 out:
678   succeed;
679 
680 notfound:
681   if ( (how & GP_EXISTENCE_ERROR) )
682     return PL_error(NULL, 0, NULL, ERR_EXISTENCE, ATOM_procedure, descr);
683   fail;
684 }
685 
686 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
687 This function  implements  $c_current_predicate/2.   current_predicate/2
688 itself  is  written  in  Prolog, based on this function.  Having dynamic
689 linking from super modules and dynamic loading from the  libraries,  the
690 definition  of current predicate has become a difficult issue.  Normally
691 it is used for meta-programming and program analysis.  I think it should
692 succeed  for  each  predicate  that  can   be   called.    The   current
693 implementation  is VERY slow due to all Prolog overhead.  This should be
694 reconsidered and probably a large part of this function should be  moved
695 to C.
696 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
697 
698 word
pl_current_predicate(term_t name,term_t spec,control_t h)699 pl_current_predicate(term_t name, term_t spec, control_t h)
700 { GET_LD
701   TableEnum e;
702   atom_t n;
703   functor_t f;
704   Module m = (Module) NULL;
705   Procedure proc;
706   term_t functor = PL_new_term_ref();
707 
708   if ( ForeignControl(h) == FRG_CUTTED )
709   { e = ForeignContextPtr(h);
710     freeTableEnum(e);
711     succeed;
712   }
713 
714   if ( !PL_strip_module__LD(spec, &m, functor, SM_NOCREATE PASS_LD) )
715     fail;
716 
717   if ( !PL_get_atom(name, &n) )
718   { if ( PL_is_variable(name) )
719       n = NULL_ATOM;
720     else
721       fail;
722   }
723   if ( !PL_get_functor(functor, &f) )
724   { if ( PL_is_variable(functor) )
725       f = 0;
726     else
727       fail;
728   }
729 
730   if ( ForeignControl(h) == FRG_FIRST_CALL)
731   { if ( f )
732     { if ( (proc = isCurrentProcedure(f, m)) )
733 	return PL_unify_atom(name, nameFunctor(f));
734       fail;
735     }
736     e = newTableEnum(m->procedures);
737   } else
738     e = ForeignContextPtr(h);
739 
740   while( advanceTableEnum(e, NULL, (void**)&proc) )
741   { FunctorDef fdef;
742 
743     fdef = proc->definition->functor;
744 
745     if ( (n && n != fdef->name) ||
746 	 !PL_unify_atom(name, fdef->name) ||
747 	 !PL_unify_functor(functor, fdef->functor) )
748       continue;
749 
750     ForeignRedoPtr(e);
751   }
752 
753   freeTableEnum(e);
754   fail;
755 }
756 
757 		 /*******************************
758 		 *    ISO CURRENT-PREDICATE/1	*
759 		 *******************************/
760 
761 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
762 Patterns: ?Name/?Arity
763 	  ?Module:(?Name/?Arity)
764 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
765 
766 typedef struct
767 { functor_t	functor;		/* Functor we are looking for */
768   atom_t	name;			/* Name of target pred */
769   int		arity;			/* arity of target pred */
770   int		macq;			/* Module is acquired */
771   Module	module;			/* Module to search in */
772   Module	super;			/* Walking along super-chain */
773   TableEnum	epred;			/* Predicate enumerator */
774   ModuleEnum	emod;			/* Module enumerator */
775 } cur_enum;
776 
777 
778 static int
isDefinedOrAutoloadProcedure(Procedure proc)779 isDefinedOrAutoloadProcedure(Procedure proc)
780 { Definition def = proc->definition;
781 
782   if ( true(def, PROC_DEFINED|P_AUTOLOAD) )
783     succeed;
784 
785   return hasClausesDefinition(def) ? TRUE : FALSE;
786 }
787 
788 
789 static Procedure
visibleProcedure(functor_t f,Module m ARG_LD)790 visibleProcedure(functor_t f, Module m ARG_LD)
791 { ListCell c;
792   Procedure p;
793 
794   for(;;)
795   { next:
796 
797     if ( (p = isCurrentProcedure(f, m)) && isDefinedOrAutoloadProcedure(p) )
798       return p;
799 
800     for(c=m->supers; c; c=c->next)
801     { if ( c->next )
802       { if ( (p=visibleProcedure(f, c->value PASS_LD)) )
803 	  return p;
804       } else
805       { m = c->value;
806 	goto next;
807       }
808     }
809 
810     return NULL;
811   }
812 }
813 
814 
815 static
816 PRED_IMPL("current_predicate", 1, current_predicate,
817 	  PL_FA_TRANSPARENT|PL_FA_NONDETERMINISTIC|PL_FA_ISO)
818 { PRED_LD
819   cur_enum e0;
820   cur_enum *e;
821   int rval = FALSE;
822   term_t mt = 0;			/* module-term */
823   term_t nt = 0;			/* name-term */
824   term_t at = 0;			/* arity-term */
825   unsigned int aextra = 0;
826   term_t spec = A1;
827 
828   if ( CTX_CNTRL != FRG_CUTTED )
829   { term_t pi = PL_copy_term_ref(spec);
830 
831     nt = PL_new_term_ref();
832     at = PL_new_term_ref();
833 
834     while( PL_is_functor(pi, FUNCTOR_colon2) )
835     { if ( !mt )
836 	mt = PL_new_term_ref();
837       _PL_get_arg(1, pi, mt);
838       _PL_get_arg(2, pi, pi);
839     }
840 
841     if ( PL_is_functor(pi, FUNCTOR_divide2) )
842     { _PL_get_arg(1, pi, nt);
843       _PL_get_arg(2, pi, at);
844     } else if ( PL_is_functor(pi, FUNCTOR_gdiv2) )
845     { _PL_get_arg(1, pi, nt);
846       _PL_get_arg(2, pi, at);
847       aextra = 2;
848     } else if ( PL_is_variable(pi) )
849     { term_t a;
850 
851       if ( !(a=PL_new_term_ref()) ||
852 	   !PL_cons_functor(a, FUNCTOR_divide2, nt, at) ||
853 	   !PL_unify(pi, a) )
854 	return FALSE;			/* resource error */
855     } else
856       goto typeerror;
857   }
858 
859   switch( CTX_CNTRL )
860   { case FRG_FIRST_CALL:
861     { e = &e0;
862       memset(e, 0, sizeof(*e));
863 
864       if ( !PL_get_atom(nt, &e->name) )
865       { if ( !PL_is_variable(nt) )
866 	  goto typeerror;
867       }
868       if ( PL_get_integer(at, &e->arity) )
869       { if ( e->arity < 0 )
870 	  return PL_error(NULL, 0, NULL, ERR_DOMAIN,
871 			  ATOM_not_less_than_zero, at);
872 
873 	e->arity += aextra;
874       } else
875       { if ( !PL_is_variable(at) )
876 	  goto typeerror;
877 	e->arity = -1;
878       }
879 
880       if ( e->name && e->arity >= 0 )
881 	e->functor = PL_new_functor(e->name, e->arity);
882 
883       if ( mt )
884       { atom_t mname;
885 
886 	if ( PL_is_variable(mt) )
887 	{ if ( !(e->emod = newModuleEnum(0)) )
888 	    return PL_no_memory();
889 
890 	  if ( !(e->module=advanceModuleEnum(e->emod)) )
891 	    return FALSE;
892 	} else if ( PL_get_atom_ex(mt, &mname) )
893 	{ if ( !(e->module = acquireModule(mname)) )
894 	    return FALSE;
895 	  e->macq = TRUE;
896 	} else
897 	{ return FALSE;
898 	}
899       } else
900       { if ( environment_frame )
901 	  e->module = contextModule(environment_frame);
902 	else
903 	  e->module = MODULE_user;
904 	e->super = e->module;
905       }
906 
907       if ( e->functor )
908       { if ( !e->emod )			/* fully specified */
909 	  return (visibleProcedure(e->functor, e->module PASS_LD) != NULL);
910       } else
911       { e->epred = newTableEnum(e->module->procedures);
912       }
913 
914       e = allocForeignState(sizeof(*e));
915       *e = e0;
916       break;
917     }
918     case FRG_REDO:
919       e = CTX_PTR;
920       break;
921     case FRG_CUTTED:
922     { e = CTX_PTR;
923       rval = TRUE;
924       goto clean;
925     }
926     default:
927     { e = NULL;
928       assert(0);
929     }
930   }
931 
932   for(;;)
933   { if ( e->functor )			/* _M:foo/2 */
934     { if ( visibleProcedure(e->functor, e->module PASS_LD) )
935       { PL_unify_atom(mt, e->module->name);
936 
937 	if ( (e->module=advanceModuleEnum(e->emod)) )
938 	{ ForeignRedoPtr(e);
939 	} else
940 	{ rval = TRUE;
941 	  goto clean;
942 	}
943       }
944     } else
945     { functor_t f;
946       Procedure proc;
947       while( advanceTableEnum(e->epred, (void**)&f, (void**)&proc) )
948       { FunctorDef fd = valueFunctor(f);
949 
950 	if ( (!e->name     || e->name == fd->name) &&
951 	     (e->arity < 0 || (unsigned int)e->arity == fd->arity) &&
952 	     fd->arity >= aextra &&
953 	     isDefinedProcedure(proc) )
954 	{ if ( mt )
955 	    PL_unify_atom(mt, e->module->name);
956 	  if ( !e->name )
957 	    PL_unify_atom(nt, fd->name);
958 	  if ( e->arity < 0 )
959 	    PL_unify_integer(at, fd->arity-aextra);
960 
961 	  ForeignRedoPtr(e);
962 	}
963       }
964     }
965 
966     if ( e->emod )			/* enumerate all modules */
967     { Module m;
968       while( (m=advanceModuleEnum(e->emod)) )
969       {
970 					/* skip hidden modules */
971 	if ( SYSTEM_MODE ||
972 	     m->name == ATOM_system ||
973 	     m->class != ATOM_system )
974 	  break;
975       }
976       if ( m )
977 	e->super = e->module = m;
978       else
979 	break;
980     } else if ( !e->functor && e->super && e->super->supers )
981     { e->super = e->super->supers->value;	/* advance to user-modules */
982 					/* TBD: handle multiple supers */
983     } else
984       break;				/* finished all modules */
985 
986     if ( !e->functor )
987     { freeTableEnum(e->epred);
988       e->epred = newTableEnum(e->super->procedures);
989     }
990   }
991 
992 clean:
993   if ( e )
994   { if ( e->epred )
995       freeTableEnum(e->epred);
996     if ( e->emod )
997       freeModuleEnum(e->emod);
998     if ( e->module && e->macq )
999       releaseModule(e->module);
1000     freeForeignState(e, sizeof(*e));
1001   }
1002 
1003   return rval;
1004 
1005 typeerror:
1006   return PL_error(NULL, 0, NULL, ERR_TYPE,
1007 		  ATOM_predicate_indicator, spec);
1008 }
1009 
1010 
1011 		 /*******************************
1012 		 *	 CLAUSE REFERENCES	*
1013 		 *******************************/
1014 
1015 #ifdef O_DEBUG
1016 static Table retracted_clauses = NULL;
1017 
1018 static void
registerRetracted(Clause cl)1019 registerRetracted(Clause cl)
1020 { DEBUG(MSG_CGC_CREF_PL, Sdprintf("/**/ r(%p).\n", cl));
1021   DEBUG(MSG_CGC_CREF_TRACK,
1022 	{ if ( !retracted_clauses )
1023 	    retracted_clauses = newHTable(1024);
1024 	  addNewHTable(retracted_clauses, cl, (void*)1);
1025 	});
1026 }
1027 
1028 static void
reclaimRetracted(Clause cl)1029 reclaimRetracted(Clause cl)
1030 { DEBUG(MSG_CGC_CREF_TRACK,
1031 	{ void *v = deleteHTable(retracted_clauses, cl);
1032 	  if ( v != (void*)1 && GD->cleaning == CLN_NORMAL )
1033 	  { Definition def = cl->predicate;
1034 	    Sdprintf("reclaim not retracted from %s\n", predicateName(def));
1035 	  }
1036 	});
1037 }
1038 
1039 void
listNotReclaimed(void)1040 listNotReclaimed(void)
1041 { if ( retracted_clauses )
1042   { for_table(retracted_clauses, n, v,
1043 	      { Clause cl = n;
1044 		Definition def = cl->predicate;
1045 
1046 		Sdprintf("%p from %s\n", cl, predicateName(def));
1047 	      });
1048   }
1049 }
1050 
1051 #else
1052 
1053 #define registerRetracted(cl) (void)0
1054 #define reclaimRetracted(cl)  (void)0
1055 
1056 #endif
1057 
1058 
1059 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1060 Clause references are used  to  link   clauses  from  the main predicate
1061 clause list as well as from additional  indexes. They form a linked list
1062 of clauses, indexed according to a  specific   key.  This key is deduced
1063 from the first argument for  the  main   predicate  clause  list or from
1064 alternative arguments for secondary clause lists.
1065 
1066 Traversing a list of clause  references   traverses  the ->next pointer,
1067 possibly matches the key and then looks into the associated ->clause for
1068 the born/died generations. If a clause erased, cleanDefinition() removes
1069 the  references  to  it  from  the  linked    lists  and  adds  them  to
1070 GD->lingering_clauses, which uses d.gnext to   link them together rather
1071 then ->next because ->next might be used by some other thread traversing
1072 the clause chain.
1073 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1074 
1075 void
acquire_clause(Clause cl)1076 acquire_clause(Clause cl)
1077 { ATOMIC_INC(&cl->references);
1078 }
1079 
1080 void
release_clause(Clause cl)1081 release_clause(Clause cl)
1082 { if ( ATOMIC_DEC(&cl->references) == 0 )
1083   { size_t size = sizeofClause(cl->code_size) + SIZEOF_CREF_CLAUSE;
1084 
1085     ATOMIC_SUB(&GD->clauses.erased_size, size);
1086     ATOMIC_DEC(&GD->clauses.erased);
1087 
1088     reclaimRetracted(cl);
1089     freeClause(cl);
1090   }
1091 }
1092 
1093 
1094 ClauseRef
newClauseRef(Clause clause,word key)1095 newClauseRef(Clause clause, word key)
1096 { ClauseRef cref = allocHeapOrHalt(SIZEOF_CREF_CLAUSE);
1097 
1098   DEBUG(MSG_CGC_CREF_PL,
1099 	Sdprintf("/**/ a(%p, %p, %d, '%s').\n",
1100 		 cref, clause, clause->references,
1101 		 predicateName(clause->predicate)));
1102 
1103   cref->next         = NULL;
1104   cref->d.key        = key;
1105   cref->value.clause = clause;
1106   acquire_clause(clause);
1107 
1108   return cref;
1109 }
1110 
1111 
1112 static void
freeClauseRef(ClauseRef cref)1113 freeClauseRef(ClauseRef cref)
1114 { Clause cl = cref->value.clause;
1115 
1116   DEBUG(MSG_CGC_CREF_PL,
1117 	Sdprintf("/**/ d(%p, %p, %d).\n",
1118 		 cref, cl, (int)cl->references));
1119 
1120   release_clause(cl);
1121 
1122   freeHeap(cref, SIZEOF_CREF_CLAUSE);
1123 }
1124 
1125 
1126 void
lingerClauseRef(ClauseRef cref)1127 lingerClauseRef(ClauseRef cref)
1128 { ClauseRef o;
1129 
1130   DEBUG(0,
1131 	if ( isProtectedCRef(cref) )
1132 	{ Clause cl = cref->value.clause;
1133 	  Sdprintf("Ditching clause for %s %lld-%lld\n",
1134 		   predicateName(cl->predicate),
1135 		   cl->generation.created,
1136 		   cl->generation.erased);
1137 	  assert(0);
1138 	});
1139 
1140   do
1141   { o = GD->clauses.lingering;
1142     cref->d.gnext = o;
1143   } while(!COMPARE_AND_SWAP_PTR(&GD->clauses.lingering, o, cref) );
1144 
1145   ATOMIC_INC(&GD->clauses.lingering_count);
1146 }
1147 
1148 
1149 static int activePredicate(const Definition *defs, const Definition def);
1150 
1151 static void
gcClauseRefs(void)1152 gcClauseRefs(void)
1153 { ClauseRef cref;
1154 
1155   if ( !(cref = GD->clauses.lingering) ||
1156        !COMPARE_AND_SWAP_PTR(&GD->clauses.lingering, cref, NULL) )
1157     return;			/* no work or someone else doing it */
1158   GD->clauses.lingering_count = 0;
1159 
1160   if ( cref )
1161   { ClauseRef next;
1162     Definition *active_defs = predicates_in_use();
1163     int freed = 0;
1164     int kept = 0;
1165 
1166     for( ; cref; cref = next)
1167     { Definition def;
1168 
1169       next = cref->d.gnext;
1170       def = cref->value.clause->predicate;
1171       if ( !activePredicate(active_defs, def) )
1172       { freeClauseRef(cref);
1173 	freed++;
1174       } else
1175       {	lingerClauseRef(cref);
1176 	kept++;
1177       }
1178     }
1179 
1180     if ( active_defs )
1181       PL_free(active_defs);
1182 
1183     DEBUG(MSG_CGC_CREF, Sdprintf("GC clause references: freed %d, kept %d\n",
1184 				 freed, kept));
1185   }
1186 }
1187 
1188 static int
activePredicate(const Definition * defs,const Definition def)1189 activePredicate(const Definition *defs, const Definition def)
1190 { if ( defs )
1191   { for( ; *defs; defs++)
1192     { if ( *defs == def )
1193 	return TRUE;
1194     }
1195   }
1196 
1197   return FALSE;
1198 }
1199 
1200 static void
setLastModifiedPredicate(Definition def,gen_t gen)1201 setLastModifiedPredicate(Definition def, gen_t gen)
1202 { Module m = def->module;
1203   gen_t lmm;
1204 
1205   def->last_modified = gen;
1206 
1207   do
1208   { lmm = m->last_modified;
1209   } while ( lmm < gen &&
1210 	    !COMPARE_AND_SWAP_UINT64(&m->last_modified, lmm, gen) );
1211 }
1212 
1213 
1214 		 /*******************************
1215 		 *	      ASSERT		*
1216 		 *******************************/
1217 
1218 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1219 Assert a clause to a procedure. Where askes to assert either at the head
1220 or at the tail of the clause list.
1221 
1222 The `where` argument is one of
1223 
1224   - CL_START (asserta)
1225   - CL_END   (assertz)
1226   - The clause reference before which the clause must be inserted.
1227     This is used by reconsult.
1228 
1229 (*) This function updates the indexing information.  If we have a static
1230 procedure, it deletes the supervisor. This is  probably a bit rough, but
1231 deals with -for example- clauses for   term_expansion/2. After the first
1232 definition this will be  called  and   an  S_TRUSTME  supervisor will be
1233 installed, causing further clauses to have no effect.
1234 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1235 
1236 ClauseRef
assertDefinition(Definition def,Clause clause,ClauseRef where ARG_LD)1237 assertDefinition(Definition def, Clause clause, ClauseRef where ARG_LD)
1238 { word key;
1239   ClauseRef cref;
1240 
1241   if ( def->events &&
1242        !predicate_update_event(def,
1243 			       where == CL_START ? ATOM_asserta : ATOM_assertz,
1244 			       clause PASS_LD) )
1245     return NULL;
1246 
1247   argKey(clause->codes, 0, &key);
1248   cref = newClauseRef(clause, key);
1249 
1250   LOCKDEF(def);
1251   acquire_def(def);
1252   if ( !def->impl.clauses.last_clause )
1253   { def->impl.clauses.first_clause = def->impl.clauses.last_clause = cref;
1254   } else if ( where == CL_START || where == def->impl.clauses.first_clause )
1255   { cref->next = def->impl.clauses.first_clause;
1256     def->impl.clauses.first_clause = cref;
1257   } else if ( where == CL_END )
1258   { ClauseRef last = def->impl.clauses.last_clause;
1259 
1260     last->next = cref;
1261     def->impl.clauses.last_clause = cref;
1262   } else				/* insert before */
1263   { ClauseRef cr;
1264 
1265     for(cr = def->impl.clauses.first_clause; cr; cr = cr->next)
1266     { if ( cr->next == where )
1267       { cref->next = where;
1268 	cr->next = cref;
1269 	break;
1270       }
1271     }
1272     assert(cr);
1273   }
1274 
1275   def->impl.clauses.number_of_clauses++;
1276   if ( false(clause, UNIT_CLAUSE) )
1277     def->impl.clauses.number_of_rules++;
1278   if ( true(def, P_DIRTYREG) )
1279     ATOMIC_INC(&GD->clauses.dirty);
1280 #ifdef O_LOGICAL_UPDATE
1281   clause->generation.created = next_global_generation();
1282   clause->generation.erased  = GEN_MAX;	/* infinite */
1283   setLastModifiedPredicate(def, clause->generation.created);
1284 #endif
1285 
1286   if ( false(def, P_DYNAMIC|P_LOCKED_SUPERVISOR) ) /* see (*) above */
1287     freeCodesDefinition(def, TRUE);
1288 
1289   addClauseToIndexes(def, clause, where);
1290   release_def(def);
1291   DEBUG(CHK_SECURE, checkDefinition(def));
1292   UNLOCKDEF(def);
1293 
1294   return cref;
1295 }
1296 
1297 
1298 ClauseRef
assertProcedure(Procedure proc,Clause clause,ClauseRef where ARG_LD)1299 assertProcedure(Procedure proc, Clause clause, ClauseRef where ARG_LD)
1300 { Definition def = getProcDefinition(proc);
1301 
1302   return assertDefinition(def, clause, where PASS_LD);
1303 }
1304 
1305 
1306 /*  Abolish a procedure.  Referenced  clauses  are   unlinked  and left
1307     dangling in the dark until the procedure referencing it deletes it.
1308 
1309     Since we have a foreign language interface we will allow to  abolish
1310     foreign  predicates  as  well.  Permission testing should be done by
1311     the caller.
1312 
1313  ** Sun Apr 17 16:18:50 1988  jan@swivax.UUCP (Jan Wielemaker)  */
1314 
1315 bool
abolishProcedure(Procedure proc,Module module)1316 abolishProcedure(Procedure proc, Module module)
1317 { GET_LD
1318   Definition def = proc->definition;
1319 
1320   DEBUG(MSG_PROC, Sdprintf("abolishProcedure(%s)\n", predicateName(def)));
1321 
1322   startCritical;
1323   LOCKDEF(def);
1324   if ( def->module != module )		/* imported predicate; remove link */
1325   { Definition ndef	     = allocHeapOrHalt(sizeof(*ndef));
1326 
1327     memset(ndef, 0, sizeof(*ndef));
1328     ndef->functor            = def->functor; /* should be merged with */
1329     ndef->impl.any.args	     = allocHeapOrHalt(sizeof(*ndef->impl.any.args)*
1330 					       def->functor->arity);
1331     ndef->module             = module;	     /* lookupProcedure()!! */
1332     ndef->codes		     = SUPERVISOR(virgin);
1333     proc->definition         = ndef;
1334     ATOMIC_INC(&GD->statistics.predicates);
1335     ATOMIC_ADD(&module->code_size, sizeof(*ndef));
1336     resetProcedure(proc, TRUE);
1337   } else if ( true(def, P_FOREIGN) )	/* foreign: make normal */
1338   { def->impl.clauses.first_clause = def->impl.clauses.last_clause = NULL;
1339     resetProcedure(proc, TRUE);
1340   } else if ( true(def, P_THREAD_LOCAL) )
1341   { UNLOCKDEF(def);
1342     if ( !endCritical )
1343       return FALSE;
1344     return PL_error(NULL, 0, NULL, ERR_PERMISSION_PROC,
1345 		    ATOM_modify, ATOM_thread_local_procedure, proc);
1346   } else				/* normal Prolog procedure */
1347   { removeClausesPredicate(def, 0, FALSE);
1348     setDynamicDefinition_unlocked(def, FALSE);
1349     resetProcedure(proc, FALSE);
1350   }
1351 
1352   DEBUG(CHK_SECURE, checkDefinition(def));
1353   UNLOCKDEF(def);
1354 
1355   return endCritical;
1356 }
1357 
1358 
1359 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1360 Remove (mark for  deletion)  all  clauses   that  come  from  the  given
1361 source-file or any sourcefile. Note   that thread-local predicates don't
1362 have clauses from files, so we don't   need to bother. Returns number of
1363 clauses that has been deleted.
1364 
1365 MT: Caller must hold L_PREDICATE
1366 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1367 
1368 size_t
removeClausesPredicate(Definition def,int sfindex,int fromfile)1369 removeClausesPredicate(Definition def, int sfindex, int fromfile)
1370 { GET_LD
1371   ClauseRef c;
1372   size_t deleted = 0;
1373   size_t memory = 0;
1374   gen_t update = global_generation()+1;
1375 
1376   if ( true(def, P_THREAD_LOCAL) )
1377     return 0;
1378 
1379   acquire_def(def);
1380   for(c = def->impl.clauses.first_clause; c; c = c->next)
1381   { Clause cl = c->value.clause;
1382 
1383     if ( (sfindex == 0 || sfindex == cl->owner_no) &&
1384 	 (!fromfile || cl->line_no > 0) &&
1385 	 false(cl, CL_ERASED) )
1386     { set(cl, CL_ERASED);
1387 #ifdef O_LOGICAL_UPDATE
1388       cl->generation.erased = update;
1389 #endif
1390       deleted++;
1391       memory += sizeofClause(cl->code_size) + SIZEOF_CREF_CLAUSE;
1392       def->impl.clauses.number_of_clauses--;
1393       def->impl.clauses.erased_clauses++;
1394       if ( false(cl, UNIT_CLAUSE) )
1395 	def->impl.clauses.number_of_rules--;
1396       deleteActiveClauseFromIndexes(def, cl);
1397       registerRetracted(cl);
1398     }
1399   }
1400   release_def(def);
1401 
1402   if ( global_generation() < update )
1403     next_global_generation();
1404 
1405   if ( deleted )
1406   { ATOMIC_SUB(&def->module->code_size, memory);
1407     ATOMIC_ADD(&GD->clauses.erased_size, memory);
1408     ATOMIC_ADD(&GD->clauses.erased, deleted);
1409     if( true(def, P_DIRTYREG) )
1410       ATOMIC_SUB(&GD->clauses.dirty, deleted);
1411 
1412     registerDirtyDefinition(def PASS_LD);
1413     DEBUG(CHK_SECURE, checkDefinition(def));
1414   }
1415 
1416   return deleted;
1417 }
1418 
1419 
1420 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1421 Retract  a  clause  from  a  dynamic  procedure.  Called  from  erase/1,
1422 retract/1 and retractall/1. Returns FALSE  if   the  clause  was already
1423 retracted or retract is vetoed by  the   update  event handling. This is
1424 also used by  trie_gen_compiled/3  to  get   rid  of  the  clauses  that
1425 represent tries.
1426 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1427 
1428 int
retractClauseDefinition(Definition def,Clause clause)1429 retractClauseDefinition(Definition def, Clause clause)
1430 { GET_LD
1431   size_t size = sizeofClause(clause->code_size) + SIZEOF_CREF_CLAUSE;
1432 
1433   if ( def->events &&
1434        !predicate_update_event(def, ATOM_retract, clause PASS_LD) )
1435     return FALSE;
1436 
1437   LOCKDEF(def);
1438   if ( true(clause, CL_ERASED) )
1439   { UNLOCKDEF(def);
1440     return FALSE;
1441   }
1442 
1443   DEBUG(CHK_SECURE, checkDefinition(def));
1444   set(clause, CL_ERASED);
1445   deleteActiveClauseFromIndexes(def, clause); /* just updates "dirtyness" */
1446   def->impl.clauses.number_of_clauses--;
1447   def->impl.clauses.erased_clauses++;
1448   if ( false(clause, UNIT_CLAUSE) )
1449     def->impl.clauses.number_of_rules--;
1450 #ifdef O_LOGICAL_UPDATE
1451   clause->generation.erased = next_global_generation();
1452   setLastModifiedPredicate(def, clause->generation.erased);
1453 #endif
1454   DEBUG(CHK_SECURE, checkDefinition(def));
1455   UNLOCKDEF(def);
1456 
1457 					/* update stats */
1458   registerRetracted(clause);
1459   if ( true(clause, DBREF_CLAUSE) )
1460     ATOMIC_INC(&GD->clauses.db_erased_refs);
1461 
1462   ATOMIC_SUB(&def->module->code_size, size);
1463   ATOMIC_ADD(&GD->clauses.erased_size, size);
1464   ATOMIC_INC(&GD->clauses.erased);
1465   if( true(def, P_DIRTYREG) )
1466     ATOMIC_DEC(&GD->clauses.dirty);
1467 
1468   registerDirtyDefinition(def PASS_LD);
1469 
1470   return TRUE;
1471 }
1472 
1473 
1474 void
unallocClause(Clause c)1475 unallocClause(Clause c)
1476 { ATOMIC_SUB(&GD->statistics.codes, c->code_size);
1477   ATOMIC_DEC(&GD->statistics.clauses);
1478   if ( c->source_no )			/* set by assert_term() */
1479   { if ( c->owner_no != c->source_no )
1480       releaseSourceFileNo(c->owner_no);
1481     releaseSourceFileNo(c->source_no);
1482   }
1483 
1484 #ifdef ALLOC_DEBUG
1485 #define ALLOC_FREE_MAGIC 0xFB
1486   size_t size = sizeofClause(c->code_size);
1487   memset(c, ALLOC_FREE_MAGIC, size);
1488 #endif
1489 
1490   PL_free(c);
1491 }
1492 
1493 
1494 #ifdef O_DEBUG_ATOMGC
1495 void
unregister_atom_clause(atom_t a)1496 unregister_atom_clause(atom_t a)
1497 { PL_unregister_atom(a);
1498 }
1499 
1500 void
register_atom_clause(atom_t a)1501 register_atom_clause(atom_t a)
1502 { PL_register_atom(a);
1503 }
1504 #endif
1505 
1506 void
freeClause(Clause c)1507 freeClause(Clause c)
1508 {
1509 #ifdef O_ATOMGC
1510 #ifdef O_DEBUG_ATOMGC
1511   forAtomsInClause(c, unregister_atom_clause);
1512 #else
1513   forAtomsInClause(c, PL_unregister_atom);
1514 #endif
1515 #endif
1516 
1517   if ( true(c, DBREF_CLAUSE) )		/* will be freed from symbol */
1518     set(c, DBREF_ERASED_CLAUSE);
1519   else
1520     unallocClause(c);
1521 }
1522 
1523 
1524 
1525 static int WUNUSED			/* FALSE if there was an error */
announceErasedClause(Clause clause)1526 announceErasedClause(Clause clause)
1527 { return ( clearBreakPointsClause(clause) >= 0 &&
1528 	   callEventHook(PLEV_ERASED_CLAUSE, clause) );
1529 }
1530 
1531 
1532 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1533 cleanDefinition()
1534     This function has two tasks. If the predicate needs to be rehashed,
1535     this is done and all erased clauses from the predicate are returned
1536     as a linked list.
1537 
1538     We cannot delete the clauses immediately as the debugger requires a
1539     call-back and we have the L_PREDICATE mutex when running this code.
1540 
1541 find_prev() finds the real  previous  clause.   The  not-locked  loop of
1542 cleanDefinition() keep track of this, but  in the meanwhile the previous
1543 may change due to an assert. Now that we are in the locked region we can
1544 search for the real previous, using   the  one from cleanDefinition() as
1545 the likely candidate.
1546 
1547 The `ddi->oldest_generation` contains the latest  marked generation that
1548 was found or GEN_MAX  if  the  predicate   is  not  active.  The `start`
1549 generation contains the generation when pl_garbage_collect_clauses() was
1550 started.
1551 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1552 
1553 static int	mustCleanDefinition(const Definition def);
1554 
1555 static ClauseRef
find_prev(Definition def,ClauseRef prev,ClauseRef cref)1556 find_prev(Definition def, ClauseRef prev, ClauseRef cref)
1557 { if ( (!prev && def->impl.clauses.first_clause == cref) ||
1558        ( prev && prev->next == cref) )
1559     return prev;
1560 
1561   DEBUG(MSG_PROC, Sdprintf("Fixing prev\n"));
1562   for(prev = def->impl.clauses.first_clause; prev; prev = prev->next)
1563   { if ( prev->next == cref )
1564       return prev;
1565   }
1566 
1567   assert(0);
1568   return NULL;
1569 }
1570 
1571 
1572 
1573 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1574 (*) This used to be acquire_def(def),  but announceErasedClause may call
1575 Prolog, leading to nested acquired definition. This is not needed anyway
1576 as the acquired definition is only  used   by  clause  GC, we are inside
1577 clause GC and clause GC calls cannot run in parallel.
1578 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1579 
1580 static size_t
cleanDefinition(Definition def,DirtyDefInfo ddi,gen_t start,int * rcp)1581 cleanDefinition(Definition def, DirtyDefInfo ddi, gen_t start, int *rcp)
1582 { size_t removed = 0;
1583 
1584   DEBUG(CHK_SECURE,
1585 	LOCKDEF(def);
1586 	checkDefinition(def);
1587         UNLOCKDEF(def));
1588 
1589   if ( mustCleanDefinition(def) && true(ddi, DDI_MARKING) )
1590   { ClauseRef cref, prev = NULL;
1591 #if O_DEBUG
1592     int left = 0;
1593 #endif
1594 
1595     assert(GD->clauses.cgc_active);		/* See (*) */
1596 
1597     for(cref = def->impl.clauses.first_clause;
1598 	cref && def->impl.clauses.erased_clauses;
1599 	cref=cref->next)
1600     { Clause cl = cref->value.clause;
1601 
1602       if ( true(cl, CL_ERASED) && ddi_is_garbage(ddi, start, cl) )
1603       { if ( !announceErasedClause(cl) )
1604 	  *rcp = FALSE;
1605 
1606 	LOCKDEF(def);
1607 	prev = find_prev(def, prev, cref);
1608 	if ( !prev )
1609 	{ def->impl.clauses.first_clause = cref->next;
1610 	  if ( !cref->next )
1611 	    def->impl.clauses.last_clause = NULL;
1612 	} else
1613 	{ prev->next = cref->next;
1614 	  if ( cref->next == NULL)
1615 	    def->impl.clauses.last_clause = prev;
1616 	}
1617 	removed++;
1618 	def->impl.clauses.erased_clauses--;
1619 	UNLOCKDEF(def);
1620 
1621 	lingerClauseRef(cref);
1622       } else
1623       { prev = cref;
1624 	DEBUG(MSG_PROC, left++);
1625       }
1626     }
1627     if ( removed )
1628     { LOCKDEF(def);
1629       cleanClauseIndexes(def, &def->impl.clauses, ddi, start);
1630       UNLOCKDEF(def);
1631     }
1632     gen_t active = ddi_oldest_generation(ddi);
1633     if ( start < active )
1634       active = start;
1635     free_lingering(&def->lingering, active);
1636 
1637     DEBUG(CHK_SECURE,
1638 	  LOCKDEF(def);
1639 	  checkDefinition(def);
1640 	  UNLOCKDEF(def));
1641 
1642     DEBUG(MSG_PROC,
1643 	  Sdprintf("cleanDefinition(%s): removed %d, left %d, erased %d\n",
1644 		   predicateName(def), removed, left,
1645 		   def->impl.clauses.erased_clauses));
1646   }
1647 
1648   return removed;
1649 }
1650 
1651 
1652 static int
mustCleanDefinition(const Definition def)1653 mustCleanDefinition(const Definition def)
1654 { return ( def->impl.clauses.erased_clauses > 0 );
1655 }
1656 
1657 
1658 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1659 Finalize a reloaded predicate. This (nearly)   atomically  makes the new
1660 definition visible.
1661 
1662 (*) Updating the generation to one in the future and incrementing at the
1663 end makes the transaction truely atomic.   In the current implementation
1664 though, another thread may increment the  generation as well, making our
1665 changes not entirely atomic. The lock-free retry mechanism won't work to
1666 fix this. Only a true lock for modifying the generation can fix this.
1667 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1668 
1669 void
reconsultFinalizePredicate(sf_reload * rl,Definition def,p_reload * r ARG_LD)1670 reconsultFinalizePredicate(sf_reload *rl, Definition def, p_reload *r ARG_LD)
1671 { if ( true(r, P_MODIFIED) )
1672   { ClauseRef cref;
1673     gen_t update   = global_generation()+1;	/* see (*) */
1674     size_t deleted = 0;
1675     size_t added   = 0;
1676     size_t memory  = 0;
1677 
1678     acquire_def(def);
1679     for(cref = def->impl.clauses.first_clause; cref; cref=cref->next)
1680     { Clause cl = cref->value.clause;
1681 
1682       if ( cl->generation.erased == rl->reload_gen && false(cl, CL_ERASED) )
1683       { set(cl, CL_ERASED);
1684 	cl->generation.erased = update;
1685 	deleted++;
1686 	memory += sizeofClause(cl->code_size) + SIZEOF_CREF_CLAUSE;
1687 	def->impl.clauses.number_of_clauses--;
1688 	def->impl.clauses.erased_clauses++;
1689 	if ( false(cl, UNIT_CLAUSE) )
1690 	  def->impl.clauses.number_of_rules--;
1691 	if ( true(def, P_DYNAMIC) )
1692 	  deleteActiveClauseFromIndexes(def, cl);
1693 	registerRetracted(cl);
1694       } else if ( cl->generation.created == rl->reload_gen )
1695       { cl->generation.created = update;
1696 	added++;
1697       }
1698     }
1699     release_def(def);
1700 
1701     if ( global_generation() < update )	/* see (*) */
1702       next_global_generation();
1703 
1704     DEBUG(MSG_RECONSULT_CLAUSE,
1705 	  Sdprintf("%s: added %ld, deleted %ld clauses "
1706 		   "at gen=%ld, GD->gen = %lld\n",
1707 		   predicateName(def), (long)added, (long)deleted,
1708 		   (long)update, (int64_t)global_generation()));
1709 
1710     if ( added || deleted )
1711       setLastModifiedPredicate(def, update);
1712 
1713     if ( deleted )
1714     { ATOMIC_SUB(&def->module->code_size, memory);
1715       ATOMIC_ADD(&GD->clauses.erased_size, memory);
1716       ATOMIC_ADD(&GD->clauses.erased, deleted);
1717       if( true(def, P_DIRTYREG) )
1718         ATOMIC_SUB(&GD->clauses.dirty, deleted);
1719 
1720       registerDirtyDefinition(def PASS_LD);
1721     }
1722 
1723     DEBUG(CHK_SECURE, checkDefinition(def));
1724   }
1725 }
1726 
1727 
1728 		 /*******************************
1729 		 *	  META PREDICATE	*
1730 		 *******************************/
1731 
1732 /** meta_predicate :HeadList is det.
1733 
1734 Declaration for meta-predicates. The  declaration   fills  the meta_info
1735 field of a definition as well  as   the  P_META and P_TRANSPARENT flags.
1736 P_META indicates that meta_info is   valid. P_TRANSPARENT indicates that
1737 the declaration contains at least one meta-argument (: or 0..9).
1738 
1739 @param HeadList	Comma separated list of predicates heads, where each
1740 		predicate head has arguments 0..9, :,+,-,?
1741 */
1742 
1743 int
isTransparentMetamask(Definition def,arg_info * args)1744 isTransparentMetamask(Definition def, arg_info *args)
1745 { size_t i, arity = def->functor->arity;
1746   int transparent = FALSE;
1747 
1748   for(i=0; i<arity && !transparent; i++)
1749   { int ma = args[i].meta;
1750     if ( MA_NEEDS_TRANSPARENT(ma) )
1751       transparent = TRUE;
1752   }
1753 
1754   return transparent;
1755 }
1756 
1757 
1758 void
setMetapredicateMask(Definition def,arg_info * args)1759 setMetapredicateMask(Definition def, arg_info *args)
1760 { size_t i, arity = def->functor->arity;
1761 
1762   for(i=0; i<arity; i++)
1763     def->impl.any.args[i].meta = args[i].meta;
1764 
1765   if ( isTransparentMetamask(def, args) )
1766     set(def, P_TRANSPARENT);
1767   else
1768     clear(def, P_TRANSPARENT);
1769   set(def, P_META);
1770 }
1771 
1772 
1773 static int
meta_declaration(term_t spec)1774 meta_declaration(term_t spec)
1775 { GET_LD
1776   term_t head = PL_new_term_ref();
1777   term_t arg = PL_new_term_ref();
1778   Procedure proc;
1779   atom_t name;
1780   size_t i, arity;
1781 
1782   if ( !get_procedure(spec, &proc, head, GP_DEFINE) ||
1783        !PL_get_name_arity(head, &name, &arity) )
1784     return FALSE;
1785 
1786 #ifdef _MSC_VER
1787   arg_info *args = alloca(arity*sizeof(*args));
1788 #else
1789   arg_info args[arity];			/* GCC dynamic allocation */
1790 #endif
1791 
1792   for(i=0; i<arity; i++)
1793   { atom_t ma;
1794 
1795     _PL_get_arg(i+1, head, arg);
1796 
1797     if ( PL_is_integer(arg) )
1798     { int e;
1799 
1800       if ( !PL_get_integer_ex(arg, &e) )
1801 	return FALSE;
1802       if ( e < 0 || e > 9 )
1803       { domain_error:
1804 	return PL_error(NULL, 0, "0..9",
1805 			ERR_DOMAIN, ATOM_meta_argument_specifier, arg);
1806       }
1807       args[i].meta = e;
1808     } else if ( PL_get_atom(arg, &ma) )
1809     { int m;
1810 
1811       if      ( ma == ATOM_plus )          m = MA_NONVAR;
1812       else if ( ma == ATOM_minus )         m = MA_VAR;
1813       else if ( ma == ATOM_question_mark ) m = MA_ANY;
1814       else if ( ma == ATOM_star )	   m = MA_ANY; /* * mapped to ? */
1815       else if ( ma == ATOM_colon )         m = MA_META;
1816       else if ( ma == ATOM_hat )           m = MA_HAT;
1817       else if ( ma == ATOM_gdiv )          m = MA_DCG;
1818       else goto domain_error;
1819 
1820       args[i].meta = m;
1821     } else
1822     { return PL_error(NULL, 0, "0..9",
1823 			ERR_TYPE, ATOM_meta_argument_specifier, arg);;
1824     }
1825   }
1826 
1827   if ( ReadingSource )
1828   { SourceFile sf = lookupSourceFile(source_file_name, TRUE);
1829     int rc = setMetapredicateSource(sf, proc, args PASS_LD);
1830     releaseSourceFile(sf);
1831     return rc;
1832   } else
1833   { setMetapredicateMask(proc->definition, args);
1834     return TRUE;
1835   }
1836 }
1837 
1838 
1839 static
1840 PRED_IMPL("meta_predicate", 1, meta_predicate, PL_FA_TRANSPARENT)
1841 { PRED_LD
1842   term_t tail = PL_copy_term_ref(A1);
1843   term_t head = PL_new_term_ref();
1844 
1845   while ( PL_is_functor(tail, FUNCTOR_comma2) )
1846   { _PL_get_arg(1, tail, head);
1847     if ( !meta_declaration(head) )
1848       return FALSE;
1849     _PL_get_arg(2, tail, tail);
1850   }
1851 
1852   if ( !meta_declaration(tail) )
1853     return FALSE;
1854 
1855   return TRUE;
1856 }
1857 
1858 
1859 static int
unify_meta_argument(term_t head,Definition def,int i ARG_LD)1860 unify_meta_argument(term_t head, Definition def, int i ARG_LD)
1861 { term_t arg = PL_new_term_ref();
1862   int m = def->impl.any.args[i].meta;
1863 
1864   _PL_get_arg(i+1, head, arg);
1865   if ( m < 10 )
1866   { return PL_unify_integer(arg, m);
1867   } else
1868   { atom_t a;
1869 
1870     switch(m)
1871     { case MA_META:	a = ATOM_colon; break;
1872       case MA_VAR:	a = ATOM_minus; break;
1873       case MA_ANY:	a = ATOM_question_mark; break;
1874       case MA_NONVAR:	a = ATOM_plus; break;
1875       case MA_HAT:	a = ATOM_hat; break;
1876       case MA_DCG:	a = ATOM_gdiv; break;
1877       default:		a = NULL_ATOM; assert(0);
1878     }
1879 
1880     return PL_unify_atom(arg, a);
1881   }
1882 }
1883 
1884 
1885 static int
unify_meta_pattern(Procedure proc,term_t head)1886 unify_meta_pattern(Procedure proc, term_t head)
1887 { GET_LD
1888   Definition def = proc->definition;
1889 
1890   if ( PL_unify_functor(head, def->functor->functor) )
1891   { int arity = def->functor->arity;
1892     int i;
1893 
1894     for(i=0; i<arity; i++)
1895     { if ( !unify_meta_argument(head, def, i PASS_LD) )
1896 	return FALSE;
1897     }
1898 
1899     return TRUE;
1900   }
1901 
1902   return FALSE;
1903 }
1904 
1905 
1906 int
PL_meta_predicate(predicate_t proc,const char * spec_s)1907 PL_meta_predicate(predicate_t proc, const char *spec_s)
1908 { Definition def = proc->definition;
1909   int arity = def->functor->arity;
1910   int i;
1911   int mask = 0;
1912   int transparent = FALSE;
1913   const unsigned char *s = (const unsigned char*)spec_s;
1914 
1915   for(i=0; i<arity; i++, s++)
1916   { int spec_c = *s;
1917     int spec;
1918 
1919     switch(spec_c)
1920     { case '+':
1921 	spec = MA_NONVAR;
1922         break;
1923       case '-':
1924 	spec = MA_VAR;
1925         break;
1926       case '?':
1927 	spec = MA_ANY;
1928         break;
1929       case ':':
1930 	spec = MA_META;
1931         break;
1932       case '^':
1933 	spec = MA_HAT;
1934         break;
1935       case '/':
1936         if ( s[1] == '/' )
1937 	{ spec = MA_DCG;
1938 	  s++;
1939 	  break;
1940 	} else
1941 	{ goto invalid;
1942 	}
1943       default:
1944 	if ( spec_c >= '0' && spec_c <= '9' )
1945 	{ spec = spec_c - '0';
1946 	  break;
1947 	}
1948       invalid:
1949         fatalError("Invalid meta-argument for %s: %s\n", procedureName(proc), spec_s);
1950 	return FALSE;
1951     }
1952 
1953     def->impl.any.args[i].meta = spec;
1954     mask |= spec<<(i*4);
1955     if ( MA_NEEDS_TRANSPARENT(spec) )
1956       transparent = TRUE;
1957   }
1958 
1959   if ( transparent )
1960     set(def, P_TRANSPARENT);
1961   else
1962     clear(def, P_TRANSPARENT);
1963   set(def, P_META);
1964 
1965   return TRUE;
1966 }
1967 
1968 
1969 void
clear_meta_declaration(Definition def)1970 clear_meta_declaration(Definition def)
1971 { int i;
1972 
1973   for(i=0; i<def->functor->arity; i++)
1974     def->impl.any.args[i].meta = MA_ANY;
1975 
1976   clear(def, P_META|P_TRANSPARENT);
1977 }
1978 
1979 #ifdef O_CLAUSEGC
1980 		 /*******************************
1981 		 *	     CLAUSE-GC		*
1982 		 *******************************/
1983 
1984 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1985 Retracted clauses are  reclaimed  using   the  clause  garbage collector
1986 (CGC). Retract itself merely sets the   erased  generation of the clause
1987 and marks related clause indexes as `dirty'.   CGC  needs to run both to
1988 reclaim the memory and to remove ClauseRef   objects  that point to dead
1989 clauses and thus slow down the  search   for  clauses.  This logic is in
1990 considerClauseGC().
1991 
1992 CGC builds on the following components and invariants:
1993 
1994   - Dynamic predicates and static predicates with removed clauses are
1995     in the table GD->procedures.dirty.
1996   - CGC does:
1997     - Set the dirty generation of all dirty predicates to GEN_MAX
1998     - markPredicatesInEnvironments() finds all referenced predicates
1999       from frames and pushed explicitly by pushPredicateAccess()
2000     - Remove all ClauseRefs pointing at clauses removed before the
2001       oldest active generation from the clause list.  Keep them using
2002       lingerClauseRef() as someone may be traversing the clause list.
2003     - Call gcClauseRefs(), which
2004       - Finds all predicates whose clause-list is being traversed as
2005         monitored using acquire_def()/release_ref().
2006       - Call freeClauseRef() for each clause-ref associated with a
2007         not-being-traversed predicate.  Re-add the others to the
2008 	lingering clause reference list.
2009       - If freeClauseRef() lowers the clause reference count to zero,
2010         destroy the clause.
2011 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2012 
2013 static int
considerClauseGC(ARG1_LD)2014 considerClauseGC(ARG1_LD)
2015 { size_t pending  = GD->clauses.erased_size - GD->clauses.erased_size_last;
2016   size_t codesize = GD->statistics.codes*sizeof(code);
2017   cgc_stats stats = {0};
2018 
2019   if ( GD->clauses.cgc_space_factor > 0 &&
2020        pending > codesize/GD->clauses.cgc_space_factor &&
2021        GD->cleaning == CLN_NORMAL )
2022   { DEBUG(MSG_CGC_CONSIDER,
2023 	  Sdprintf("CGC? too much garbage: %lld bytes in %lld clauses\n",
2024 		   (int64_t)GD->clauses.erased_size,
2025 		   (int64_t)GD->clauses.erased));
2026     return TRUE;
2027   }
2028 
2029   if ( LD->statistics.inferences > LD->clauses.cgc_inferences )
2030   { int rgc;
2031 
2032     LD->clauses.cgc_inferences = LD->statistics.inferences + 500;
2033 
2034     stats.dirty_pred_clauses = GD->clauses.dirty;
2035     if ( stats.dirty_pred_clauses == (size_t)-1 )
2036       return FALSE;			/* already clicked in */
2037 
2038     if ( !cgc_thread_stats(&stats PASS_LD) )
2039       return FALSE;
2040 
2041     rgc =  ( (double)stats.erased_skipped >
2042 	     (double)stats.local_size*GD->clauses.cgc_stack_factor +
2043 	     (double)stats.dirty_pred_clauses*GD->clauses.cgc_clause_factor );
2044     rgc = rgc && (GD->cleaning == CLN_NORMAL);
2045     DEBUG(MSG_CGC_CONSIDER,
2046 	  Sdprintf("GCG? [%s] %ld skipped; lsize=%ld; clauses=%ld\n",
2047 		   rgc ? "Y" : " ",
2048 		   (long)stats.erased_skipped,
2049 		   (long)stats.local_size,
2050 		   (long)stats.dirty_pred_clauses));
2051 
2052     return rgc;
2053   }
2054 
2055   return FALSE;
2056 }
2057 
2058 /** '$cgc_params'(-OldSpace, -OldStack, -OldClause,
2059  *		  +NewSpace, +NewStack, +NewClause)
2060  *
2061  * Query and set the clause GC parameters.
2062  */
2063 
2064 static
2065 PRED_IMPL("$cgc_params", 6, cgc_params, 0)
2066 { PRED_LD
2067 
2068   return ( PL_unify_integer(A1, GD->clauses.cgc_space_factor) &&
2069 	   PL_unify_float(A2, GD->clauses.cgc_stack_factor) &&
2070 	   PL_unify_float(A3, GD->clauses.cgc_clause_factor) &&
2071 	   PL_get_integer_ex(A4, &GD->clauses.cgc_space_factor) &&
2072 	   PL_get_float_ex(A5, &GD->clauses.cgc_stack_factor) &&
2073 	   PL_get_float_ex(A6, &GD->clauses.cgc_clause_factor) );
2074 }
2075 
2076 
2077 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2078 Dirty Definition Info handling.  This should respect
2079 
2080   - If a predicate is registered dirty after the preparation step
2081     calling ddi_reset(), none of its clauses may be collected.
2082   - If a DDI has seen ddi_reset(), the DDI info is valid and can be
2083     used to safely reclaim clauses.
2084 
2085 ddi_add_access_gen() adds access  generations  to   the  dirty  def.  We
2086 maintain two strategies:
2087 
2088   - Upto PROC_DIRTY_GENS, we simply add the predicates to the array.
2089   - Above, the array is an array of intervals (generation pairs)
2090 
2091 
2092 A clause can be collected if it is invisible in all registered access
2093 generations.
2094 
2095 TBD: Use multiple intervals.
2096 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2097 
2098 #ifdef O_DEBUG
2099 static char *
ddi_generation_name(DirtyDefInfo ddi)2100 ddi_generation_name(DirtyDefInfo ddi)
2101 { char s[1024];
2102   char *o = s;
2103   *o++ = '{';
2104 
2105   if ( false(ddi, DDI_INTERVALS) )
2106   { int i;
2107 
2108     for(i=0; i<ddi->count; i++)
2109     { Ssprintf(o, "%s%lld", i==0?"":" ", ddi->access[i]);
2110       o += strlen(o);
2111     }
2112   } else
2113   { int i;
2114 
2115     for(i=0; i<ddi->count; i++)
2116     { gen_t f = ddi->access[i++];
2117       gen_t t = ddi->access[i++];
2118 
2119       Ssprintf(o, "%s%lld-%lld", i==2?"":" ", f, t);
2120       o += strlen(o);
2121     }
2122   }
2123   *o++ = '}';
2124   *o = EOS;
2125 
2126   return buffer_string(s, BUF_DISCARDABLE);
2127 }
2128 #endif /*O_DEBUG*/
2129 
2130 static DirtyDefInfo
ddi_new(Definition def)2131 ddi_new(Definition def)
2132 { DirtyDefInfo ddi = PL_malloc(sizeof(*ddi));
2133 
2134   ddi->predicate = def;
2135   ddi->flags = 0;
2136   return ddi;
2137 }
2138 
2139 static void
ddi_reset(DirtyDefInfo ddi)2140 ddi_reset(DirtyDefInfo ddi)
2141 { ddi->count = 0;
2142   ddi->flags = DDI_MARKING;
2143 }
2144 
2145 int
ddi_contains_gen(DirtyDefInfo ddi,gen_t access)2146 ddi_contains_gen(DirtyDefInfo ddi, gen_t access)
2147 { if ( false(ddi, DDI_INTERVALS) )
2148   { int i;
2149 
2150     for(i=0; i<ddi->count; i++)
2151     { if ( ddi->access[i] == access )
2152 	return TRUE;
2153     }
2154   } else
2155   { int i;
2156 
2157     for(i=0; i<ddi->count; )
2158     { if ( access >= ddi->access[i++] &&
2159 	   access <= ddi->access[i++] )
2160 	return TRUE;
2161     }
2162   }
2163 
2164   return FALSE;
2165 }
2166 
2167 
2168 static void
ddi_to_intervals(DirtyDefInfo ddi,gen_t access)2169 ddi_to_intervals(DirtyDefInfo ddi, gen_t access)
2170 { gen_t min = access;
2171   gen_t max = access;
2172   int i;
2173 
2174   DEBUG(MSG_CGC_GENERATION,
2175 	Sdprintf("DDI for %s to interval: %s\n",
2176 		 predicateName(ddi->predicate),
2177 		 ddi_generation_name(ddi)));
2178 
2179   for(i=0; i<ddi->count; i++)
2180   { gen_t a = ddi->access[i];
2181 
2182     if ( a < min ) min = a;
2183     if ( a > max ) max = a;
2184   }
2185 
2186   ddi->access[0] = min;
2187   ddi->access[1] = max;
2188   ddi->count = 2;
2189   set(ddi, DDI_INTERVALS);
2190 }
2191 
2192 
2193 static void
ddi_interval_add_access_gen(DirtyDefInfo ddi,gen_t access)2194 ddi_interval_add_access_gen(DirtyDefInfo ddi, gen_t access)
2195 { if ( access < ddi->access[0] ) ddi->access[0] = access;
2196   if ( access > ddi->access[1] ) ddi->access[1] = access;
2197 }
2198 
2199 
2200 void
ddi_add_access_gen(DirtyDefInfo ddi,gen_t access)2201 ddi_add_access_gen(DirtyDefInfo ddi, gen_t access)
2202 { if ( true(ddi, DDI_MARKING) )
2203   { PL_LOCK(L_CGCGEN);
2204     if ( !ddi_contains_gen(ddi, access) )
2205     { if ( false(ddi, DDI_INTERVALS) )
2206       { if ( ddi->count < PROC_DIRTY_GENS )
2207 	  ddi->access[ddi->count++] = access;
2208 	else
2209 	  ddi_to_intervals(ddi, access);
2210       } else
2211       { ddi_interval_add_access_gen(ddi, access);
2212       }
2213     }
2214     PL_UNLOCK(L_CGCGEN);
2215   }
2216 }
2217 
2218 int
ddi_is_garbage(DirtyDefInfo ddi,gen_t start,Clause cl)2219 ddi_is_garbage(DirtyDefInfo ddi, gen_t start, Clause cl)
2220 { assert(true(ddi, DDI_MARKING));
2221 
2222   if ( cl->generation.erased >= start )
2223     return FALSE;
2224 
2225   if ( false(ddi, DDI_INTERVALS) )
2226   { int i;
2227 
2228     for(i=0; i<ddi->count; i++)
2229     { if ( GLOBALLY_VISIBLE_CLAUSE(cl, ddi->access[i]) )
2230 	return FALSE;
2231     }
2232   } else
2233   { int i;
2234 
2235     assert(ddi->count == 2);
2236 
2237     for(i=0; i<ddi->count; )
2238     { gen_t f = ddi->access[i++];
2239       gen_t t = ddi->access[i++];
2240 
2241       if ( !(cl->generation.erased  < f ||
2242 	     cl->generation.created > t) )
2243 	return FALSE;
2244     }
2245   }
2246 
2247   return TRUE;
2248 }
2249 
2250 static gen_t
ddi_oldest_generation(DirtyDefInfo ddi)2251 ddi_oldest_generation(DirtyDefInfo ddi)
2252 { gen_t oldest = GEN_MAX;
2253 
2254   if ( false(ddi, DDI_INTERVALS) )
2255   { int i;
2256 
2257     for(i=0; i<ddi->count; i++)
2258     { if ( ddi->access[i] < oldest )
2259 	oldest = ddi->access[i];
2260     }
2261   } else
2262   { int i;
2263 
2264     for(i=0; i<ddi->count; i += 2)
2265     { gen_t f = ddi->access[i];
2266 
2267       if ( f < oldest )
2268 	oldest = f;
2269     }
2270   }
2271 
2272   return oldest;
2273 }
2274 
2275 #ifdef O_DEBUG
2276 static Table protectedCRefs = NULL;
2277 
2278 static void
protectCRef(ClauseRef cref)2279 protectCRef(ClauseRef cref)
2280 { GET_LD
2281   void *k;
2282 
2283   if ( !protectedCRefs )
2284     protectedCRefs = newHTable(64);
2285 
2286   if ( (k=lookupHTable(protectedCRefs, cref)) )
2287   { k = (void*)((intptr_t)k+1);
2288     updateHTable(protectedCRefs, cref, k);
2289     // Sdprintf("Protect %p --> %zd\n", cref, (intptr_t)k);
2290   } else
2291   { addNewHTable(protectedCRefs, cref, (void*)1);
2292     // Sdprintf("Protect %p\n", cref);
2293   }
2294 }
2295 
2296 static void
unprotectCRef(ClauseRef cref)2297 unprotectCRef(ClauseRef cref)
2298 { GET_LD
2299   void *k;
2300 
2301   if ( (k=lookupHTable(protectedCRefs, cref)) )
2302   { k = (void*)((intptr_t)k-1);
2303     if ( k )
2304     { updateHTable(protectedCRefs, cref, k);
2305       // Sdprintf("UnProtect %p --> %zd\n", cref, (intptr_t)k);
2306     } else
2307     { deleteHTable(protectedCRefs, cref);
2308       // Sdprintf("UnProtect %p\n", cref);
2309     }
2310   } else
2311   { assert(0);
2312   }
2313 }
2314 
2315 static int
isProtectedCRef(ClauseRef cref)2316 isProtectedCRef(ClauseRef cref)
2317 { GET_LD
2318 
2319   return ( protectedCRefs &&
2320 	   lookupHTable(protectedCRefs, cref) );
2321 }
2322 #endif /*O_DEBUG*/
2323 
2324 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2325 (*) We set the initial oldest_generation to "very old" (0). This ensures
2326 that if a predicate is  registered   dirty  before clause-gc starts, the
2327 oldest generation is 0 and thus no clause reference will be collected.
2328 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2329 
2330 static void
registerDirtyDefinition(Definition def ARG_LD)2331 registerDirtyDefinition(Definition def ARG_LD)
2332 { if ( false(def, P_DIRTYREG) )
2333   { DirtyDefInfo ddi = ddi_new(def);
2334 
2335     if ( addHTable(GD->procedures.dirty, def, ddi) == ddi )
2336     { set(def, P_DIRTYREG);
2337       ATOMIC_ADD(&GD->clauses.dirty, def->impl.clauses.number_of_clauses);
2338     } else
2339     { PL_free(ddi);			/* someone else did this */
2340     }
2341   }
2342   if ( !isSignalledGCThread(SIG_CLAUSE_GC PASS_LD) &&	/* already asked for */
2343        !GD->clauses.cgc_active &&	/* currently running */
2344        considerClauseGC(PASS_LD1) )
2345   { if ( GD->clauses.db_erased_refs > GD->clauses.erased / 10 )
2346     { DEBUG(MSG_CGC_CONSIDER,
2347 	    Sdprintf("CGC: %ld of %ld erased clauses has a clause ref; "
2348 		     "asking for AGC\n",
2349 		     (long)GD->clauses.db_erased_refs,
2350 		     (long)GD->clauses.erased));
2351       signalGCThread(SIG_ATOM_GC);
2352     }
2353     signalGCThread(SIG_CLAUSE_GC);
2354   }
2355 }
2356 
2357 static void
unregisterDirtyDefinition(Definition def)2358 unregisterDirtyDefinition(Definition def)
2359 { DirtyDefInfo ddi;
2360 
2361   if ( (ddi=deleteHTable(GD->procedures.dirty, def)) )
2362   { PL_free(ddi);
2363     clear(def, P_DIRTYREG);
2364     ATOMIC_SUB(&GD->clauses.dirty, def->impl.clauses.number_of_clauses);
2365   }
2366 }
2367 
2368 
2369 static void
maybeUnregisterDirtyDefinition(Definition def)2370 maybeUnregisterDirtyDefinition(Definition def)
2371 { if ( true(def, P_DIRTYREG) &&
2372        def->impl.clauses.erased_clauses == 0 )
2373   { unregisterDirtyDefinition(def);
2374   }
2375 
2376   if ( true(def, P_ERASED) )
2377   { DEBUG(MSG_PROC_COUNT, Sdprintf("Delayed unalloc %s\n", predicateName(def)));
2378     assert(def->module == NULL);
2379     if ( def->impl.clauses.first_clause == NULL )
2380     { if ( def->lingering )
2381       { static int done = FALSE;
2382 	if ( !done )
2383 	{ Sdprintf("maybeUnregisterDirtyDefinition(%s): lingering data\n",
2384 		   predicateName(def));
2385 	  done = TRUE;
2386 	}
2387       }
2388       unregisterDirtyDefinition(def);
2389       deleteIndexes(&def->impl.clauses, TRUE);
2390       freeHeap(def->impl.any.args, sizeof(arg_info)*def->functor->arity);
2391       if ( def->tabling )
2392 	freeHeap(def->tabling, sizeof(*def->tabling));
2393       freeHeap(def, sizeof(*def));
2394     }
2395   }
2396 }
2397 
2398 
2399 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2400 (*) We set the initial generation to   GEN_MAX  to know which predicates
2401 have been marked. We can only reclaim   clauses  that were erased before
2402 the start generation of the clause garbage collector.
2403 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2404 
2405 foreign_t
pl_garbage_collect_clauses(void)2406 pl_garbage_collect_clauses(void)
2407 { GET_LD
2408   int rc = TRUE;
2409 
2410   if ( GD->procedures.dirty->size > 0 &&
2411        COMPARE_AND_SWAP_INT(&GD->clauses.cgc_active, FALSE, TRUE) )
2412   { size_t removed = 0;
2413     size_t erased_pending = GD->clauses.erased_size;
2414     double gct, t0 = ThreadCPUTime(LD, CPU_USER);
2415     gen_t start_gen = global_generation();
2416     int verbose = truePrologFlag(PLFLAG_TRACE_GC) && !LD->in_print_message;
2417 
2418     if ( verbose )
2419     { if ( (rc=printMessage(ATOM_informational,
2420 			    PL_FUNCTOR_CHARS, "cgc", 1,
2421 			      PL_CHARS, "start")) == FALSE )
2422 	goto out;
2423     }
2424 
2425     DEBUG(MSG_CGC, Sdprintf("CGC @ %lld ... ", start_gen));
2426     DEBUG(MSG_CGC_STACK,
2427 	  { Sdprintf("CGC @ %lld ... ", start_gen);
2428 	    PL_backtrace(5,0);
2429 	  });
2430 
2431 					/* sanity-check */
2432     for_table(GD->procedures.dirty, n, v,
2433 	      { DirtyDefInfo ddi = v;
2434 
2435 		DEBUG(CHK_SECURE,
2436 		      { Definition def = n;
2437 			LOCKDEF(def);
2438 			checkDefinition(def);
2439 			UNLOCKDEF(def);
2440 		      });
2441 		ddi_reset(ddi);			  /* see (*) */
2442 	      });
2443 
2444     markPredicatesInEnvironments(LD);
2445 #ifdef O_PLMT
2446     forThreadLocalDataUnsuspended(markPredicatesInEnvironments, 0);
2447 #endif
2448 
2449     DEBUG(MSG_CGC, Sdprintf("(marking done)\n"));
2450 
2451     for_table(GD->procedures.dirty, n, v,
2452 	      { Definition def = n;
2453 		DirtyDefInfo ddi = v;
2454 
2455 		if ( false(def, P_FOREIGN) &&
2456 		     def->impl.clauses.erased_clauses > 0 )
2457 		{ size_t del = cleanDefinition(def, ddi, start_gen, &rc);
2458 
2459 		  removed += del;
2460 		  DEBUG(MSG_CGC_PRED,
2461 			Sdprintf("cleanDefinition(%s, %s): "
2462 				 "%zd clauses (left %d)\n",
2463 				 predicateName(def),
2464 				 ddi_generation_name(ddi),
2465 				 del,
2466 				 (int)def->impl.clauses.erased_clauses));
2467 		}
2468 
2469 		maybeUnregisterDirtyDefinition(def);
2470 	      });
2471 
2472     gcClauseRefs();
2473     GD->clauses.cgc_count++;
2474     GD->clauses.cgc_reclaimed	+= removed;
2475     GD->clauses.cgc_time        += (gct=ThreadCPUTime(LD, CPU_USER) - t0);
2476     GD->clauses.erased_size_last = GD->clauses.erased_size;
2477 
2478     DEBUG(MSG_CGC, Sdprintf("CGC: removed %ld clauses "
2479 			    "(%ld bytes reclaimed, %ld pending) in %2f sec.\n",
2480 			    (long)removed,
2481 			    (long)erased_pending - GD->clauses.erased_size,
2482 			    (long)GD->clauses.erased_size,
2483 			    gct));
2484 
2485     if ( verbose )
2486       rc = printMessage(
2487 	      ATOM_informational,
2488 	      PL_FUNCTOR_CHARS, "cgc", 1,
2489 		PL_FUNCTOR_CHARS, "done", 4,
2490 		  PL_INT64,  (int64_t)removed,
2491 		  PL_INT64,  (int64_t)(erased_pending - GD->clauses.erased_size),
2492 		  PL_INT64,  (int64_t)GD->clauses.erased_size,
2493 		  PL_DOUBLE, gct);
2494 
2495   out:
2496     GD->clauses.cgc_active = FALSE;
2497   }
2498 
2499   return rc;
2500 }
2501 
2502 #endif /*O_CLAUSEGC*/
2503 
2504 #ifdef O_DEBUG
2505 		 /*******************************
2506 		 *	    CHECKING		*
2507 		 *******************************/
2508 
2509 word
pl_check_definition(term_t spec)2510 pl_check_definition(term_t spec)
2511 { GET_LD
2512   Procedure proc;
2513   Definition def;
2514   int nclauses = 0;
2515   int nerased = 0;
2516   int nindexable = 0;
2517   ClauseRef cref;
2518 
2519   if ( !get_procedure(spec, &proc, 0, GP_FIND) )
2520     return Sdprintf("$check_definition/1: can't find definition");
2521   def = getProcDefinition(proc);
2522 
2523   if ( true(def, P_FOREIGN) )
2524     succeed;
2525 
2526   acquire_def(def);
2527   for(cref = def->impl.clauses.first_clause; cref; cref = cref->next)
2528   { Clause clause = cref->value.clause;
2529 
2530     if ( cref->d.key == 0 )
2531       nindexable++;
2532 
2533     if ( false(clause, CL_ERASED) )
2534       nclauses++;
2535     else
2536       nerased++;
2537   }
2538   release_def(def);
2539 
2540   if ( nerased != def->impl.clauses.erased_clauses )
2541     Sdprintf("%s has %d erased clauses, claims %d\n",
2542 	     predicateName(def), nerased, def->impl.clauses.erased_clauses);
2543 
2544   checkClauseIndexSizes(def, nindexable);
2545 
2546   if ( def->impl.clauses.number_of_clauses != nclauses )
2547     Sdprintf("%s has inconsistent number_of_clauses (%d, should be %d)",
2548 	     predicateName(def), def->impl.clauses.number_of_clauses, nclauses);
2549 
2550   succeed;
2551 }
2552 #endif /*O_DEBUG*/
2553 
2554 		/********************************
2555 		*     UNDEFINED PROCEDURES      *
2556 		*********************************/
2557 
2558 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2559 A dynamic call to `f' in `m' has to be made (via call/1 or from C). This
2560 procedure returns the procedure to be run.   If no such procedure exists
2561 an undefined procedure is created and returned. In this case interpret()
2562 will later call trapUndefined() to generate   an  error message (or link
2563 the procedure from the library via autoload).
2564 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2565 
2566 Procedure
resolveProcedure__LD(functor_t f,Module module ARG_LD)2567 resolveProcedure__LD(functor_t f, Module module ARG_LD)
2568 { Procedure proc;
2569 
2570   if ( (proc = visibleProcedure(f, module PASS_LD)) )
2571     return proc;
2572 
2573   return lookupProcedure(f, module);
2574 }
2575 
2576 
2577 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2578 autoImport() tries to autoimport  f  into   module  `m'  and returns the
2579 definition if this is possible.
2580 
2581 PROBLEM: I'm not entirely  sure  it  is  save  to  deallocated  the  old
2582 definition  structure  in  all  cases.   It  is  not  member of any heap
2583 structure, thus sofar everything  is  alright.   After  a  dynamic  link
2584 interpret()  picks up the new definition pointer, thus this should be ok
2585 as well.  Any other C-code that  does  nasty  things  (non-deterministic
2586 code  perhaps,  calls  indirect via C? (I do recall once conciously have
2587 decided its not save, but can't recall why ...)
2588 
2589 Its definitely not safe in MT context as   others  may be racing for the
2590 definition.  How  do  we  get   this    working   without   locking  the
2591 proc->definition fetch?
2592 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2593 
2594 Definition
autoImport(functor_t f,Module m)2595 autoImport(functor_t f, Module m)
2596 { GET_LD
2597   Procedure proc;
2598   Definition def, odef;
2599   ListCell c;
2600 					/* Defined: no problem */
2601   if ( (proc = isCurrentProcedure(f, m)) )
2602   { if ( isDefinedProcedure(proc) )
2603       return proc->definition;
2604     if ( true(proc->definition, P_AUTOLOAD) )
2605       return NULL;
2606   }
2607 
2608   for(c=m->supers; c; c=c->next)
2609   { Module s = c->value;
2610 
2611     if ( (def = autoImport(f, s)) )
2612       goto found;
2613   }
2614   return NULL;
2615 
2616 found:
2617   if ( proc == NULL )			/* Create header if not there */
2618   { if ( !(proc = lookupProcedure(f, m)) )
2619       return NULL;
2620   }
2621 					/* Now, take the lock also used */
2622 					/* by lookupProcedure().  Note */
2623 					/* that another thread may have */
2624 					/* done the job for us. */
2625   LOCKMODULE(m);
2626   if ( (odef=proc->definition) != def )	/* Nope, we must link the def */
2627   { shareDefinition(def);
2628     proc->definition = def;
2629 
2630     if ( unshareDefinition(odef) == 0 )
2631     {
2632 #ifdef O_PLMT
2633       PL_LOCK(L_THREAD);
2634       if ( (GD->statistics.threads_created -
2635 	    GD->statistics.threads_finished) == 1 )
2636       { DEBUG(MSG_PROC_COUNT, Sdprintf("Unalloc %s\n", predicateName(odef)));
2637 	unregisterDirtyDefinition(odef);
2638 	freeHeap(odef, sizeof(*odef));
2639 	GD->statistics.predicates--;
2640       } else
2641       { DEBUG(MSG_PROC, Sdprintf("autoImport(%s,%s): Linger %s (%p)\n",
2642 				 functorName(f), PL_atom_chars(m->name),
2643 				 predicateName(odef), odef));
2644 	lingerDefinition(odef);
2645       }
2646       PL_UNLOCK(L_THREAD);
2647 #else
2648       freeHeap(odef, sizeof(struct definition));
2649 #endif
2650     }
2651   }
2652   UNLOCKMODULE(m);
2653 
2654   return def;
2655 }
2656 
2657 
2658 static int
test_autoload_loop(Definition def ARG_LD)2659 test_autoload_loop(Definition def ARG_LD)
2660 { DefinitionChain ch;
2661 
2662   for(ch=LD->autoload.nesting; ch; ch=ch->next)
2663   { if ( ch->definition == def )
2664     { LD->autoload.loop = def;
2665 
2666       Sdprintf("ERROR: autoload loop:\n");
2667       Sdprintf("ERROR:   %s\n", predicateName(def));
2668 
2669       for(ch=LD->autoload.nesting; ch; ch=ch->next)
2670       { Sdprintf("ERROR:   %s\n", predicateName(ch->definition));
2671 	if ( ch->definition == def )
2672 	  break;
2673       }
2674 
2675       return FALSE;
2676     }
2677   }
2678 
2679   return TRUE;
2680 }
2681 
2682 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2683 Call the autoloader for the given definition.
2684 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2685 
2686 static atom_t
autoLoader(Definition def)2687 autoLoader(Definition def)
2688 { GET_LD
2689   fid_t  cid;
2690   term_t argv;
2691   qid_t qid;
2692   atom_t answer = ATOM_nil;
2693   struct definition_chain cell;
2694 
2695   if ( !GD->procedures.undefinterc4 )
2696     GD->procedures.undefinterc4 = PL_pred(FUNCTOR_undefinterc4,
2697 					  MODULE_system);
2698 
2699   if ( !(cid  = PL_open_foreign_frame()) ||
2700        !(argv = PL_new_term_refs(4)) )
2701     return answer;
2702 
2703   PL_put_atom(    argv+0, def->module->name);
2704   PL_put_atom(    argv+1, def->functor->name);
2705   PL_put_integer( argv+2, def->functor->arity);
2706 
2707   push_input_context(ATOM_autoload);
2708   cell.definition = def;
2709   cell.next = LD->autoload.nesting;
2710   LD->autoload.nesting = &cell;
2711   if ( (qid = PL_open_query(MODULE_system, PL_Q_NODEBUG|PL_Q_PASS_EXCEPTION,
2712 			    GD->procedures.undefinterc4, argv)) )
2713   { if ( PL_next_solution(qid) )
2714       PL_get_atom(argv+3, &answer);
2715     PL_close_query(qid);
2716   } else if ( PL_exception(0) )
2717   { PL_clear_exception();
2718   }
2719   LD->autoload.nesting = LD->autoload.nesting->next;
2720   pop_input_context();
2721   PL_discard_foreign_frame(cid);
2722 
2723   return answer;
2724 }
2725 
2726 
2727 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2728 According to Paulo Moura, predicates defined either dynamic, multifile or
2729 discontiguous should not cause an undefined predicate warning.
2730 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2731 
2732 Definition
trapUndefined(Definition def ARG_LD)2733 trapUndefined(Definition def ARG_LD)
2734 { int retry_times = 0;
2735   Definition newdef;
2736   Module module = def->module;
2737   FunctorDef functor = def->functor;
2738 
2739   retry:
2740 					/* Auto import */
2741   if ( (newdef = autoImport(functor->functor, module)) )
2742     return newdef;
2743 					/* Pred/Module does not want to trap */
2744   if ( true(def, PROC_DEFINED) ||
2745        getUnknownModule(module) == UNKNOWN_FAIL )
2746     return def;
2747 
2748   DEBUG(5, Sdprintf("trapUndefined(%s)\n", predicateName(def)));
2749 
2750 					/* Trap via exception/3 */
2751   if ( truePrologFlag(PLFLAG_AUTOLOAD) && !GD->bootsession )
2752   { if ( !test_autoload_loop(def PASS_LD) )
2753     { return def;
2754     } else
2755     { atom_t answer = autoLoader(def);
2756 
2757       def = lookupDefinition(functor->functor, module);
2758 
2759       if ( answer == ATOM_fail )
2760       { return def;
2761       } else if ( answer == ATOM_error )
2762       { goto error;
2763       } else if ( answer == ATOM_retry )
2764       { if ( retry_times++ )
2765 	{ warning("[Thread %d]: exception handler failed to define %s\n",
2766 		  PL_thread_self(),
2767 		  predicateName(def));
2768 	  return def;
2769 	}
2770 	goto retry;
2771       }
2772     }
2773   }
2774 				/* No one wants to intercept */
2775 error:
2776   if ( GD->bootsession )
2777   { sysError("Undefined predicate: %s", predicateName(def));
2778   } else
2779   { createUndefSupervisor(def);
2780   }
2781 
2782   return def;
2783 }
2784 
2785 
2786 		 /*******************************
2787 		 *	  REQUIRE SUPPORT	*
2788 		 *******************************/
2789 
2790 word
pl_require(term_t pred)2791 pl_require(term_t pred)
2792 { Procedure proc;
2793 
2794   if ( !get_procedure(pred, &proc, 0, GP_RESOLVE) )
2795     return get_procedure(pred, &proc, 0, GP_DEFINE);
2796 
2797   succeed;
2798 }
2799 
2800 
2801 		/********************************
2802 		*            RETRACT            *
2803 		*********************************/
2804 
2805 typedef struct
2806 { Definition def;
2807   struct clause_choice chp;
2808   int allocated;
2809 } retract_context;
2810 
2811 static retract_context *
alloc_retract_context(retract_context * ctx0)2812 alloc_retract_context(retract_context *ctx0)
2813 { retract_context *ctx = allocForeignState(sizeof(*ctx));
2814 
2815   *ctx = *ctx0;
2816   ctx->allocated = TRUE;
2817 
2818   return ctx;
2819 }
2820 
2821 static void
free_retract_context(retract_context * ctx ARG_LD)2822 free_retract_context(retract_context *ctx ARG_LD)
2823 { popPredicateAccess(ctx->def);
2824   leaveDefinition(ctx->def);
2825 
2826   if ( ctx->allocated )
2827     freeForeignState(ctx, sizeof(*ctx));
2828 }
2829 
2830 static
2831 PRED_IMPL("retract", 1, retract,
2832 	  PL_FA_TRANSPARENT|PL_FA_NONDETERMINISTIC|PL_FA_ISO)
2833 { PRED_LD
2834   term_t term = A1;
2835   retract_context ctxbuf;
2836   retract_context *ctx;
2837   ClauseRef cref;
2838 
2839   if ( CTX_CNTRL == FRG_CUTTED )
2840   { ctx = CTX_PTR;
2841 
2842     unprotectCRef(ctx->chp.cref);
2843     free_retract_context(ctx PASS_LD);
2844 
2845     return TRUE;
2846   } else
2847   { Module m = NULL;
2848     term_t cl = PL_new_term_ref();
2849     term_t head = PL_new_term_ref();
2850     term_t body = PL_new_term_ref();
2851     Word argv;
2852     atom_t b;
2853     fid_t fid;
2854     definition_ref *dref = NULL;
2855 
2856     if ( !PL_strip_module_ex(term, &m, cl) ||
2857 	 !get_head_and_body_clause(cl, head, body, NULL PASS_LD) )
2858       return FALSE;
2859     if ( PL_get_atom(body, &b) && b == ATOM_true )
2860       PL_put_term(cl, head);
2861 
2862     argv = valTermRef(head);
2863     deRef(argv);
2864     if ( isTerm(*argv) )		/* retract(foobar(a1, ...)) */
2865       argv = argTermP(*argv, 0);
2866     else
2867       argv = NULL;			/* retract(foobar) */
2868 
2869     if ( CTX_CNTRL == FRG_FIRST_CALL )
2870     { functor_t fd;
2871       Procedure proc;
2872       Definition def;
2873 
2874       if ( !PL_get_functor(head, &fd) )
2875 	return PL_error(NULL, 0, NULL, ERR_TYPE, ATOM_callable, head);
2876       if ( !(proc = isCurrentProcedure(fd, m)) )
2877       { checkModifySystemProc(fd);
2878 	fail;
2879       }
2880 
2881       def = getProcDefinition(proc);
2882 
2883       if ( true(def, P_FOREIGN) )
2884 	return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, proc);
2885       if ( false(def, P_DYNAMIC) )
2886       { if ( isDefinedProcedure(proc) )
2887 	  return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, proc);
2888 	setDynamicDefinition(def, TRUE); /* implicit */
2889 	fail;				/* no clauses */
2890       }
2891 
2892       enterDefinition(def);			/* reference the predicate */
2893       dref = pushPredicateAccessObj(def PASS_LD);
2894       setGenerationFrameVal(environment_frame, dref->generation);
2895       cref = firstClause(argv, environment_frame, def, &ctxbuf.chp PASS_LD);
2896       DEBUG(MSG_CGC_RETRACT,
2897 	    Sdprintf("Started retract from %s at gen = %lld\n",
2898 		     predicateName(def), generationFrame(environment_frame)));
2899       if ( !cref )
2900       { popPredicateAccess(def);
2901 	leaveDefinition(def);
2902 	fail;
2903       }
2904 
2905       ctx = &ctxbuf;
2906       ctx->def = def;
2907       ctx->allocated = 0;
2908     } else
2909     { ctx  = CTX_PTR;
2910       DEBUG(MSG_CGC_RETRACT,
2911 	    Sdprintf("Retry retract from %s at gen = %lld\n",
2912 		     predicateName(ctx->def),
2913 		     generationFrame(environment_frame)));
2914       unprotectCRef(ctx->chp.cref);
2915       cref = nextClause(&ctx->chp, argv, environment_frame, ctx->def);
2916     }
2917 
2918     if ( !(fid = PL_open_foreign_frame()) )
2919     { free_retract_context(ctx PASS_LD);
2920       return FALSE;
2921     }
2922 
2923     while( cref )
2924     { if ( decompile(cref->value.clause, cl, 0) )
2925       { if ( retractClauseDefinition(ctx->def, cref->value.clause) ||
2926 	     CTX_CNTRL != FRG_FIRST_CALL )
2927 	{ if ( !ctx->chp.cref )		/* deterministic last one */
2928 	  { free_retract_context(ctx PASS_LD);
2929 	    PL_close_foreign_frame(fid);
2930 	    return TRUE;
2931 	  }
2932 
2933 	  if ( ctx == &ctxbuf )		/* non-determinisic; save state */
2934 	    ctx = alloc_retract_context(ctx);
2935 
2936 	  DEBUG(0,
2937 		assert(GLOBALLY_VISIBLE_CLAUSE(
2938 			   ctx->chp.cref->value.clause,
2939 			   generationFrame(environment_frame))));
2940 	  protectCRef(ctx->chp.cref);
2941 
2942 	  PL_close_foreign_frame(fid);
2943 	  ForeignRedoPtr(ctx);
2944 	} else
2945 	{ setGenerationFrame(environment_frame);
2946 	  assert(dref);
2947 	  dref->generation = generationFrame(environment_frame);
2948 	  DEBUG(MSG_CGC_RETRACT,
2949 		Sdprintf("Retract: first clause deleted; set gen to %lld\n",
2950 			 generationFrame(environment_frame)));
2951 	}
2952       }
2953 
2954       if ( PL_exception(0) )
2955 	break;
2956 
2957       PL_rewind_foreign_frame(fid);
2958       cref = nextClause(&ctx->chp, argv, environment_frame, ctx->def);
2959     }
2960 
2961     PL_close_foreign_frame(fid);
2962     free_retract_context(ctx PASS_LD);
2963     return FALSE;
2964   }
2965 }
2966 
2967 
2968 static int
allVars(int argc,Word argv ARG_LD)2969 allVars(int argc, Word argv ARG_LD)
2970 { int i, r, allvars = TRUE;
2971   Word *reset = alloca(argc*sizeof(Word));
2972 
2973   for(i=0; i<argc; i++)
2974   { Word p2;
2975 
2976     deRef2(argv+i, p2);
2977     if ( isVar(*p2) )
2978     { reset[i] = p2;
2979       *p2 = ATOM_nil;
2980     } else
2981     { allvars = FALSE;
2982       break;
2983     }
2984   }
2985 
2986   for(r=0; r<i; r++)
2987     setVar(*reset[r]);
2988 
2989   return allvars;
2990 }
2991 
2992 
2993 static
2994 PRED_IMPL("retractall", 1, retractall, PL_FA_NONDETERMINISTIC|PL_FA_ISO)
2995 { GET_LD
2996   term_t head = A1;
2997   term_t thehead = PL_new_term_ref();
2998   Procedure proc;
2999   Definition def;
3000   ClauseRef cref;
3001   Word argv;
3002   int allvars = TRUE;
3003   fid_t fid;
3004   int rc = TRUE;
3005 
3006   if ( !get_procedure(head, &proc, thehead, GP_CREATE) )
3007     fail;
3008 
3009   def = getProcDefinition(proc);
3010   if ( true(def, P_FOREIGN) )
3011     return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, proc);
3012   if ( false(def, P_DYNAMIC) )
3013   { if ( isDefinedProcedure(proc) )
3014       return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, proc);
3015     if ( !setDynamicDefinition(def, TRUE) )
3016       fail;
3017     succeed;				/* nothing to retract */
3018   }
3019 
3020   if ( !retractall_event(def, thehead, FUNCTOR_start1 PASS_LD) )
3021     return FALSE;
3022 
3023   argv = valTermRef(thehead);
3024   deRef(argv);
3025   if ( isTerm(*argv) )
3026   { int arity = arityTerm(*argv);
3027     argv = argTermP(*argv, 0);
3028 
3029     allvars = allVars(arity, argv PASS_LD);
3030   } else
3031   { allvars = TRUE;
3032     argv = NULL;
3033   }
3034 
3035   enterDefinition(def);
3036   setGenerationFrameVal(environment_frame, pushPredicateAccess(def));
3037   fid = PL_open_foreign_frame();
3038 
3039   DEBUG(CHK_SECURE,
3040 	LOCKDEF(def);
3041 	checkDefinition(def);
3042         UNLOCKDEF(def));
3043   if ( allvars )
3044   { gen_t gen = generationFrame(environment_frame);
3045 
3046     acquire_def(def);
3047     for(cref = def->impl.clauses.first_clause; cref; cref = cref->next)
3048     { if ( visibleClauseCNT(cref->value.clause, gen) )
3049       { if ( !(rc=retractClauseDefinition(def, cref->value.clause)) )
3050 	{ if ( PL_exception(0) )
3051 	    break;
3052 	}
3053       }
3054     }
3055     release_def(def);
3056     rc = TRUE;
3057   } else
3058   { struct clause_choice chp;
3059 
3060     if ( !(cref = firstClause(argv, environment_frame, def, &chp PASS_LD)) )
3061     { popPredicateAccess(def);
3062       leaveDefinition(def);
3063       return TRUE;
3064     }
3065 
3066     while( cref )
3067     { if ( decompileHead(cref->value.clause, thehead) )
3068       { if ( !(rc=retractClauseDefinition(def, cref->value.clause)) )
3069 	{ if ( PL_exception(0) )
3070 	    break;
3071 	}
3072       } else
3073       { if ( PL_exception(0) )
3074 	{ rc = FALSE;
3075 	  break;
3076 	}
3077       }
3078 
3079       PL_rewind_foreign_frame(fid);
3080 
3081       if ( !chp.cref )
3082       { popPredicateAccess(def);
3083 	leaveDefinition(def);
3084 	return TRUE;
3085       }
3086 
3087       if ( argv )				/* may be shifted */
3088       { argv = valTermRef(thehead);
3089 	argv = argTermP(*argv, 0);
3090       }
3091 
3092       cref = nextClause(&chp, argv, environment_frame, def);
3093       rc = TRUE;
3094     }
3095   }
3096   popPredicateAccess(def);
3097   leaveDefinition(def);
3098   DEBUG(CHK_SECURE,
3099 	LOCKDEF(def);
3100 	checkDefinition(def);
3101 	UNLOCKDEF(def));
3102 
3103   if ( rc )
3104     rc = retractall_event(def, thehead, FUNCTOR_end1 PASS_LD);
3105 
3106   return rc;
3107 }
3108 
3109 		/********************************
3110 		*       PROLOG PREDICATES       *
3111 		*********************************/
3112 
3113 static word
do_abolish(Module m,term_t atom,term_t arity)3114 do_abolish(Module m, term_t atom, term_t arity)
3115 { GET_LD
3116   functor_t f;
3117   Procedure proc;
3118   atom_t name;
3119   int a = 0;
3120 
3121   if ( !PL_get_atom_ex(atom, &name) ||
3122        !get_arity(arity, 0, MAXARITY, &a) )
3123     fail;
3124 
3125   if ( !(f = isCurrentFunctor(name, a)) )
3126     succeed;
3127   if ( !checkModifySystemProc(f) )
3128     fail;
3129   if ( !(proc = isCurrentProcedure(f, m)) )
3130     succeed;
3131 
3132   if ( truePrologFlag(PLFLAG_ISO) && false(proc->definition, P_DYNAMIC) )
3133     return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, proc);
3134 
3135   return abolishProcedure(proc, m);
3136 }
3137 
3138 
3139 word
pl_abolish(term_t name,term_t arity)3140 pl_abolish(term_t name, term_t arity)	/* Name, Arity */
3141 { GET_LD
3142   Module m = NULL;
3143 
3144   return ( PL_strip_module(name, &m, name) &&
3145 	   do_abolish(m, name, arity)
3146 	 );
3147 }
3148 
3149 
3150 word
pl_abolish1(term_t spec)3151 pl_abolish1(term_t spec)		/* Name/Arity */
3152 { GET_LD
3153   term_t name  = PL_new_term_ref();
3154   term_t arity = PL_new_term_ref();
3155   Module m = NULL;
3156 
3157   if ( !PL_strip_module(spec, &m, spec) )
3158     return FALSE;
3159 
3160   if ( !PL_is_functor(spec, FUNCTOR_divide2) )
3161     return PL_error(NULL, 0, NULL, ERR_TYPE, ATOM_predicate_indicator, spec);
3162 
3163   _PL_get_arg(1, spec, name);
3164   _PL_get_arg(2, spec, arity);
3165 
3166   return do_abolish(m, name, arity);
3167 }
3168 
3169 
3170 typedef struct patt_mask
3171 { atom_t	key;
3172   unsigned int  mask;
3173 } patt_mask;
3174 
3175 static const patt_mask patt_masks[] =
3176 { { ATOM_dynamic,	   P_DYNAMIC },
3177   { ATOM_multifile,	   P_MULTIFILE },
3178   { ATOM_locked,	   P_LOCKED },
3179   { ATOM_system,	   P_LOCKED },		/* compatibility */
3180   { ATOM_spy,		   SPY_ME },
3181   { ATOM_tabled,	   P_TABLED },
3182   { ATOM_incremental,	   P_INCREMENTAL },
3183   { ATOM_tshared,	   P_TSHARED },
3184   { ATOM_trace,		   TRACE_ME },
3185   { ATOM_hide_childs,	   HIDE_CHILDS },
3186   { ATOM_transparent,	   P_TRANSPARENT },
3187   { ATOM_discontiguous,	   P_DISCONTIGUOUS },
3188   { ATOM_volatile,	   P_VOLATILE },
3189   { ATOM_thread_local,	   P_THREAD_LOCAL },
3190   { ATOM_noprofile,	   P_NOPROFILE },
3191   { ATOM_iso,		   P_ISO },
3192   { ATOM_public,	   P_PUBLIC },
3193   { ATOM_non_terminal,	   P_NON_TERMINAL },
3194   { ATOM_quasi_quotation_syntax, P_QUASI_QUOTATION_SYNTAX },
3195   { ATOM_clausable,	   P_CLAUSABLE },
3196   { ATOM_autoload,	   P_AUTOLOAD },
3197   { (atom_t)0,		   0 }
3198 };
3199 
3200 static unsigned int
attribute_mask(atom_t key)3201 attribute_mask(atom_t key)
3202 { const patt_mask *p;
3203 
3204   for(p=patt_masks; p->key; p++)
3205   { if ( p->key == key )
3206       return p->mask;
3207   }
3208 
3209   { GET_LD
3210     term_t t;
3211 
3212     return ( (t = PL_new_term_ref()) &&
3213 	     PL_put_atom(t, key) &&
3214 	     PL_domain_error("predicate_property", t)
3215 	   );
3216   }
3217 }
3218 
3219 
3220 static size_t
num_visible_clauses(Definition def,atom_t key)3221 num_visible_clauses(Definition def, atom_t key)
3222 { GET_LD
3223 
3224   if ( LD->gen_reload != GEN_INVALID )
3225   { ClauseRef c;
3226     size_t num_clauses = 0;
3227 
3228     acquire_def(def);
3229     for(c = def->impl.clauses.first_clause; c; c = c->next)
3230     { Clause cl = c->value.clause;
3231       if ( key == ATOM_number_of_rules && true(cl, UNIT_CLAUSE) )
3232         continue;
3233       if ( visibleClause(cl, generationFrame(environment_frame)) )
3234         num_clauses++;
3235     }
3236     release_def(def);
3237     return num_clauses;
3238   }
3239 
3240   if ( key == ATOM_number_of_clauses )
3241     return def->impl.clauses.number_of_clauses;
3242   else
3243     return def->impl.clauses.number_of_rules;
3244 }
3245 
3246 
3247 size_t
sizeof_predicate(Definition def)3248 sizeof_predicate(Definition def)
3249 { GET_LD
3250   size_t size = sizeof(*def);
3251 
3252   size += sizeof_supervisor(def->codes);
3253 
3254   if ( false(def, P_FOREIGN) )
3255   { ClauseRef c;
3256 
3257     acquire_def(def);
3258     for(c = def->impl.clauses.first_clause; c; c = c->next)
3259     { Clause cl = c->value.clause;
3260 
3261       size += sizeofClause(cl->code_size);
3262       size += SIZEOF_CREF_CLAUSE;
3263     }
3264     release_def(def);
3265 
3266     size += sizeofClauseIndexes(def);
3267   }
3268 
3269   return size;
3270 }
3271 
3272 
3273 static
3274 PRED_IMPL("$get_predicate_attribute", 3, get_predicate_attribute,
3275 	  PL_FA_TRANSPARENT)
3276 { PRED_LD
3277   term_t pred  = A1;
3278   term_t what  = A2;
3279   term_t value = A3;
3280   Procedure proc;
3281   Definition def;
3282   functor_t fd;
3283   atom_t key;
3284   Module module = (Module) NULL;
3285   unsigned int att;
3286   term_t head = PL_new_term_ref();
3287 
3288   if ( !PL_strip_module(pred, &module, head) ||
3289        !PL_get_functor(head, &fd) ||
3290        ( !(proc = visibleProcedure(fd, module PASS_LD)) &&
3291 	 !(proc = isCurrentProcedure(fd, module)) ) )
3292     fail;
3293 
3294   def = proc->definition;
3295 
3296   if ( !PL_get_atom(what, &key) )
3297     return PL_error(NULL, 0, NULL, ERR_TYPE, ATOM_atom, what);
3298 
3299   if ( key == ATOM_imported )
3300   { if ( module == def->module )
3301       fail;
3302     return PL_unify_atom(value, def->module->name);
3303   } else if ( key == ATOM_indexed )
3304   { return unify_index_pattern(proc, value);
3305   } else if ( key == ATOM_meta_predicate )
3306   { if ( false(def, P_META) )
3307       fail;
3308     return unify_meta_pattern(proc, value);
3309   } else if ( key == ATOM_exported )
3310   { return PL_unify_integer(value, isPublicModule(module, proc));
3311   } else if ( key == ATOM_defined )
3312   { int d;
3313 
3314     if ( isDefinedProcedure(proc) )
3315       d = 1;
3316     else
3317       d = 0;
3318 
3319     return PL_unify_integer(value, d);
3320   } else if ( key == ATOM_line_count || key == ATOM_file )
3321   { int line;
3322     Clause clause;
3323     int rc = FALSE;
3324 
3325     if ( false(def, P_FOREIGN|P_THREAD_LOCAL) )
3326     { acquire_def(def);
3327       if ( def->impl.clauses.first_clause &&
3328 	   (clause = def->impl.clauses.first_clause->value.clause) &&
3329 	   (line=clause->line_no) )
3330       { if ( key == ATOM_line_count )
3331 	{ rc = PL_unify_integer(value, line);
3332 	} else
3333 	{ SourceFile sf = indexToSourceFile(clause->source_no);
3334 
3335 	  if ( sf )
3336 	    rc = PL_unify_atom(value, sf->name);
3337 	}
3338       }
3339       release_def(def);
3340     }
3341 
3342     return rc;
3343   } else if ( key == ATOM_foreign )
3344   { return PL_unify_integer(value, true(def, P_FOREIGN) ? 1 : 0);
3345   } else if ( key == ATOM_number_of_clauses )
3346   { size_t num_clauses;
3347     if ( def->flags & P_FOREIGN )
3348       fail;
3349 
3350     def = getProcDefinition(proc);
3351     num_clauses = num_visible_clauses(def, key);
3352     if ( num_clauses == 0 && false(def, P_DYNAMIC) )
3353       fail;
3354     return PL_unify_int64(value, num_clauses);
3355   } else if ( key == ATOM_last_modified_generation )
3356   { if ( def->flags & P_FOREIGN )
3357       fail;
3358     def = getProcDefinition(proc);
3359     return PL_unify_int64(value, def->last_modified);
3360   } else if ( key == ATOM_number_of_rules )
3361   { if ( def->flags & P_FOREIGN )
3362       fail;
3363 
3364     def = getProcDefinition(proc);
3365     if ( def->impl.clauses.number_of_clauses == 0 && false(def, P_DYNAMIC) )
3366       fail;
3367     return PL_unify_integer(value, num_visible_clauses(def, key));
3368   } else if ( key == ATOM_size )
3369   { def = getProcDefinition(proc);
3370     return PL_unify_integer(value, sizeof_predicate(def));
3371   } else if ( tbl_is_predicate_attribute(key) )
3372   { size_t sz_value;
3373 
3374     if ( tbl_get_predicate_attribute(def, key, &sz_value) == TRUE )
3375       return PL_unify_int64(value, sz_value);
3376     return FALSE;
3377   } else if ( (att = attribute_mask(key)) )
3378   { return PL_unify_integer(value, (def->flags & att) ? 1 : 0);
3379   } else
3380   { return FALSE;
3381   }
3382 }
3383 
3384 
3385 static int
setDynamicDefinition_unlocked(Definition def,bool isdyn)3386 setDynamicDefinition_unlocked(Definition def, bool isdyn)
3387 { GET_LD
3388 
3389   if ( ( isdyn &&  true(def, P_DYNAMIC)) ||
3390        (!isdyn && false(def, P_DYNAMIC)) )
3391     return TRUE;
3392 
3393   if ( isdyn )				/* static --> dynamic */
3394   { if ( truePrologFlag(PLFLAG_PROTECT_STATIC_CODE) &&
3395 	 hasClausesDefinition(def) )
3396       return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PREDICATE, def);
3397 
3398     set(def, P_DYNAMIC);
3399     freeCodesDefinition(def, TRUE);	/* reset to S_VIRGIN */
3400   } else				/* dynamic --> static */
3401   { clear(def, P_DYNAMIC);
3402     freeCodesDefinition(def, TRUE);	/* reset to S_VIRGIN */
3403   }
3404 
3405   return TRUE;
3406 }
3407 
3408 
3409 int
setDynamicDefinition(Definition def,bool isdyn)3410 setDynamicDefinition(Definition def, bool isdyn)
3411 { int rc;
3412 
3413   LOCKDEF(def);
3414   rc = setDynamicDefinition_unlocked(def, isdyn);
3415   UNLOCKDEF(def);
3416 
3417   return rc;
3418 }
3419 
3420 int
setThreadLocalDefinition(Definition def,bool val)3421 setThreadLocalDefinition(Definition def, bool val)
3422 {
3423 #ifdef O_PLMT
3424 
3425   LOCKDEF(def);
3426   if ( (val && true(def, P_THREAD_LOCAL)) ||
3427        (!val && false(def, P_THREAD_LOCAL)) )
3428   { UNLOCKDEF(def);
3429     return TRUE;
3430   }
3431 
3432   if ( val )				/* static --> local */
3433   { if ( def->impl.clauses.first_clause )
3434     { UNLOCKDEF(def);
3435       return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PREDICATE, def);
3436     }
3437 
3438     def->impl.local.local = new_ldef_vector();
3439     MEMORY_RELEASE();
3440     set(def, P_DYNAMIC|P_VOLATILE|P_THREAD_LOCAL);
3441     def->codes = SUPERVISOR(thread_local);
3442 
3443     UNLOCKDEF(def);
3444     return TRUE;
3445   } else				/* local --> static */
3446   { UNLOCKDEF(def);
3447     return PL_error(NULL, 0, "predicate is thread-local",
3448 		    ERR_MODIFY_STATIC_PREDICATE, def);
3449   }
3450 #else
3451   setDynamicDefinition(def, val);
3452 
3453   if ( val )
3454     set(def, P_VOLATILE|P_THREAD_LOCAL);
3455   else
3456     clear(def, P_VOLATILE|P_THREAD_LOCAL);
3457 
3458   succeed;
3459 #endif
3460 }
3461 
3462 
3463 static int
setClausableDefinition(Definition def,int val)3464 setClausableDefinition(Definition def, int val)
3465 { GET_LD
3466 
3467   if ( val )
3468   { if ( truePrologFlag(PLFLAG_PROTECT_STATIC_CODE) &&
3469 	 hasClausesDefinition(def) )
3470       return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PREDICATE, def);
3471     set(def, P_CLAUSABLE);
3472   } else
3473   { clear(def, P_CLAUSABLE);
3474   }
3475 
3476   return TRUE;
3477 }
3478 
3479 int
setAttrDefinition(Definition def,unsigned attr,int val)3480 setAttrDefinition(Definition def, unsigned attr, int val)
3481 { int rc;
3482 
3483   if ( attr == P_DYNAMIC )
3484   { rc = setDynamicDefinition(def, val);
3485   } else if ( attr == P_THREAD_LOCAL )
3486   { rc = setThreadLocalDefinition(def, val);
3487   } else if ( attr == P_CLAUSABLE )
3488   { rc = setClausableDefinition(def, val);
3489   } else
3490   { if ( !val )
3491     { clear(def, attr);
3492     } else
3493     { set(def, attr);
3494     }
3495     if ( attr == P_INCREMENTAL )
3496       freeCodesDefinition(def, TRUE);
3497 
3498     rc = TRUE;
3499   }
3500 
3501   return rc;
3502 }
3503 
3504 
3505 static int
get_bool_or_int_ex(term_t t,int * val ARG_LD)3506 get_bool_or_int_ex(term_t t, int *val ARG_LD)
3507 { if ( PL_get_bool(t, val) )
3508     return TRUE;
3509   if ( PL_get_integer(t, val) && !(*val & ~1) )
3510     return TRUE;			/* accept 0 and 1 */
3511   return PL_get_bool_ex(t, val);	/* generate an error */
3512 }
3513 
3514 
3515 static
3516 PRED_IMPL("$set_predicate_attribute", 3, set_predicate_attribute,
3517 	  PL_FA_TRANSPARENT)
3518 { PRED_LD
3519   term_t pred  = A1;
3520   term_t what  = A2;
3521   term_t value = A3;
3522   Procedure proc;
3523   Definition def;
3524   atom_t key;
3525   int val;
3526   uintptr_t att;
3527 
3528   if ( !PL_get_atom_ex(what, &key) )
3529     return FALSE;
3530   if ( tbl_is_predicate_attribute(key) )
3531   { size_t v;
3532     atom_t inf;
3533 
3534     if ( PL_get_atom(value, &inf) && inf == ATOM_infinite )
3535       v	= (size_t)-1;
3536     else if ( !PL_get_size_ex(value, &v) )
3537       return FALSE;
3538 
3539     if ( get_procedure(pred, &proc, 0, GP_DEFINE|GP_NAMEARITY) )
3540       return tbl_set_predicate_attribute(proc->definition, key, v) == TRUE;
3541 
3542     return FALSE;
3543   }
3544 
3545   if ( !get_bool_or_int_ex(value, &val PASS_LD) ||
3546        !(att = attribute_mask(key)) )
3547     return FALSE;
3548 
3549   if ( (att&SPY_ME) )
3550   { if ( !get_procedure(pred, &proc, 0, GP_RESOLVE) )
3551       fail;
3552   } else
3553   { if ( !get_procedure(pred, &proc, 0, GP_DEFINE|GP_NAMEARITY) )
3554       fail;
3555   }
3556   def = proc->definition;
3557 
3558   if ( ReadingSource && MODULE_parse == def->module )
3559   { SourceFile sf = lookupSourceFile(source_file_name, TRUE);
3560     int rc = setAttrProcedureSource(sf, proc, att, val PASS_LD);
3561     releaseSourceFile(sf);
3562     return rc;
3563   } else
3564   { return setAttrDefinition(def, att, val);
3565   }
3566 }
3567 
3568 
3569 static
3570 PRED_IMPL("$default_predicate", 2, default_predicate, PL_FA_TRANSPARENT)
3571 { Procedure p1, p2;
3572 
3573   if ( get_procedure(A1, &p1, 0, GP_FIND) &&
3574        get_procedure(A2, &p2, 0, GP_FIND) )
3575   { if ( p1->definition == p2->definition || !isDefinedProcedure(p1) )
3576       succeed;
3577   }
3578 
3579   fail;
3580 }
3581 
3582 
3583 static
3584 PRED_IMPL("$get_clause_attribute", 3, get_clause_attribute, 0)
3585 { GET_LD
3586   Clause clause;
3587   atom_t a;
3588 
3589   term_t ref   = A1;
3590   term_t att   = A2;
3591   term_t value = A3;
3592 
3593   if ( !PL_get_clref(ref, &clause) ||
3594        !PL_get_atom_ex(att, &a) )
3595     return FALSE;
3596 
3597   if ( a == ATOM_line_count )
3598   { if ( clause->line_no )
3599       return PL_unify_integer(value, clause->line_no);
3600   } else if ( a == ATOM_file )
3601   { SourceFile sf = indexToSourceFile(clause->source_no);
3602 
3603     if ( sf )
3604       return PL_unify_atom(value, sf->name);
3605   } else if ( a == ATOM_owner )
3606   { SourceFile sf = indexToSourceFile(clause->owner_no);
3607 
3608     if ( sf )
3609       return PL_unify_atom(value, sf->name);
3610   } else if ( a == ATOM_size )
3611   { size_t size = sizeofClause(clause->code_size);
3612 
3613     return PL_unify_int64(value, size);
3614   } else if ( a == ATOM_fact )
3615   { return PL_unify_atom(value,
3616 			 true(clause, UNIT_CLAUSE) ? ATOM_true
3617 						   : ATOM_false);
3618   } else if ( a == ATOM_erased )
3619   { atom_t erased;
3620 
3621     if ( visibleClause(clause, generationFrame(environment_frame)) )
3622       erased = ATOM_false;
3623     else
3624       erased = ATOM_true;
3625 
3626     return PL_unify_atom(value, erased);
3627   } else if ( a == ATOM_predicate_indicator )
3628   { if ( unify_definition(MODULE_user, value,
3629 			  clause->predicate, 0,
3630 			  GP_QUALIFY|GP_NAMEARITY) )
3631       return TRUE;
3632   } else if ( a == ATOM_module )
3633   { return PL_unify_atom(value, clauseBodyContext(clause)->name);
3634   }
3635 
3636   fail;
3637 }
3638 
3639 
3640 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3641 redefineProcedure() is called when a procedure   needs to be defined and
3642 it seems to have a definition.
3643 
3644 Sf is the `owning' source-file
3645 
3646 (*) occurs if this is actually false. This   happens if a file holding a
3647 running predicate is reloaded because the clauses cannot be wiped.
3648 (**) there is a definition, but we are reloading and we have not yet
3649 seen this predicate, so it isn't there.
3650 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
3651 
3652 int
redefineProcedure(Procedure proc,SourceFile sf,unsigned int suppress)3653 redefineProcedure(Procedure proc, SourceFile sf, unsigned int suppress)
3654 { GET_LD
3655   Definition def = proc->definition;
3656 
3657   if ( true(def, P_FOREIGN) )
3658   {			/* first call printMessage() */
3659 			/* so we can provide info about the old definition */
3660     if ( !printMessage(ATOM_warning,
3661 		       PL_FUNCTOR_CHARS, "redefined_procedure", 2,
3662 		         PL_CHARS, "foreign",
3663 		         _PL_PREDICATE_INDICATOR, proc) )
3664       return FALSE;
3665 			/* ... then abolish */
3666     abolishProcedure(proc, def->module);
3667   } else if ( false(def, P_MULTIFILE) )
3668   { ClauseRef first;
3669 
3670     def = getProcDefinition__LD(def PASS_LD);
3671     if ( !(first = hasClausesDefinition(def)) )
3672       return TRUE;				/* (*) see above */
3673 
3674     if ( first->value.clause->owner_no == sf->index )
3675     { if ( sf->reload && !reloadHasClauses(sf, proc PASS_LD) )
3676 	return TRUE;				/* (**) see above */
3677 
3678       if ( ((debugstatus.styleCheck & ~suppress) & DISCONTIGUOUS_STYLE) &&
3679 	   false(def, P_DISCONTIGUOUS) &&
3680 	   sf->current_procedure )
3681       { if ( !printMessage(ATOM_warning,
3682 			   PL_FUNCTOR_CHARS, "discontiguous", 2,
3683 			     _PL_PREDICATE_INDICATOR, proc,
3684 			     _PL_PREDICATE_INDICATOR, sf->current_procedure) )
3685 	  return FALSE;
3686       }
3687     } else if ( !hasProcedureSourceFile(sf, proc) )
3688     { if ( true(def, P_THREAD_LOCAL) )
3689 	return PL_error(NULL, 0, NULL, ERR_MODIFY_THREAD_LOCAL_PROC, proc);
3690 
3691       if ( first )
3692       { if ( !printMessage(ATOM_warning,
3693 			   PL_FUNCTOR_CHARS, "redefined_procedure", 2,
3694 			     PL_CHARS, "static",
3695 			     _PL_PREDICATE_INDICATOR, proc) )
3696 	  return FALSE;
3697       }
3698 			/* again, _after_ the printMessage() */
3699       abolishProcedure(proc, def->module);
3700     }
3701   }
3702 
3703   return TRUE;
3704 }
3705 
3706 
3707 
3708 /** copy_predicate(From:predicate_indicator, To:predicate_indicator) is det.
3709 
3710 Copy all clauses of From into To. To is created as a dynamic predicate.
3711 */
3712 
3713 static void
remoduleClause(Clause cl,Module old,Module new)3714 remoduleClause(Clause cl, Module old, Module new)
3715 { Code PC, end;
3716   int in_body = FALSE;
3717 
3718   if ( true(cl, UNIT_CLAUSE) )
3719     return;
3720 
3721   PC  = cl->codes;
3722   end = &PC[cl->code_size];
3723   for( ; PC < end; PC = stepPC(PC) )
3724   { code op = fetchop(PC);
3725 
3726     if ( in_body )
3727     { const char *ats=codeTable[op].argtype;
3728       int an;
3729 
3730       for(an=0; ats[an]; an++)
3731       { switch(ats[an])
3732 	{ case CA1_PROC:
3733 	  { Procedure op = (Procedure)PC[an+1];
3734 
3735 	    if ( op->definition->module != MODULE_system )
3736 	    { functor_t f = op->definition->functor->functor;
3737 
3738 	      PC[an+1] = (code)lookupProcedure(f, new);
3739 	    }
3740 	    break;
3741 	  }
3742 	  case CA1_MODULE:
3743 	  { if ( old == (Module)PC[an+1] )
3744 	      PC[an+1] = (code)new;
3745 	  }
3746 	}
3747       }
3748     } else if ( op == I_ENTER )
3749     { in_body = TRUE;
3750     }
3751   }
3752 }
3753 
3754 
3755 static
3756 PRED_IMPL("copy_predicate_clauses", 2, copy_predicate_clauses, PL_FA_TRANSPARENT)
3757 { PRED_LD
3758   Procedure from, to;
3759   Definition def, copy_def;
3760   ClauseRef cref;
3761   gen_t generation;
3762 
3763   if ( !get_procedure(A1, &from, 0, GP_NAMEARITY|GP_RESOLVE) )
3764     fail;
3765   if ( !isDefinedProcedure(from) )
3766     trapUndefined(getProcDefinition(from) PASS_LD);
3767   def = getProcDefinition(from);
3768   generation = global_generation();		/* take a consistent snapshot */
3769 
3770   if ( true(def, P_FOREIGN) )
3771     return PL_error(NULL, 0, NULL, ERR_PERMISSION_PROC,
3772 		    ATOM_access, ATOM_private_procedure, from);
3773 
3774   if ( !get_procedure(A2, &to, 0, GP_NAMEARITY|GP_CREATE) )
3775     return FALSE;
3776 
3777   copy_def = getProcDefinition(to);
3778   if ( true(copy_def, P_FOREIGN) )
3779     return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, to);
3780   if ( false(copy_def, P_DYNAMIC) )
3781   { if ( isDefinedProcedure(to) )
3782       return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, to);
3783     if ( !setDynamicDefinition(copy_def, TRUE) )
3784       fail;
3785   }
3786 
3787   enterDefinition(def);
3788   acquire_def(def);
3789   for( cref = def->impl.clauses.first_clause; cref; cref = cref->next )
3790   { Clause cl = cref->value.clause;
3791 
3792     if ( visibleClause(cl, generation) )
3793     { size_t size = sizeofClause(cl->code_size);
3794       Clause copy = PL_malloc_atomic(size);
3795 
3796       memcpy(copy, cl, size);
3797       copy->predicate = copy_def;
3798       if ( def->module != copy_def->module )
3799 	remoduleClause(copy, def->module, copy_def->module);
3800 #ifdef O_ATOMGC
3801 #ifdef O_DEBUG_ATOMGC
3802       forAtomsInClause(copy, register_atom_clause);
3803 #else
3804       forAtomsInClause(copy, PL_register_atom);
3805 #endif
3806 #endif
3807       assertProcedure(to, copy, CL_END PASS_LD);
3808     }
3809   }
3810   release_def(def);
3811   leaveDefinition(def);
3812 
3813   return TRUE;
3814 }
3815 
3816 
3817 #if defined(O_MAINTENANCE) || defined(O_DEBUG)
3818 
3819 		 /*******************************
3820 		 *	INTERNAL DEBUGGING	*
3821 		 *******************************/
3822 
3823 
3824 static void
listGenerations(Definition def)3825 listGenerations(Definition def)
3826 { GET_LD
3827   gen_t gen = generationFrame(environment_frame);
3828   ClauseRef cref;
3829   int i;
3830 
3831   Sdprintf("%s has %d clauses at generation %ld\n",
3832 	   predicateName(def),
3833 	   def->impl.clauses.number_of_clauses, gen);
3834 
3835   acquire_def(def);
3836   for(i=1,cref=def->impl.clauses.first_clause; cref; cref=cref->next, i++)
3837   { Clause clause = cref->value.clause;
3838 
3839     Sdprintf("%p: [%2d] %8u-%10u%s%s%s\n",
3840 	     clause, i,
3841 	     clause->generation.created,
3842 	     clause->generation.erased,
3843 	     true(clause, CL_ERASED) ? " erased" : "",
3844 	     visibleClause(clause, gen) ? " v " : " X ",
3845 	     keyName(cref->d.key));
3846   }
3847   release_def(def);
3848 
3849   listIndexGenerations(def, gen);
3850 }
3851 
3852 
3853 void
checkDefinition(Definition def)3854 checkDefinition(Definition def)
3855 { GET_LD
3856   unsigned int nc, indexed = 0;
3857   ClauseRef cref;
3858   unsigned int erased = 0;
3859   Definition old;
3860 
3861 						/* check basic clause list */
3862   acquire_def2(def, old);
3863   for(nc=0, cref = def->impl.clauses.first_clause; cref; cref=cref->next)
3864   { Clause clause = cref->value.clause;
3865 
3866     if ( false(clause, CL_ERASED) )
3867     { if ( cref->d.key )
3868 	indexed++;
3869       nc++;
3870     } else
3871     { erased++;
3872     }
3873   }
3874   release_def2(def, old);
3875 
3876   assert(nc == def->impl.clauses.number_of_clauses);
3877   assert(erased == def->impl.clauses.erased_clauses);
3878 
3879   checkClauseIndexes(def);
3880 }
3881 
3882 
3883 foreign_t
pl_check_procedure(term_t desc)3884 pl_check_procedure(term_t desc)
3885 { GET_LD
3886   Procedure proc;
3887   Definition def;
3888 
3889   if ( !get_procedure(desc, &proc, 0, GP_FIND|GP_NAMEARITY) )
3890     fail;
3891   def = getProcDefinition(proc);
3892 
3893   if ( true(def, P_FOREIGN) )
3894     fail;
3895 
3896   LOCKDEF(def);
3897   checkDefinition(def);
3898   UNLOCKDEF(def);
3899 
3900   succeed;
3901 }
3902 
3903 
3904 foreign_t
pl_list_generations(term_t desc)3905 pl_list_generations(term_t desc)
3906 { GET_LD
3907   Procedure proc;
3908   Definition def;
3909 
3910   if ( !get_procedure(desc, &proc, 0, GP_FIND|GP_NAMEARITY) )
3911     fail;
3912   def = getProcDefinition(proc);
3913 
3914   if ( true(def, P_FOREIGN) )
3915     fail;				/* permission error */
3916 
3917   listGenerations(def);
3918 
3919   succeed;
3920 }
3921 
3922 
3923 #endif /*O_MAINTENANCE*/
3924 
3925 
3926 		 /*******************************
3927 		 *      PUBLISH PREDICATES	*
3928 		 *******************************/
3929 
3930 BeginPredDefs(proc)
3931   PRED_DEF("retractall", 1, retractall, PL_FA_NONDETERMINISTIC|PL_FA_ISO)
3932   PRED_DEF("$set_predicate_attribute", 3, set_predicate_attribute,
3933 	   PL_FA_TRANSPARENT)
3934   PRED_DEF("$get_predicate_attribute", 3, get_predicate_attribute,
3935 	   PL_FA_TRANSPARENT)
3936   PRED_DEF("$default_predicate", 2, default_predicate, PL_FA_TRANSPARENT)
3937   PRED_DEF("meta_predicate", 1, meta_predicate, PL_FA_TRANSPARENT)
3938   PRED_DEF("$get_clause_attribute", 3, get_clause_attribute, 0)
3939   PRED_DEF("retract", 1, retract,
3940 	   PL_FA_TRANSPARENT|PL_FA_NONDETERMINISTIC|PL_FA_ISO)
3941   PRED_DEF("current_predicate", 1, current_predicate,
3942 	   PL_FA_TRANSPARENT|PL_FA_NONDETERMINISTIC|PL_FA_ISO)
3943   PRED_DEF("copy_predicate_clauses", 2, copy_predicate_clauses, PL_FA_TRANSPARENT)
3944   PRED_DEF("$cgc_params", 6, cgc_params, 0)
3945 EndPredDefs
3946