1 /*
2  * tclResolve.c --
3  *
4  *	Contains hooks for customized command/variable name resolution
5  *	schemes. These hooks allow extensions like [incr Tcl] to add their own
6  *	name resolution rules to the Tcl language. Rules can be applied to a
7  *	particular namespace, to the interpreter as a whole, or both.
8  *
9  * Copyright (c) 1998 Lucent Technologies, Inc.
10  *
11  * See the file "license.terms" for information on usage and redistribution of
12  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
13  */
14 
15 #include "tclInt.h"
16 
17 /*
18  * Declarations for functions local to this file:
19  */
20 
21 static void		BumpCmdRefEpochs(Namespace *nsPtr);
22 
23 /*
24  *----------------------------------------------------------------------
25  *
26  * Tcl_AddInterpResolvers --
27  *
28  *	Adds a set of command/variable resolution functions to an interpreter.
29  *	These functions are consulted when commands are resolved in
30  *	Tcl_FindCommand, and when variables are resolved in TclLookupVar and
31  *	LookupCompiledLocal. Each namespace may also have its own set of
32  *	resolution functions which take precedence over those for the
33  *	interpreter.
34  *
35  *	When a name is resolved, it is handled as follows. First, the name is
36  *	passed to the resolution functions for the namespace. If not resolved,
37  *	the name is passed to each of the resolution functions added to the
38  *	interpreter. Finally, if still not resolved, the name is handled using
39  *	the default Tcl rules for name resolution.
40  *
41  * Results:
42  *	Returns pointers to the current name resolution functions in the
43  *	cmdProcPtr, varProcPtr and compiledVarProcPtr arguments.
44  *
45  * Side effects:
46  *	If a compiledVarProc is specified, this function bumps the
47  *	compileEpoch for the interpreter, forcing all code to be recompiled.
48  *	If a cmdProc is specified, this function bumps the cmdRefEpoch in all
49  *	namespaces, forcing commands to be resolved again using the new rules.
50  *
51  *----------------------------------------------------------------------
52  */
53 
54 void
Tcl_AddInterpResolvers(Tcl_Interp * interp,const char * name,Tcl_ResolveCmdProc * cmdProc,Tcl_ResolveVarProc * varProc,Tcl_ResolveCompiledVarProc * compiledVarProc)55 Tcl_AddInterpResolvers(
56     Tcl_Interp *interp,		/* Interpreter whose name resolution rules are
57 				 * being modified. */
58     const char *name,		/* Name of this resolution scheme. */
59     Tcl_ResolveCmdProc *cmdProc,/* New function for command resolution. */
60     Tcl_ResolveVarProc *varProc,/* Function for variable resolution at
61 				 * runtime. */
62     Tcl_ResolveCompiledVarProc *compiledVarProc)
63 				/* Function for variable resolution at compile
64 				 * time. */
65 {
66     Interp *iPtr = (Interp *) interp;
67     ResolverScheme *resPtr;
68     unsigned len;
69 
70     /*
71      * Since we're adding a new name resolution scheme, we must force all code
72      * to be recompiled to use the new scheme. If there are new compiled
73      * variable resolution rules, bump the compiler epoch to invalidate
74      * compiled code. If there are new command resolution rules, bump the
75      * cmdRefEpoch in all namespaces.
76      */
77 
78     if (compiledVarProc) {
79 	iPtr->compileEpoch++;
80     }
81     if (cmdProc) {
82 	BumpCmdRefEpochs(iPtr->globalNsPtr);
83     }
84 
85     /*
86      * Look for an existing scheme with the given name. If found, then replace
87      * its rules.
88      */
89 
90     for (resPtr=iPtr->resolverPtr ; resPtr!=NULL ; resPtr=resPtr->nextPtr) {
91 	if (*name == *resPtr->name && strcmp(name, resPtr->name) == 0) {
92 	    resPtr->cmdResProc = cmdProc;
93 	    resPtr->varResProc = varProc;
94 	    resPtr->compiledVarResProc = compiledVarProc;
95 	    return;
96 	}
97     }
98 
99     /*
100      * Otherwise, this is a new scheme. Add it to the FRONT of the linked
101      * list, so that it overrides existing schemes.
102      */
103 
104     resPtr = ckalloc(sizeof(ResolverScheme));
105     len = strlen(name) + 1;
106     resPtr->name = ckalloc(len);
107     memcpy(resPtr->name, name, len);
108     resPtr->cmdResProc = cmdProc;
109     resPtr->varResProc = varProc;
110     resPtr->compiledVarResProc = compiledVarProc;
111     resPtr->nextPtr = iPtr->resolverPtr;
112     iPtr->resolverPtr = resPtr;
113 }
114 
115 /*
116  *----------------------------------------------------------------------
117  *
118  * Tcl_GetInterpResolvers --
119  *
120  *	Looks for a set of command/variable resolution functions with the
121  *	given name in an interpreter. These functions are registered by
122  *	calling Tcl_AddInterpResolvers.
123  *
124  * Results:
125  *	If the name is recognized, this function returns non-zero, along with
126  *	pointers to the name resolution functions in the Tcl_ResolverInfo
127  *	structure. If the name is not recognized, this function returns zero.
128  *
129  * Side effects:
130  *	None.
131  *
132  *----------------------------------------------------------------------
133  */
134 
135 int
Tcl_GetInterpResolvers(Tcl_Interp * interp,const char * name,Tcl_ResolverInfo * resInfoPtr)136 Tcl_GetInterpResolvers(
137     Tcl_Interp *interp,		/* Interpreter whose name resolution rules are
138 				 * being queried. */
139     const char *name,		/* Look for a scheme with this name. */
140     Tcl_ResolverInfo *resInfoPtr)
141 				/* Returns pointers to the functions, if
142 				 * found */
143 {
144     Interp *iPtr = (Interp *) interp;
145     ResolverScheme *resPtr;
146 
147     /*
148      * Look for an existing scheme with the given name. If found, then return
149      * pointers to its functions.
150      */
151 
152     for (resPtr=iPtr->resolverPtr ; resPtr!=NULL ; resPtr=resPtr->nextPtr) {
153 	if (*name == *resPtr->name && strcmp(name, resPtr->name) == 0) {
154 	    resInfoPtr->cmdResProc = resPtr->cmdResProc;
155 	    resInfoPtr->varResProc = resPtr->varResProc;
156 	    resInfoPtr->compiledVarResProc = resPtr->compiledVarResProc;
157 	    return 1;
158 	}
159     }
160 
161     return 0;
162 }
163 
164 /*
165  *----------------------------------------------------------------------
166  *
167  * Tcl_RemoveInterpResolvers --
168  *
169  *	Removes a set of command/variable resolution functions previously
170  *	added by Tcl_AddInterpResolvers. The next time a command/variable name
171  *	is resolved, these functions won't be consulted.
172  *
173  * Results:
174  *	Returns non-zero if the name was recognized and the resolution scheme
175  *	was deleted. Returns zero otherwise.
176  *
177  * Side effects:
178  *	If a scheme with a compiledVarProc was deleted, this function bumps
179  *	the compileEpoch for the interpreter, forcing all code to be
180  *	recompiled. If a scheme with a cmdProc was deleted, this function
181  *	bumps the cmdRefEpoch in all namespaces, forcing commands to be
182  *	resolved again using the new rules.
183  *
184  *----------------------------------------------------------------------
185  */
186 
187 int
Tcl_RemoveInterpResolvers(Tcl_Interp * interp,const char * name)188 Tcl_RemoveInterpResolvers(
189     Tcl_Interp *interp,		/* Interpreter whose name resolution rules are
190 				 * being modified. */
191     const char *name)		/* Name of the scheme to be removed. */
192 {
193     Interp *iPtr = (Interp *) interp;
194     ResolverScheme **prevPtrPtr, *resPtr;
195 
196     /*
197      * Look for an existing scheme with the given name.
198      */
199 
200     prevPtrPtr = &iPtr->resolverPtr;
201     for (resPtr=iPtr->resolverPtr ; resPtr!=NULL ; resPtr=resPtr->nextPtr) {
202 	if (*name == *resPtr->name && strcmp(name, resPtr->name) == 0) {
203 	    break;
204 	}
205 	prevPtrPtr = &resPtr->nextPtr;
206     }
207 
208     /*
209      * If we found the scheme, delete it.
210      */
211 
212     if (resPtr) {
213 	/*
214 	 * If we're deleting a scheme with compiled variable resolution rules,
215 	 * bump the compiler epoch to invalidate compiled code. If we're
216 	 * deleting a scheme with command resolution rules, bump the
217 	 * cmdRefEpoch in all namespaces.
218 	 */
219 
220 	if (resPtr->compiledVarResProc) {
221 	    iPtr->compileEpoch++;
222 	}
223 	if (resPtr->cmdResProc) {
224 	    BumpCmdRefEpochs(iPtr->globalNsPtr);
225 	}
226 
227 	*prevPtrPtr = resPtr->nextPtr;
228 	ckfree(resPtr->name);
229 	ckfree(resPtr);
230 
231 	return 1;
232     }
233     return 0;
234 }
235 
236 /*
237  *----------------------------------------------------------------------
238  *
239  * BumpCmdRefEpochs --
240  *
241  *	This function is used to bump the cmdRefEpoch counters in the
242  *	specified namespace and all of its child namespaces. It is used
243  *	whenever name resolution schemes are added/removed from an
244  *	interpreter, to invalidate all command references.
245  *
246  * Results:
247  *	None.
248  *
249  * Side effects:
250  *	Bumps the cmdRefEpoch in the specified namespace and its children,
251  *	recursively.
252  *
253  *----------------------------------------------------------------------
254  */
255 
256 static void
BumpCmdRefEpochs(Namespace * nsPtr)257 BumpCmdRefEpochs(
258     Namespace *nsPtr)		/* Namespace being modified. */
259 {
260     Tcl_HashEntry *entry;
261     Tcl_HashSearch search;
262 
263     nsPtr->cmdRefEpoch++;
264 
265 #ifndef BREAK_NAMESPACE_COMPAT
266     for (entry = Tcl_FirstHashEntry(&nsPtr->childTable, &search);
267 	    entry != NULL; entry = Tcl_NextHashEntry(&search)) {
268 	Namespace *childNsPtr = Tcl_GetHashValue(entry);
269 
270 	BumpCmdRefEpochs(childNsPtr);
271     }
272 #else
273     if (nsPtr->childTablePtr != NULL) {
274 	for (entry = Tcl_FirstHashEntry(nsPtr->childTablePtr, &search);
275 		entry != NULL; entry = Tcl_NextHashEntry(&search)) {
276 	    Namespace *childNsPtr = Tcl_GetHashValue(entry);
277 
278 	    BumpCmdRefEpochs(childNsPtr);
279 	}
280     }
281 #endif
282     TclInvalidateNsPath(nsPtr);
283 }
284 
285 /*
286  *----------------------------------------------------------------------
287  *
288  * Tcl_SetNamespaceResolvers --
289  *
290  *	Sets the command/variable resolution functions for a namespace,
291  *	thereby changing the way that command/variable names are interpreted.
292  *	This allows extension writers to support different name resolution
293  *	schemes, such as those for object-oriented packages.
294  *
295  *	Command resolution is handled by a function of the following type:
296  *
297  *	  typedef int (Tcl_ResolveCmdProc)(Tcl_Interp *interp,
298  *		  const char *name, Tcl_Namespace *context,
299  *		  int flags, Tcl_Command *rPtr);
300  *
301  *	Whenever a command is executed or Tcl_FindCommand is invoked within
302  *	the namespace, this function is called to resolve the command name. If
303  *	this function is able to resolve the name, it should return the status
304  *	code TCL_OK, along with the corresponding Tcl_Command in the rPtr
305  *	argument. Otherwise, the function can return TCL_CONTINUE, and the
306  *	command will be treated under the usual name resolution rules. Or, it
307  *	can return TCL_ERROR, and the command will be considered invalid.
308  *
309  *	Variable resolution is handled by two functions. The first is called
310  *	whenever a variable needs to be resolved at compile time:
311  *
312  *	  typedef int (Tcl_ResolveCompiledVarProc)(Tcl_Interp *interp,
313  *		  const char *name, Tcl_Namespace *context,
314  *		  Tcl_ResolvedVarInfo *rPtr);
315  *
316  *	If this function is able to resolve the name, it should return the
317  *	status code TCL_OK, along with variable resolution info in the rPtr
318  *	argument; this info will be used to set up compiled locals in the call
319  *	frame at runtime. The function may also return TCL_CONTINUE, and the
320  *	variable will be treated under the usual name resolution rules. Or, it
321  *	can return TCL_ERROR, and the variable will be considered invalid.
322  *
323  *	Another function is used whenever a variable needs to be resolved at
324  *	runtime but it is not recognized as a compiled local. (For example,
325  *	the variable may be requested via Tcl_FindNamespaceVar.) This function
326  *	has the following type:
327  *
328  *	  typedef int (Tcl_ResolveVarProc)(Tcl_Interp *interp,
329  *		  const char *name, Tcl_Namespace *context,
330  *		  int flags, Tcl_Var *rPtr);
331  *
332  *	This function is quite similar to the compile-time version. It returns
333  *	the same status codes, but if variable resolution succeeds, this
334  *	function returns a Tcl_Var directly via the rPtr argument.
335  *
336  * Results:
337  *	Nothing.
338  *
339  * Side effects:
340  *	Bumps the command epoch counter for the namespace, invalidating all
341  *	command references in that namespace. Also bumps the resolver epoch
342  *	counter for the namespace, forcing all code in the namespace to be
343  *	recompiled.
344  *
345  *----------------------------------------------------------------------
346  */
347 
348 void
Tcl_SetNamespaceResolvers(Tcl_Namespace * namespacePtr,Tcl_ResolveCmdProc * cmdProc,Tcl_ResolveVarProc * varProc,Tcl_ResolveCompiledVarProc * compiledVarProc)349 Tcl_SetNamespaceResolvers(
350     Tcl_Namespace *namespacePtr,/* Namespace whose resolution rules are being
351 				 * modified. */
352     Tcl_ResolveCmdProc *cmdProc,/* Function for command resolution */
353     Tcl_ResolveVarProc *varProc,/* Function for variable resolution at
354 				 * run-time */
355     Tcl_ResolveCompiledVarProc *compiledVarProc)
356 				/* Function for variable resolution at compile
357 				 * time. */
358 {
359     Namespace *nsPtr = (Namespace *) namespacePtr;
360 
361     /*
362      * Plug in the new command resolver, and bump the epoch counters so that
363      * all code will have to be recompiled and all commands will have to be
364      * resolved again using the new policy.
365      */
366 
367     nsPtr->cmdResProc = cmdProc;
368     nsPtr->varResProc = varProc;
369     nsPtr->compiledVarResProc = compiledVarProc;
370 
371     nsPtr->cmdRefEpoch++;
372     nsPtr->resolverEpoch++;
373     TclInvalidateNsPath(nsPtr);
374 }
375 
376 /*
377  *----------------------------------------------------------------------
378  *
379  * Tcl_GetNamespaceResolvers --
380  *
381  *	Returns the current command/variable resolution functions for a
382  *	namespace. By default, these functions are NULL. New functions can be
383  *	installed by calling Tcl_SetNamespaceResolvers, to provide new name
384  *	resolution rules.
385  *
386  * Results:
387  *	Returns non-zero if any name resolution functions have been assigned
388  *	to this namespace; also returns pointers to the functions in the
389  *	Tcl_ResolverInfo structure. Returns zero otherwise.
390  *
391  * Side effects:
392  *	None.
393  *
394  *----------------------------------------------------------------------
395  */
396 
397 int
Tcl_GetNamespaceResolvers(Tcl_Namespace * namespacePtr,Tcl_ResolverInfo * resInfoPtr)398 Tcl_GetNamespaceResolvers(
399     Tcl_Namespace *namespacePtr,/* Namespace whose resolution rules are being
400 				 * modified. */
401     Tcl_ResolverInfo *resInfoPtr)
402 				/* Returns: pointers for all name resolution
403 				 * functions assigned to this namespace. */
404 {
405     Namespace *nsPtr = (Namespace *) namespacePtr;
406 
407     resInfoPtr->cmdResProc = nsPtr->cmdResProc;
408     resInfoPtr->varResProc = nsPtr->varResProc;
409     resInfoPtr->compiledVarResProc = nsPtr->compiledVarResProc;
410 
411     if (nsPtr->cmdResProc != NULL || nsPtr->varResProc != NULL ||
412 	    nsPtr->compiledVarResProc != NULL) {
413 	return 1;
414     }
415     return 0;
416 }
417 
418 /*
419  * Local Variables:
420  * mode: c
421  * c-basic-offset: 4
422  * fill-column: 78
423  * End:
424  */
425