1 /* Class functions ... really part of heap.c, but split out here to make it
2  * more manageable.
3  */
4 
5 /*
6 
7     Copyright (C) 1991-2003 The National Gallery
8 
9     This program is free software; you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation; either version 2 of the License, or
12     (at your option) any later version.
13 
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License along
20     with this program; if not, write to the Free Software Foundation, Inc.,
21     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 
23  */
24 
25 /*
26 
27     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
28 
29  */
30 
31 #include "ip.h"
32 
33 /*
34 #define DEBUG_MEMBER
35 #define DEBUG_VERBOSE
36 #define DEBUG
37 #define DEBUG_BUILD
38  */
39 
40 static gboolean
class_is_class(PElement * instance)41 class_is_class( PElement *instance )
42 {
43 	if( !PEISCLASS( instance ) ) {
44 		char txt[50];
45 		VipsBuf buf = VIPS_BUF_STATIC( txt );
46 
47 		if( !itext_value( reduce_context, &buf, instance ) )
48 			return( FALSE );
49 		error_top( _( "Bad argument." ) );
50 		error_sub( _( "Object %s is not a class." ),
51 			vips_buf_all( &buf ) );
52 
53 		return( FALSE );
54 	}
55 
56 	return( TRUE );
57 }
58 
59 Compile *
class_get_compile(PElement * instance)60 class_get_compile( PElement *instance )
61 {
62 	if( !class_is_class( instance ) )
63 		return( NULL );
64 
65 	return( PEGETCLASSCOMPILE( instance ) );
66 }
67 
68 /* Look up "super" in a class ... try to do it quickly.
69  */
70 gboolean
class_get_super(PElement * instance,PElement * out)71 class_get_super( PElement *instance, PElement *out )
72 {
73 	Compile *compile;
74 
75 	if( !(compile = class_get_compile( instance )) )
76 		return( FALSE );
77 	g_assert( compile->super );
78 
79 	return( class_get_symbol( instance, compile->super, out ) );
80 }
81 
82 void *
class_map(PElement * instance,class_map_fn fn,void * a,void * b)83 class_map( PElement *instance, class_map_fn fn, void *a, void *b )
84 {
85 	PElement member;
86 	HeapNode *p;
87 
88 	if( !PEISCLASS( instance ) )
89 		return( NULL );
90 
91 	/* Loop over the instance member list.
92 	 */
93 	PEGETCLASSMEMBER( &member, instance );
94 	if( !PEISELIST( &member ) )
95 		for( p = PEGETVAL( &member ); p; p = GETRIGHT( p ) ) {
96 			HeapNode *hn;
97 			PElement s, v;
98 			Symbol *sym;
99 			void *result;
100 
101 			/* Get the sym/value pair, get the sym.
102 			 */
103 			hn = GETLEFT( p );
104 			PEPOINTLEFT( hn, &s );
105 			PEPOINTRIGHT( hn, &v );
106 			sym = PEGETSYMREF( &s );
107 
108 			if( (result = fn( sym, &v, a, b )) )
109 				return( result );
110 
111 		}
112 
113 	return( NULL );
114 }
115 
116 /* Look up a member in a class instance by name. If lookup fails in this
117  * instance, try the superclass. Don't search secrets. Point sym and value
118  * at the symbol we found and its value. sym can be NULL for no result
119  * required.
120  */
121 gboolean
class_get_member(PElement * instance,const char * name,Symbol ** sym_out,PElement * out)122 class_get_member( PElement *instance, const char *name,
123 	Symbol **sym_out, PElement *out )
124 {
125 	PElement member;
126 	PElement super;
127 	HeapNode *p;
128 
129 #ifdef DEBUG_MEMBER
130 	printf( "class_get_member: looking up \"%s\" in class ", name );
131 	pgraph( instance );
132 #endif /*DEBUG_MEMBER*/
133 
134 	if( !class_is_class( instance ) )
135 		return( FALSE );
136 
137 	/* Search this instance member list.
138 	 */
139 	PEGETCLASSMEMBER( &member, instance );
140 	if( !PEISELIST( &member ) )
141 		for( p = PEGETVAL( &member ); p; p = GETRIGHT( p ) ) {
142 			HeapNode *hn;
143 			PElement s;
144 			Symbol *sym;
145 
146 			/* Get the sym/value pair, get the sym.
147 			 */
148 			hn = GETLEFT( p );
149 			PEPOINTLEFT( hn, &s );
150 
151 			/* Match?
152 			 */
153 			sym = PEGETSYMREF( &s );
154 			if( strcmp( IOBJECT( sym )->name, name ) == 0 ) {
155 				/* Found!
156 				 */
157 				PEPOINTRIGHT( hn, out );
158 				if( sym_out )
159 					*sym_out = sym;
160 
161 #ifdef DEBUG_MEMBER
162 				printf( "class_get_member: found: " );
163 				pgraph( out );
164 #endif /*DEBUG_MEMBER*/
165 
166 				return( TRUE );
167 			}
168 		}
169 
170 	/* Nope ... try the superclass.
171 	 */
172 	if( !class_get_super( instance, &super ) || !PEISELIST( &super ) ) {
173 		/*
174 
175 			FIXME ... gcc 2.95.2 gets this wrong, tries to
176 				  eliminate the tail recursion with -O2
177 				  and makes bad code
178 			      ... guess how long that took to find
179 			      ... put this back at some point
180 
181 		return( class_get_member( &super, name, sym_out, value ) );
182 		 */
183 		gboolean result = class_get_member( &super, name,
184 			sym_out, out );
185 
186 		return( result );
187 	}
188 
189 	error_top( _( "Member not found." ) );
190 	error_sub( _( "Member \"%s\" not found in class \"%s\"." ),
191 		name, IOBJECT( PEGETCLASSCOMPILE( instance )->sym )->name );
192 
193 	return( FALSE );
194 }
195 
196 /* Look up a symbol in a class. Write to out, or FALSE for not found. Look up
197  * by symbol pointer. Search secrets as well. Try the superclass if lookup
198  * fails.
199  */
200 gboolean
class_get_symbol(PElement * instance,Symbol * sym,PElement * out)201 class_get_symbol( PElement *instance, Symbol *sym, PElement *out )
202 {
203 	HeapNode *p;
204 	PElement secret;
205 	PElement super;
206 
207 #ifdef DEBUG_MEMBER
208 	printf( "class_get_symbol: looking up " );
209 	symbol_name_print( sym );
210 	printf( "in class " );
211 	pgraph( instance );
212 #endif /*DEBUG_MEMBER*/
213 
214 	if( !class_is_class( instance ) )
215 		return( FALSE );
216 
217 	PEGETCLASSSECRET( &secret, instance );
218 	if( PEISNODE( &secret ) )
219 		for( p = PEGETVAL( &secret ); p; p = GETRIGHT( p ) ) {
220 			PElement s;
221 			HeapNode *hn;
222 
223 			/* Get the sym/value pair, get the sym.
224 			 */
225 			hn = GETLEFT( p );
226 			PEPOINTLEFT( hn, &s );
227 
228 			/* Match?
229 			 */
230 			if( PEGETSYMREF( &s ) == sym ) {
231 				/* Found!
232 				 */
233 				PEPOINTRIGHT( hn, out );
234 
235 #ifdef DEBUG_MEMBER
236 				printf( "class_get_symbol: found: " );
237 				pgraph( out );
238 #endif /*DEBUG_MEMBER*/
239 
240 				return( TRUE );
241 			}
242 		}
243 
244 	/* Nope ... try the superclass.
245 	 */
246 	if( !class_get_super( instance, &super ) || !PEISELIST( &super ) ) {
247 		/*
248 
249 			FIXME ... gcc 2.95.2 gets this wrong, tries to
250 				  eliminate the tail recursion with -O2
251 				  and makes bad code
252 			      ... guess how long that took to find
253 			      ... put this back at some point
254 
255 		return( class_get_member( &super, name, out ) );
256 		 */
257 		gboolean result = class_get_symbol( &super, sym, out );
258 
259 		return( result );
260 	}
261 
262 	return( FALSE );
263 }
264 
265 /* Search back up the inheritance tree for an exact instance of this
266  * class.
267  */
268 gboolean
class_get_exact(PElement * instance,const char * name,PElement * out)269 class_get_exact( PElement *instance, const char *name, PElement *out )
270 {
271 	PElement pe;
272 
273 	pe = *instance;
274 	while( !reduce_is_instanceof_exact( reduce_context, name, &pe ) ) {
275 		if( !class_get_super( &pe, &pe ) || PEISELIST( &pe ) )
276 			return( FALSE );
277 	}
278 
279 	*out = pe;
280 
281 	return( TRUE );
282 }
283 
284 /* Stuff we need for class build.
285  */
286 typedef struct {
287 	Heap *heap;		/* Heap to build on */
288 	Symbol *sym;		/* Sym we are local to */
289 	PElement *arg;		/* Args to constructor */
290 	PElement *this;		/* Base of instance we are building */
291 	int i;			/* Index in arg list */
292 	Compile *compile;	/* Compile for our class */
293 } ClassBuildInfo;
294 
295 /* Member sym of class pbi->sym needs secret as an argument ... add it!
296  */
297 static gboolean
class_member_secret(ClassBuildInfo * pbi,Symbol * sym,GSList * secret,PElement * out)298 class_member_secret( ClassBuildInfo *pbi,
299 	Symbol *sym, GSList *secret, PElement *out )
300 {
301 	Symbol *ssym;
302 	Heap *heap = pbi->heap;
303 	HeapNode *apl;
304 
305 	if( !secret )
306 		return( TRUE );
307 	ssym = SYMBOL( secret->data );
308 
309 	/* Make function application for this member.
310 	 */
311 	if( NEWNODE( heap, apl ) )
312 		return( FALSE );
313 	apl->type = TAG_APPL;
314 	PEPUTLEFT( apl, out );
315 
316 	/* Is the secret "this"? Easy.
317 	 */
318 	if( ssym == pbi->sym->expr->compile->this ) {
319 		PEPUTRIGHT( apl, pbi->this );
320 	}
321 	else {
322 		/* Look up ssym in pbi->sym's secrets ... should be there
323 		 * somewhere. Use it's index to find the pbi->arg[] we need.
324 		 */
325 		int pos = g_slist_index(
326 			pbi->sym->expr->compile->secret, ssym );
327 
328 		/* FIXME ... may not be if we've regenerated one of these
329 		 * stupid things :-( change this so we always go through
330 		 * 'this'.
331 		 */
332 		if( pos < 0 || pos >= pbi->sym->expr->compile->nsecret ) {
333 			error_top( _( "No such secret." ) );
334 			error_sub( _( "Editing local classes which reference "
335 				"non-local objects is a bit broken at the "
336 				"moment :-(" ) );
337 			return( FALSE );
338 		}
339 
340 		PEPUTRIGHT( apl,
341 			&pbi->arg[pbi->sym->expr->compile->nsecret - pos - 1] );
342 	}
343 
344 	PEPUTP( out, ELEMENT_NODE, apl );
345 
346 #ifdef DEBUG_VERBOSE
347 {
348 	PElement p1;
349 	char txt[1024];
350 	VipsBuf buf = VIPS_BUF_STATIC( txt );
351 
352 	PEPOINTRIGHT( apl, &p1 );
353 	graph_pelement( pbi->heap, &buf, &p1, TRUE );
354 	printf( "class_member_secret: secret arg " );
355 	symbol_name_print( ssym );
356 	printf( "to member " );
357 	symbol_name_print( sym );
358 	printf( "= %s\n", vips_buf_all( &buf ) );
359 }
360 #endif /*DEBUG_VERBOSE*/
361 
362 	return( class_member_secret( pbi, sym, secret->next, out ) );
363 }
364 
365 /* Add a member to a class.
366  */
367 static void *
add_class_member(Symbol * sym,ClassBuildInfo * pbi,PElement * out)368 add_class_member( Symbol *sym, ClassBuildInfo *pbi, PElement *out )
369 {
370 	Heap *heap = pbi->heap;
371 	HeapNode *base, *sv;
372 	PElement v;
373 
374 	/* Is this something that should be part of a class.
375 	 */
376 	if( sym->type != SYM_VALUE )
377 		return( NULL );
378 
379 	/* Make new class-local-list element for this local.
380 	 */
381 	if( NEWNODE( heap, base ) )
382 		return( sym );
383 	base->type = TAG_CONS;
384 	PPUTLEFT( base, ELEMENT_ELIST, NULL );
385 	PEPUTRIGHT( base, out );
386 	PEPUTP( out, ELEMENT_NODE, base );
387 
388 	/* Make sym/value pair for this local.
389 	 */
390 	if( NEWNODE( heap, sv ) )
391 		return( sym );
392 	sv->type = TAG_CONS;
393 	PPUT( sv, ELEMENT_SYMREF, sym, ELEMENT_SYMBOL, sym );
394 	PPUTLEFT( base, ELEMENT_NODE, sv );
395 
396 	/* Build value ... apply args to the symbol.
397 	 */
398 	PEPOINTRIGHT( sv, &v );
399 	if( !class_member_secret( pbi, sym, sym->expr->compile->secret, &v ) )
400 		return( sym );
401 
402 #ifdef DEBUG_VERBOSE
403 {
404 	char txt[1024];
405 	VipsBuf buf = VIPS_BUF_STATIC( txt );
406 
407 	graph_pelement( heap, &buf, &v, TRUE );
408 	printf( "add_class_member: member \"%s\" of class \"%s\" = %s\n",
409 		IOBJECT( sym )->name, IOBJECT( pbi->sym )->name,
410 		vips_buf_all( &buf ) );
411 }
412 #endif /*DEBUG_VERBOSE*/
413 
414 	return( NULL );
415 }
416 
417 /* Add a symbol/value pair to a class.
418  */
419 static gboolean
add_class_svpair(ClassBuildInfo * pbi,Symbol * sym,PElement * val,PElement * out)420 add_class_svpair( ClassBuildInfo *pbi,
421 	Symbol *sym, PElement *val, PElement *out )
422 {
423 	Heap *heap = pbi->heap;
424 	HeapNode *base, *sv;
425 
426 #ifdef DEBUG_VERBOSE
427 {
428 	char txt[1024];
429 	VipsBuf buf = VIPS_BUF_STATIC( txt );
430 
431 	graph_pelement( heap, &buf, val, TRUE );
432 	printf( "add_class_svpair: adding parameter \"%s\" to class "
433 		"\"%s\" = %s\n",
434 		IOBJECT( sym )->name, IOBJECT( pbi->sym )->name,
435 		vips_buf_all( &buf ) );
436 }
437 #endif /*DEBUG_VERBOSE*/
438 
439 	/* Make new class-local-list element for this parameter.
440 	 */
441 	if( NEWNODE( heap, base ) )
442 		return( FALSE );
443 	base->type = TAG_CONS;
444 	PPUTLEFT( base, ELEMENT_ELIST, NULL );
445 	PEPUTRIGHT( base, out );
446 	PEPUTP( out, ELEMENT_NODE, base );
447 
448 	/* Make sym/value pair for this parameter.
449 	 */
450 	if( NEWNODE( heap, sv ) )
451 		return( FALSE );
452 	sv->type = TAG_CONS;
453 	PPUTLEFT( sv, ELEMENT_SYMREF, sym )
454 	PEPUTRIGHT( sv, val );
455 	PPUTLEFT( base, ELEMENT_NODE, sv );
456 
457 	return( TRUE );
458 }
459 
460 /* Add a parameter (secret or real) to a class.
461  */
462 static void *
add_class_parameter(Symbol * sym,ClassBuildInfo * pbi,PElement * out)463 add_class_parameter( Symbol *sym, ClassBuildInfo *pbi, PElement *out )
464 {
465 	/* Add this symbol/value pair.
466 	 */
467 	if( !add_class_svpair( pbi, sym, &pbi->arg[pbi->i], out ) )
468 		return( sym );
469 
470 	/* Move arg index on.
471 	 */
472 	pbi->i += 1;
473 
474 	return( NULL );
475 }
476 
477 /* Add the name member ... build the name string carefully.
478  */
479 static void *
class_new_single_name(Heap * heap,PElement * pe,ClassBuildInfo * pbi,PElement * instance)480 class_new_single_name( Heap *heap, PElement *pe,
481 	ClassBuildInfo *pbi, PElement *instance )
482 {
483 	Symbol *snm = compile_lookup( pbi->compile, MEMBER_NAME );
484 	char txt[256];
485 	VipsBuf buf = VIPS_BUF_STATIC( txt );
486 
487 	/* Make class name string.
488 	 */
489 	symbol_qualified_name( pbi->sym, &buf );
490 	PEPUTP( pe, ELEMENT_ELIST, NULL );
491 	if( !heap_managedstring_new( heap, vips_buf_all( &buf ), pe ) )
492 		return( heap );
493 
494 	/* Add as a member.
495 	 */
496 	if( !add_class_svpair( pbi, snm, pe, instance ) )
497 		return( heap );
498 
499 	return( NULL );
500 }
501 
502 /* Make a single level class instance ... fn below then loops over a class
503  * hierarchy with this.
504  */
505 static gboolean
class_new_single(Heap * heap,Compile * compile,PElement * arg,PElement * this,PElement * out)506 class_new_single( Heap *heap,
507 	Compile *compile, PElement *arg, PElement *this, PElement *out )
508 {
509 	Symbol *sym = compile->sym;
510 	Symbol *sths = compile->this;
511 
512 	HeapNode *base, *sm;
513 	PElement p1;
514 	ClassBuildInfo pbi;
515 
516 #ifdef DEBUG
517 {
518 	int i;
519 
520 	printf( "class_new_single: starting for " );
521 	symbol_name_print( sym );
522 	printf( "%d secrets, %d params\n",
523 		compile->nsecret, compile->nparam );
524 
525 	for( i = 0; i < compile->nsecret; i++ ) {
526 		char txt[256];
527 		VipsBuf buf = VIPS_BUF_STATIC( txt );
528 
529 		graph_pelement( heap, &buf, &arg[i], TRUE );
530 		printf( "\tsecret %2d = %s\n", i, vips_buf_all( &buf ) );
531 	}
532 
533 	for( i = 0; i < compile->nparam; i++ ) {
534 		char txt[256];
535 		VipsBuf buf = VIPS_BUF_STATIC( txt );
536 
537 		graph_pelement( heap, &buf, &arg[i + compile->nsecret], TRUE );
538 		printf( "\targ %2d = %s\n", i, vips_buf_all( &buf ) );
539 	}
540 }
541 #endif /*DEBUG*/
542 
543 	/* Make class base.
544 	 */
545 	if( NEWNODE( heap, base ) )
546 		return( FALSE );
547 	base->type = TAG_CLASS;
548 	PPUT( base, ELEMENT_COMPILEREF, compile, ELEMENT_ELIST, NULL );
549 	PEPUTP( out, ELEMENT_NODE, base );
550 
551 	/* Make node for holding secrets and members.
552 	 */
553 	if( NEWNODE( heap, sm ) )
554 		return( FALSE );
555 	sm->type = TAG_CONS;
556 	PPUT( sm, ELEMENT_ELIST, NULL, ELEMENT_ELIST, NULL );
557 	PPUTRIGHT( base, ELEMENT_NODE, sm );
558 
559 	/* Build list of members.
560 	 */
561 	pbi.heap = heap;
562 	pbi.sym = sym;
563 	pbi.arg = arg;
564 	pbi.this = this;
565 	pbi.compile = compile;
566 	PEPOINTRIGHT( sm, &p1 );
567 	if( icontainer_map_rev( ICONTAINER( compile ),
568 		(icontainer_map_fn) add_class_member, &pbi, &p1 ) )
569 		return( FALSE );
570 
571 	/* Add name member.
572 	 */
573 	if( heap_safe_pointer( heap,
574 		(heap_safe_pointer_fn) class_new_single_name,
575 		&pbi, &p1, NULL, NULL ) )
576 		return( FALSE );
577 
578 	/* Add this member.
579 	 */
580 	if( !add_class_svpair( &pbi, sths, this, &p1 ) )
581 		return( FALSE );
582 
583 	/* Add class parameters to member list.
584 	 */
585 	pbi.i = 0;
586 	if( slist_map2_rev( compile->param,
587 		(SListMap2Fn) add_class_parameter, &pbi, &p1 ) )
588 		return( FALSE );
589 
590 	/* Now ... secret list starts off pointing to head of member list.
591 	 */
592 	PEPUTLEFT( sm, &p1 );
593 
594 	/* Add all secret parameters to secret list.
595 	 */
596 	PEPOINTLEFT( sm, &p1 );
597 	if( slist_map2_rev( compile->secret,
598 		(SListMap2Fn) add_class_parameter, &pbi, &p1 ) )
599 		return( FALSE );
600 
601 #ifdef DEBUG
602 {
603 	char txt[256];
604 	VipsBuf buf = VIPS_BUF_STATIC( txt );
605 
606 	graph_pelement( heap, &buf, out, TRUE );
607 	printf( "class_new_single: built instance of " );
608 	symbol_name_print( sym );
609 	printf( ":\n%s\n", vips_buf_all( &buf ) );
610 }
611 #endif /*DEBUG*/
612 
613 	return( TRUE );
614 }
615 
616 /* Look at a scrap of graph and try to find a constructor it might be using.
617  * This will only work for really basic functions :-( but it's enough to allow
618  * us to pass extra secrets through the superclass. Used by (eg.) Colour when
619  * it overrides Value and adds the colourspace arg.
620  */
621 static Compile *
class_guess_constructor(PElement * fn)622 class_guess_constructor( PElement *fn )
623 {
624 	if( PEISCONSTRUCTOR( fn ) )
625 		return( PEGETCOMPILE( fn ) );
626 	else if( PEISNODE( fn ) ) {
627 		HeapNode *hn = PEGETVAL( fn );
628 
629 		if( hn->type == TAG_APPL ) {
630 			PElement left;
631 
632 			PEPOINTLEFT( hn, &left );
633 
634 			return( class_guess_constructor( &left ) );
635 		}
636 	}
637 
638 	return( NULL );
639 }
640 
641 /* Look at arg0 and try to extract the arguments (all the RHS of the @ nodes).
642  * Return the number of args we found, or -1 if we find crazy stuff.
643  */
644 static int
class_guess_args(PElement arg[],PElement * fn)645 class_guess_args( PElement arg[], PElement *fn )
646 {
647 	if( PEISCONSTRUCTOR( fn ) )
648 		return( 0 );
649 	else if( PEISNODE( fn ) ) {
650 		PElement left;
651 		int i;
652 
653 		PEPOINTLEFT( PEGETVAL( fn ), &left );
654 		if( (i = class_guess_args( arg, &left )) == -1 )
655 			return( -1 );
656 		if( i >= MAX_SYSTEM ) {
657 			error_top( _( "Too many arguments." ) );
658 			error_sub( _( "You can't have more than %d "
659 				"arguments to a superclass constructor." ),
660 				MAX_SYSTEM );
661 
662 			return( -1 );
663 		}
664 
665 		PEPOINTRIGHT( PEGETVAL( fn ), &arg[i] );
666 
667 		return( i + 1 );
668 	}
669 	else
670 		return( -1 );
671 }
672 
673 static void *
class_new_super_sub(Heap * heap,PElement * p1,Compile * compile,PElement * arg,PElement * this,PElement * super)674 class_new_super_sub( Heap *heap, PElement *p1,
675 	Compile *compile, PElement *arg, PElement *this, PElement *super )
676 {
677 	/* Build the superclass ... we overwrite the super
678 	 * list with the constructed class, so make a copy of
679 	 * the pointer to stop it being GCed.
680 	 */
681 	PEPUTPE( p1, super );
682 
683 	if( !class_new_single( heap, compile, arg, this, super ) )
684 		return( heap );
685 
686 	return( NULL );
687 }
688 
689 /* Clone a class instance. Copy pointers to the the args, secrets and super;
690  * rebuild with the specified "this". Instance and out can be equal.
691  */
692 static gboolean
class_clone_super(Heap * heap,Compile * compile,PElement * instance,PElement * this,PElement * out)693 class_clone_super( Heap *heap, Compile *compile,
694 	PElement *instance, PElement *this, PElement *out )
695 {
696 	PElement arg[MAX_SYSTEM];
697 	const int nargs = compile->nsecret + compile->nparam;
698 	PElement secret;
699 	int i;
700 
701 	g_assert( nargs <= MAX_SYSTEM );
702 
703 #ifdef DEBUG_VERBOSE
704 {
705 	char txt[MAX_STRSIZE];
706 	VipsBuf buf = VIPS_BUF_STATIC( txt );
707 
708 	graph_pelement( heap, &buf, instance, TRUE );
709 	printf( "class_new_clone: about to clone \"%s\": %s\n",
710 		IOBJECT( compile->sym )->name, vips_buf_all( &buf ) );
711 }
712 #endif /*DEBUG_VERBOSE*/
713 
714 	/* Pull out values of secrets and class args into arg[].
715 	 */
716 	PEGETCLASSSECRET( &secret, instance );
717 	for( i = 0; i < nargs; i++ ) {
718 		HeapNode *hn = PEGETVAL( &secret );
719 		HeapNode *sv = GETLEFT( hn );
720 		int index = nargs - i - 1;
721 
722 		PEPOINTRIGHT( sv, &arg[index] );
723 		PEPOINTRIGHT( hn, &secret );
724 	}
725 
726 	/* Build class again.
727 	 */
728 	return( class_new_single( heap, compile, arg, this, out ) );
729 }
730 
731 static void *
class_clone_super_sub(Heap * heap,PElement * p1,Compile * compile,PElement * instance,PElement * this,PElement * out)732 class_clone_super_sub( Heap *heap, PElement *p1,
733 	Compile *compile, PElement *instance, PElement *this, PElement *out )
734 {
735 	/* instance and out can point to the same node, so save a pointer to
736 	 * instance to stop it being GCed.
737 	 */
738 	PEPUTPE( p1, instance );
739 
740 	if( !class_clone_super( heap, compile, instance, this, out ) )
741 		return( heap );
742 
743 	return( NULL );
744 }
745 
746 /* Does this class have a "super"? Build it and recurse.
747  */
748 gboolean
class_new_super(Heap * heap,Compile * compile,PElement * this,PElement * instance)749 class_new_super( Heap *heap,
750 	Compile *compile, PElement *this, PElement *instance )
751 {
752 	PElement super;
753 
754 	if( compile->has_super && class_get_super( instance, &super ) ) {
755 		Compile *super_compile;
756 		int len, fn_len;
757 		PElement arg0;
758 
759 		/* It must be a list whose first element is the superclass
760 		 * constructor, or a partially parameterised constructor, or
761 		 * the superclass itself (if it has already
762 		 * been constructed, or has no args). Other elements in the
763 		 * list are the remaining args.
764 		 *
765 		 * We keep the list form, since we want to not build the
766 		 * superclass until now if we can help it ... otherwise we
767 		 * have to construct once, then construct again when we clone.
768 		 */
769 		if( (len = heap_list_length( &super )) < 1 ||
770 			!heap_list_index( &super, 0, &arg0 ) ||
771 			!heap_reduce_strict( &arg0 ) )
772 			return( FALSE );
773 
774 		if( (super_compile = class_guess_constructor( &arg0 )) ) {
775 			PElement fn_arg[MAX_SYSTEM];
776 			PElement arg[MAX_SYSTEM];
777 			int i;
778 
779 			/* How many function args are there?
780 			 */
781 			if( (fn_len = class_guess_args( fn_arg, &arg0 )) < 0 )
782 				return( FALSE );
783 
784 			/* Check total arg count.
785 			 */
786 			if( super_compile->nsecret != 0 ) {
787 				char txt[1024];
788 				VipsBuf buf = VIPS_BUF_STATIC( txt );
789 
790 				slist_map2( super_compile->secret,
791 					(SListMap2Fn) symbol_name_error,
792 					&buf, NULL );
793 
794 				error_top( _( "Bad superclass." ) );
795 				error_sub( _( "Superclass constructor \"%s\" "
796 					"refers to non-local symbols %s" ),
797 					symbol_name( super_compile->sym ),
798 					vips_buf_all( &buf ) );
799 
800 				return( FALSE );
801 			}
802 			if( len - 1 + fn_len != super_compile->nparam ) {
803 				error_top( _( "Wrong number of arguments." ) );
804 				error_sub( _( "Superclass constructor \"%s\" "
805 					"expects %d arguments, not %d." ),
806 					symbol_name( super_compile->sym ),
807 					super_compile->nparam,
808 					len - 1 + fn_len );
809 
810 				return( FALSE );
811 			}
812 
813 			/* Grab the explicit args from the super list.
814 			 */
815 			for( i = 0; i < len - 1; i++ ) {
816 				if( !heap_list_index( &super, len - 1 - i,
817 					&arg[i] ) )
818 					return( FALSE );
819 			}
820 
821 			/* Append the function args, but reverse them as we
822 			 * go so we get most-nested arg last.
823 			 */
824 			for( i = 0; i < fn_len; i++ )
825 				arg[i + len - 1] = fn_arg[fn_len - 1 - i];
826 
827 			/* Build the superclass ... we overwrite the super
828 			 * list with the constructed class, so make a copy of
829 			 * the pointer to stop it being GCed.
830 			 */
831 			if( heap_safe_pointer( heap,
832 				(heap_safe_pointer_fn) class_new_super_sub,
833 				super_compile, arg, this, &super ) )
834 				return( FALSE );
835 
836 		}
837 		else if( PEISCLASS( &arg0 ) ) {
838 			/* Super is a constructed class ... clone it, but with
839 			 * our "this" in there. Slow, but useful.
840 			 */
841 			super_compile = PEGETCLASSCOMPILE( &arg0 );
842 
843 			if( heap_safe_pointer( heap,
844 				(heap_safe_pointer_fn) class_clone_super_sub,
845 				super_compile, &arg0, this, &super ) )
846 				return( FALSE );
847 		}
848 		else {
849 			char txt1[300];
850 			VipsBuf buf1 = VIPS_BUF_STATIC( txt1 );
851 			char txt2[300];
852 			VipsBuf buf2 = VIPS_BUF_STATIC( txt2 );
853 
854 			error_top( _( "Bad superclass." ) );
855 
856 			itext_value( reduce_context, &buf1, &arg0 );
857 			vips_buf_appendf( &buf2,
858 				_( "First element in superclass of \"%s\" "
859 				"must be class or constructor." ),
860 				symbol_name( compile->sym ) );
861 			vips_buf_appendf( &buf2, "\n" );
862 			vips_buf_appendf( &buf2, _( "You passed:" ) );
863 			error_sub( "%s\n  %s",
864 				vips_buf_all( &buf2 ), vips_buf_all( &buf1 ) );
865 
866 			return( FALSE );
867 		}
868 
869 		/* And recursively build any superclasses.
870 		 */
871 		if( !class_new_super( heap, super_compile, this, &super ) )
872 			return( FALSE );
873 	}
874 
875 	return( TRUE );
876 }
877 
878 /* Make a class instance.
879  */
880 gboolean
class_new(Heap * heap,Compile * compile,HeapNode ** arg,PElement * out)881 class_new( Heap *heap, Compile *compile, HeapNode **arg, PElement *out )
882 {
883 	int i;
884 	PElement pe_arg[MAX_SYSTEM];
885 
886 	/* Make a set of arg pointers.
887 	 */
888 	if( compile->nparam + compile->nsecret >= MAX_SYSTEM ) {
889 		error_top( _( "Too many arguments." ) );
890 		error_sub( _( "Too many arguments to class constructor \"%s\". "
891 			"No more than %d arguments are supported." ),
892 			symbol_name( compile->sym ), MAX_SYSTEM );
893 		return( FALSE );
894 	}
895 	for( i = 0; i < compile->nparam + compile->nsecret; i++ ) {
896 		PEPOINTRIGHT( arg[i], &pe_arg[i] );
897 	}
898 
899 	/* Build the base instance.
900 	 */
901 	if( !class_new_single( heap, compile, pe_arg, out, out ) )
902 		return( FALSE );
903 
904 	/* And recursively build any superclasses.
905 	 */
906 	if( !class_new_super( heap, compile, out, out ) )
907 		return( FALSE );
908 
909 #ifdef DEBUG_BUILD
910 {
911 	char txt[MAX_STRSIZE];
912 	VipsBuf buf = VIPS_BUF_STATIC( txt );
913 
914 	graph_pelement( heap, &buf, out, TRUE );
915 	printf( "class_new: built instance of \"%s\": %s\n",
916 		IOBJECT( compile->sym )->name, vips_buf_all( &buf ) );
917 }
918 #endif /*DEBUG_BUILD*/
919 
920 	return( TRUE );
921 }
922 
923 /* Clone a class instance. Copy pointers to the the args, secrets and super;
924  * regenerate all the members. instance and out can be equal.
925  */
926 gboolean
class_clone_args(Heap * heap,PElement * instance,PElement * out)927 class_clone_args( Heap *heap, PElement *instance, PElement *out )
928 {
929 	HeapNode *arg[MAX_SYSTEM];
930 	Compile *compile = PEGETCLASSCOMPILE( instance );
931 	const int nargs = compile->nsecret + compile->nparam;
932 	PElement secret;
933 	int i;
934 
935 	g_assert( nargs <= MAX_SYSTEM );
936 
937 #ifdef DEBUG_VERBOSE
938 {
939 	char txt[MAX_STRSIZE];
940 	VipsBuf buf = VIPS_BUF_STATIC( txt );
941 
942 	graph_pelement( heap, &buf, instance, TRUE );
943 	printf( "class_clone_args: about to clone \"%s\": %s\n",
944 		IOBJECT( compile->sym )->name, vips_buf_all( &buf ) );
945 }
946 #endif /*DEBUG_VERBOSE*/
947 
948 	/* Pull out values of secrets and class args into RHS of arg[].
949 	 */
950 	PEGETCLASSSECRET( &secret, instance );
951 	for( i = 0; i < nargs; i++ ) {
952 		HeapNode *hn = PEGETVAL( &secret );
953 		HeapNode *sv = GETLEFT( hn );
954 		int index = nargs - i - 1;
955 
956 		arg[index] = sv;
957 		PEPOINTRIGHT( hn, &secret );
958 	}
959 
960 	/* Build class again.
961 	 */
962 	return( class_new( heap, compile, &arg[0], out ) );
963 }
964 
965 /* Build a class instance picking parameters from C args ... handy for
966  * making a new toggle instance on a click, for example.
967  */
968 gboolean
class_newv(Heap * heap,const char * name,PElement * out,...)969 class_newv( Heap *heap, const char *name, PElement *out, ... )
970 {
971 	va_list ap;
972 	Symbol *sym;
973 	Compile *compile;
974 	HeapNode args[MAX_SYSTEM];
975 	HeapNode *pargs[MAX_SYSTEM];
976 	int i;
977 
978 	if( !(sym = compile_lookup( symbol_root->expr->compile, name )) ||
979 		!is_value( sym ) || !is_class( sym->expr->compile ) ) {
980 		error_top( _( "Class not found." ) );
981 		error_sub( _( "Class \"%s\" not found." ), name );
982 		return( FALSE );
983 	}
984 	compile = sym->expr->compile;
985 	if( compile->nparam >= MAX_SYSTEM ) {
986 		error_top( _( "Too many arguments." ) );
987 		error_sub( _( "Too many arguments to class constructor \"%s\". "
988 			"No more than %d arguments are supported." ),
989 			symbol_name( compile->sym ), MAX_SYSTEM );
990 		return( FALSE );
991 	}
992 
993         va_start( ap, out );
994 	for( i = 0; i < compile->nparam; i++ ) {
995 		PElement *arg = va_arg( ap, PElement * );
996 		PElement rhs;
997 
998 		pargs[i] = &args[i];
999 		PEPOINTRIGHT( pargs[i], &rhs );
1000 		PEPUTPE( &rhs, arg );
1001 	}
1002         va_end( ap );
1003 
1004 	return( class_new( heap, compile, &pargs[0], out ) );
1005 }
1006 
1007 static void
class_typecheck_error(PElement * instance,const char * name,const char * type)1008 class_typecheck_error( PElement *instance, const char *name, const char *type )
1009 {
1010 	char txt[1024];
1011 	VipsBuf buf = VIPS_BUF_STATIC( txt );
1012 	PElement val;
1013 
1014 	vips_buf_appendf( &buf, _( "Member \"%s\" of class \"%s\" "
1015 		"should be of type \"%s\", instead it's:" ),
1016 		name,
1017 		IOBJECT( PEGETCLASSCOMPILE( instance )->sym )->name,
1018 		type );
1019 	vips_buf_appends( &buf, "\n   " );
1020 	if( class_get_member( instance, name, NULL, &val ) &&
1021 		!itext_value( reduce_context, &buf, &val ) )
1022 		return;
1023 
1024 	error_top( _( "Bad argument." ) );
1025 	error_sub( "%s", vips_buf_all( &buf ) );
1026 }
1027 
1028 /* A function that gets a type from a class.
1029  */
1030 typedef gboolean (*ClassGetFn)( PElement *, void * );
1031 
1032 static gboolean
class_get_member_check(PElement * instance,const char * name,const char * type,ClassGetFn fn,void * a)1033 class_get_member_check( PElement *instance, const char *name, const char *type,
1034 	ClassGetFn fn, void *a )
1035 {
1036 	PElement val;
1037 
1038 	if( !class_get_member( instance, name, NULL, &val ) )
1039 		return( FALSE );
1040 
1041 	if( !fn( &val, a ) ) {
1042 		class_typecheck_error( instance, name, type );
1043 		return( FALSE );
1044 	}
1045 
1046 	return( TRUE );
1047 }
1048 
1049 gboolean
class_get_member_bool(PElement * instance,const char * name,gboolean * out)1050 class_get_member_bool( PElement *instance, const char *name, gboolean *out )
1051 {
1052 	return( class_get_member_check( instance, name, "bool",
1053 		(ClassGetFn) heap_get_bool, out ) );
1054 }
1055 
1056 gboolean
class_get_member_real(PElement * instance,const char * name,double * out)1057 class_get_member_real( PElement *instance, const char *name, double *out )
1058 {
1059 	return( class_get_member_check( instance, name, "real",
1060 		(ClassGetFn) heap_get_real, out ) );
1061 }
1062 
1063 gboolean
class_get_member_int(PElement * instance,const char * name,int * out)1064 class_get_member_int( PElement *instance, const char *name, int *out )
1065 {
1066 	double d;
1067 
1068 	if( !class_get_member_check( instance, name, "real",
1069 		(ClassGetFn) heap_get_real, &d ) )
1070 		return( FALSE );
1071 	*out = IM_RINT( d );
1072 
1073 	return( TRUE );
1074 }
1075 
1076 gboolean
class_get_member_class(PElement * instance,const char * name,const char * type,PElement * out)1077 class_get_member_class( PElement *instance, const char *name,
1078 	const char *type, PElement *out )
1079 {
1080 	gboolean result;
1081 
1082 	if( !class_get_member_check( instance, name, type,
1083 		(ClassGetFn) heap_get_class, out ) )
1084 		return( FALSE );
1085 
1086 	if( !heap_is_instanceof( type, out, &result ) )
1087 		return( FALSE );
1088 	if( !result ) {
1089 		class_typecheck_error( instance, name, type );
1090 		return( FALSE );
1091 	}
1092 
1093 	return( TRUE );
1094 }
1095 
1096 gboolean
class_get_member_image(PElement * instance,const char * name,Imageinfo ** out)1097 class_get_member_image( PElement *instance, const char *name, Imageinfo **out )
1098 {
1099 	return( class_get_member_check( instance, name, "image",
1100 		(ClassGetFn) heap_get_image, out ) );
1101 }
1102 
1103 gboolean
class_get_member_lstring(PElement * instance,const char * name,GSList ** labels)1104 class_get_member_lstring( PElement *instance, const char *name,
1105 	GSList **labels )
1106 {
1107 	return( class_get_member_check( instance, name, "finite [[char]]",
1108 		(ClassGetFn) heap_get_lstring, labels ) );
1109 }
1110 
1111 gboolean
class_get_member_string(PElement * instance,const char * name,char * buf,int sz)1112 class_get_member_string( PElement *instance, const char *name,
1113 	char *buf, int sz )
1114 {
1115 	PElement val;
1116 
1117 	if( !class_get_member( instance, name, NULL, &val ) )
1118 		return( FALSE );
1119 
1120 	if( !heap_get_string( &val, buf, sz ) ) {
1121 		class_typecheck_error( instance, name, "finite [char]" );
1122 		return( FALSE );
1123 	}
1124 
1125 	return( TRUE );
1126 }
1127 
1128 gboolean
class_get_member_instance(PElement * instance,const char * name,const char * klass,PElement * out)1129 class_get_member_instance( PElement *instance,
1130 	const char *name, const char *klass, PElement *out )
1131 {
1132 	gboolean result;
1133 
1134 	return( class_get_member( instance, name, NULL, out ) &&
1135 		heap_is_instanceof( klass, out, &result ) &&
1136 		result );
1137 }
1138 
1139 gboolean
class_get_member_matrix_size(PElement * instance,const char * name,int * xsize,int * ysize)1140 class_get_member_matrix_size( PElement *instance, const char *name,
1141 	int *xsize, int *ysize )
1142 {
1143 	PElement val;
1144 
1145 	if( !class_get_member( instance, name, NULL, &val ) )
1146 		return( FALSE );
1147 
1148 	if( !heap_get_matrix_size( &val, xsize, ysize ) ) {
1149 		class_typecheck_error( instance, name,
1150 			"finite rectangular [[real]]" );
1151 		return( FALSE );
1152 	}
1153 
1154 	return( TRUE );
1155 }
1156 
1157 gboolean
class_get_member_matrix(PElement * instance,const char * name,double * buf,int n,int * xsize,int * ysize)1158 class_get_member_matrix( PElement *instance, const char *name,
1159 	double *buf, int n, int *xsize, int *ysize )
1160 {
1161 	PElement val;
1162 
1163 	if( !class_get_member( instance, name, NULL, &val ) )
1164 		return( FALSE );
1165 
1166 	if( !heap_get_matrix( &val, buf, n, xsize, ysize ) ) {
1167 		class_typecheck_error( instance, name,
1168 			"finite rectangular [[real]]" );
1169 		return( FALSE );
1170 	}
1171 
1172 	return( TRUE );
1173 }
1174 
1175 gboolean
class_get_member_realvec(PElement * instance,const char * name,double * buf,int n,int * length)1176 class_get_member_realvec( PElement *instance, const char *name,
1177 	double *buf, int n, int *length )
1178 {
1179 	PElement val;
1180 	int l;
1181 
1182 	if( !class_get_member( instance, name, NULL, &val ) )
1183 		return( FALSE );
1184 
1185 	if( (l = heap_get_realvec( &val, buf, n )) < 0 ) {
1186 		class_typecheck_error( instance, name, "finite [real]" );
1187 		return( FALSE );
1188 	}
1189 
1190 	*length = l;
1191 
1192 	return( TRUE );
1193 }
1194