1 /*
2 
3     Copyright (C) 1991-2003 The National Gallery
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 
19  */
20 
21 /*
22 
23     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
24 
25  */
26 
27 /*
28 #define DEBUG
29  */
30 
31 /* Just show secrets we added
32 #define DEBUG_ADD
33  */
34 
35 #include "ip.h"
36 
37 /* build secret sets for exprs
38 
39 cases:
40 
41 	fred a
42 		= jim 12
43 	{
44 		jim b = a + b;
45 	}
46 
47 jim refers to a parameter in an enclosing scope ... we add extra secret
48 parameters to jim like this:
49 
50 	fred a
51 		= jim [a] 12
52 	{
53 		jim [a] b = a + b;
54 	}
55 
56 across class boundaries:
57 
58 	fred a
59 		= jim
60 	{
61 		jim = class {
62 			b = a;
63 		}
64 	}
65 
66 now fred.jim.b refers to fred.a ... a needs to be added to the secrets on
67 jim's constructor like this:
68 
69 	fred a
70 		= jim [a]
71 	{
72 		jim [a] = class {
73 			b = a;
74 		}
75 	}
76 
77 if the secret is a class member, pass "this" instead and the inner thing then
78 gets from that
79 
80 	fred a = class
81 	{
82 		jim [fred.this] b = fred.this.a + b;
83 	}
84 
85 if the inner thing is also a class, need to get in two stages ... first get
86 the right this, then get from that
87 
88 	fred a = class
89 	{
90 		jim [fred.this] = class {
91 			b = jim.this.fred.this.a;
92 		}
93 	}
94 
95 need to work for any sort of nesting of functions and classes
96 
97 	fred = class {
98 		b = c
99 		{
100 			c = this;
101 		}
102 	}
103 
104 not just params ... can involve locals of parents
105 
106  */
107 
108 /* Add a secret. Set changed if we make a change.
109  */
110 static void *
secret_add(Symbol * secret,Compile * compile,gboolean * changed)111 secret_add( Symbol *secret, Compile *compile, gboolean *changed )
112 {
113 	Compile *parent = compile_get_parent( compile );
114 
115 #ifdef DEBUG
116 	printf( "secret_add: considering secret " );
117 	symbol_name_print( secret );
118 	printf( "for " );
119 	compile_name_print( compile );
120 	printf( " ...\n" );
121 #endif /*DEBUG*/
122 
123 	/* If expr is a class, don't add our own this.
124 	 */
125 	if( is_class( compile ) && secret == compile->this )
126 		return( NULL );
127 
128 	/* If expr already has secret as a param or secret, don't add again.
129 	 */
130 	if( g_slist_find( compile->secret, secret ) ||
131 		g_slist_find( compile->param, secret ) )
132 		return( NULL );
133 
134 	/* If secret is a member (param, func, whatever), add secret's
135 	 * enclosing "this" instead ... expr can then get secret from there.
136 	 * Unless the secret is already a "this", of course.
137 	 */
138 	if( is_class( COMPILE( ICONTAINER( secret )->parent ) ) &&
139 		!is_this( secret ) )
140 		secret = COMPILE( ICONTAINER( secret )->parent )->this;
141 
142 	/* If compile is a member (and not a class itself), add the secret to
143 	 * compile's constructor instead ... compile can get from "this".
144 	 */
145 	if( is_class( parent ) && secret != parent->this &&
146 		!is_class( compile ) )
147 		compile = parent;
148 
149 	/* We may have moved stuff about ... check for dupes again.
150 	 */
151 	if( g_slist_find( compile->secret, secret ) ||
152 		g_slist_find( compile->param, secret ) )
153 		return( NULL );
154 
155 #ifdef DEBUG_ADD
156 	printf( "secret_add: adding secret " );
157 	symbol_name_print( secret );
158 	printf( "to " );
159 	compile_name_print( compile );
160 	printf( "\n" );
161 #endif /*DEBUG_ADD*/
162 
163 	compile->secret = g_slist_append( compile->secret, secret );
164 	compile->nsecret += 1;
165 	*changed = TRUE;
166 
167 	return( NULL );
168 }
169 
170 /* If compile is a member, then secret lists are easy ... just use "this".
171  */
172 static void *
secret_set_class(Compile * compile)173 secret_set_class( Compile *compile )
174 {
175 	if( is_class( compile_get_parent( compile ) ) ) {
176 		Compile *parent = compile_get_parent( compile );
177 		Symbol *ths = parent->this;
178 		gboolean changed;
179 
180 		if( secret_add( ths, compile, &changed ) )
181 			return( (void *) ths );
182 	}
183 
184 	return( NULL );
185 }
186 
187 /* child is one of compile's children ...  is it reference to a parameter
188  * in an enclosing scope? If yes, we've found a secret!
189  */
190 static void *
secret_is_nonlocal(Symbol * child,Compile * compile)191 secret_is_nonlocal( Symbol *child, Compile *compile )
192 {
193 	gboolean changed;
194 
195 	if( child->type == SYM_PARAM &&
196 		COMPILE( ICONTAINER( child )->parent ) != compile ) {
197 		if( secret_add( child, compile, &changed ) )
198 			return( child );
199 	}
200 
201 	return( NULL );
202 }
203 
204 /* Make initial secret list ... if this is a member/function, search for
205  * references to symbols in an enclosing scope.
206  */
207 static void *
secret_find_nonlocal(Compile * compile)208 secret_find_nonlocal( Compile *compile )
209 {
210 	/* Look for any secrets.
211 	 */
212 	if( slist_map( compile->children,
213 		(SListMapFn) secret_is_nonlocal, compile ) )
214 		return( compile );
215 
216 	return( NULL );
217 }
218 
219 /* Does child have any secrets that compile does not?
220  */
221 static void *
secret_test(Symbol * child,Compile * compile,gboolean * changed)222 secret_test( Symbol *child, Compile *compile, gboolean *changed )
223 {
224 	/* If this is a parameter or a zombie, nothing to do.
225 	 */
226 	if( !is_value( child ) )
227 		return( NULL );
228 
229 	if( child->expr->compile )
230 		if( slist_map2( child->expr->compile->secret,
231 			(SListMap2Fn) secret_add, compile, changed ) )
232 			return( child );
233 
234 	return( NULL );
235 }
236 
237 /* Close secret list ... if sym has a child with a secret sym does not have,
238  * sym needs child's secret too.
239  */
240 static void *
secret_close(Compile * compile,gboolean * changed)241 secret_close( Compile *compile, gboolean *changed )
242 {
243 	if( is_class( compile ) ) {
244 		/* For classes, need to consider all of their locals. Any
245 		 * secrets our locals have, we need too.
246 		 */
247 		if( icontainer_map( ICONTAINER( compile ),
248 			(icontainer_map_fn) secret_test, compile, changed ) )
249 			return( compile );
250 	}
251 	else {
252 		/* Look at our immediate children, any of them have secrets
253 		 * we don't?
254 		 */
255 		if( slist_map2( compile->children,
256 			(SListMap2Fn) secret_test, compile, changed ) )
257 			return( compile );
258 	}
259 
260 	return( NULL );
261 }
262 
263 #ifdef DEBUG
264 /* Sub-fn of below ... add param as a secret to sym.
265  */
266 static void *
secret_all_add(Compile * compile,Symbol * param)267 secret_all_add( Compile *compile, Symbol *param )
268 {
269 	gboolean changed;
270 
271 	return( secret_add( param, compile, &changed ) );
272 }
273 
274 /* Sub-fn of below ... add param as a secret for all of compile's locals.
275  */
276 static void *
secret_all_sym(Symbol * param,Compile * compile)277 secret_all_sym( Symbol *param, Compile *compile )
278 {
279 	return( compile_map_all( compile,
280 		(map_compile_fn) secret_all_add, param ) );
281 }
282 
283 /* Make syms params and secrets secrets for all sub-syms. Only handy for
284  * debugging.
285  */
286 static void *
secret_all(Compile * compile)287 secret_all( Compile *compile )
288 {
289 	if( slist_map( compile->param,
290 		(SListMapFn) secret_all_sym, compile ) ||
291 		slist_map( compile->secret,
292 			(SListMapFn) secret_all_sym, compile ) )
293 		return( compile );
294 
295 	return( NULL );
296 }
297 #endif /*DEBUG*/
298 
299 /* Make secret param lists for compile and all of it's sub-defs.
300  */
301 void
secret_build(Compile * compile)302 secret_build( Compile *compile )
303 {
304 	gboolean changed;
305 
306 #ifdef DEBUG_ADD
307 	printf( "secret_build: " );
308 	symbol_name_print( compile->sym );
309 	printf( "\n" );
310 #endif /*DEBUG_ADD*/
311 
312 	/* Look for class definitions ... all members of a
313 	 * class should take a single secret, their "this" parameter.
314 	 * When they in turn call their locals, they can get the
315 	 * secrets they need from "this".
316 	 */
317 	(void) compile_map_all( compile,
318 		(map_compile_fn) secret_set_class, NULL );
319 
320 	/* Now look for non-member functions ... if they reference
321 	 * parameters in an enclosing scope, add that parameter to
322 	 * the secret list.
323 	 */
324 	(void) compile_map_all( compile,
325 		(map_compile_fn) secret_find_nonlocal, NULL );
326 
327 	/* Now take the closure of the secret lists ... have to
328 	 * fix() this to get limit of secret_close().
329 	 */
330 	do {
331 		changed = FALSE;
332 		(void) compile_map_all( compile,
333 			(map_compile_fn) secret_close, &changed );
334 	} while( changed );
335 }
336 
337