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