1 /*
2  * Copyright (C) 2000-2005 Chris Ross and various contributors
3  * Copyright (C) 1999-2000 Chris Ross
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * o Redistributions of source code must retain the above copyright notice, this
10  *   list of conditions and the following disclaimer.
11  * o Redistributions in binary form must reproduce the above copyright notice,
12  *   this list of conditions and the following disclaimer in the documentation
13  *   and/or other materials provided with the distribution.
14  * o Neither the name of the ferite software nor the names of its contributors may
15  *   be used to endorse or promote products derived from this software without
16  *   specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #ifdef HAVE_CONFIG_HEADER
32 #include "../config.h"
33 #endif
34 
35 #include "ferite.h"
36 
37 /* this is so that each registered class gets a unique id. this allows for native
38  * code to check whether they are being passed one of their objects or someone elses. */
39 long ferite_internal_class_counter = 1000;
40 
41 /**
42  * @group Classes
43  * @description Class are what objects are made from. There are a number of functions in this group
44  *              that allows for creation and manipulation of both objects and classes.
45  */
46 
47 /**
48  * @function ferite_register_inherited_class
49  * @declaration FeriteClass *ferite_register_inherited_class( FeriteScript *script, FeriteNamespace *ns, char *name, char *parent )
50  * @brief Registers, creates and returns a class object
51  * @param FeriteScript *script The current script being run
52  * @param FeriteNamespace *ns     The namespace in which the class should be registered
53  * @param char *name   The name of the class eg. Socket
54  * @param char *parent The name of the parent class, this is the class that will be inherited from, if NULL the class will inherit from the base class 'Obj'.
55  * @description This is the only way to create a class natively.
56  * @return The newly created class
57  */
ferite_register_inherited_class(FeriteScript * script,FeriteNamespace * ns,char * name,char * parent)58 FeriteClass *ferite_register_inherited_class( FeriteScript *script, FeriteNamespace *ns, char *name, char *parent )
59 {
60     FeriteClass *ptr = NULL, *klass = NULL;
61 
62     FE_ENTER_FUNCTION;
63 
64     if( (ferite_namespace_element_exists( script, ns, name )) != NULL )
65     {
66         ferite_error( script, 0, "An item '%s' already exists so the class can't be created.\n", name );
67         FE_LEAVE_FUNCTION(NULL);
68     }
69     if( parent == NULL && strcmp( name, "Obj" ) )
70     {
71         parent = "Obj";
72     }
73     if( parent != NULL )
74     {
75         ptr = ferite_find_class( script, ns, parent );
76         if( ptr == NULL )
77         {
78             ferite_warning( script, "Parent class %s does not exist. Not inheriting from it for %s.\n", parent, name );
79             if( strcmp( parent, "Obj" ) )
80             {
81                 parent = "Obj";
82                 ptr = ferite_find_class( script, ns, parent );
83             }
84             if( ptr == NULL )
85             {
86                 ferite_error( script, 0, "Failed to even use Obj as a parent for %s\n", name );
87                 FE_LEAVE_FUNCTION( NULL );
88             }
89         }
90         else if( ptr->state == FE_ITEM_IS_FINAL )
91         {
92             ferite_error( script, 0, "Parent class %s is final and can not be extended by new class %s\n", parent, name );
93             FE_LEAVE_FUNCTION( NULL );
94         }
95         else if( ptr->state == FE_ITEM_IS_PROTOCOL )
96         {
97             ferite_error( script, 0, "Parent '%s' is a protocol and not a class and therefore can not be extended by new class %s\n", parent, name );
98             FE_LEAVE_FUNCTION( NULL );
99         }
100     }
101     klass = fmalloc( sizeof( FeriteClass ) );
102     klass->name = fstrdup( name );
103     klass->object_vars = ferite_variable_hash_alloc( script, FE_CLASS_VARIABLE_HASH_SIZE );
104     klass->class_vars = ferite_variable_hash_alloc( script, FE_CLASS_VARIABLE_HASH_SIZE );
105     klass->object_methods = ferite_create_hash( script, FE_CLASS_FUNCTION_HASH_SIZE );
106     klass->class_methods = ferite_create_hash( script, FE_CLASS_FUNCTION_HASH_SIZE );
107     klass->id = ++ferite_internal_class_counter;
108     klass->odata = NULL;
109     klass->parent = ptr;
110     klass->next = NULL;
111     klass->container = ns;
112     klass->state = 0;
113     klass->impl_list = ferite_create_stack( script, 5 );
114     ferite_register_ns_class( script, ns, klass );
115 
116     FE_LEAVE_FUNCTION( klass );
117 }
118 
119 /**
120  * @function ferite_register_class_function
121  * @declaration int ferite_register_class_function( FeriteScript *script, FeriteClass *klass, FeriteFunction *f )
122  * @brief Register a function within a class
123  * @param FeriteScript *script The current script
124  * @param FeriteClass *klass The class to place the function in
125  * @param FeriteFunction *f      The function structure
126  * @param int is_static Boolean, 0 = not static, 1 = static. Allows the specifcation of whether or not the function has static access within the class
127  * @description It must be noted that the function being passed must not have the super, and self variables within their signitue, this
128  *              function will handle that automatically
129  * @return 1 if function was registered correctly, 0 otherwise
130  */
ferite_register_class_function(FeriteScript * script,FeriteClass * klass,FeriteFunction * f,int is_static)131 int ferite_register_class_function( FeriteScript *script, FeriteClass *klass, FeriteFunction *f, int is_static )
132 {
133     FeriteFunction *function = NULL;
134     FeriteHash *target_hash = NULL;
135 
136     FE_ENTER_FUNCTION;
137     if( klass != NULL )
138     {
139         target_hash = klass->object_methods;
140         if( is_static )
141           target_hash = klass->class_methods;
142 
143         if( (function = ferite_hash_get( script, target_hash, f->name )) != NULL )
144         {
145             if( strcmp( f->name, "destructor" ) == 0 )
146             {
147                 ferite_error( script, 0, "You can only have one '%s' destructor in the class %s\n",
148                               ( is_static ? "static" : "instance" ),
149                               klass->name );
150                 FE_LEAVE_FUNCTION( FE_FALSE );
151             }
152             else if( strcmp( f->name, klass->name ) == 0 && is_static )
153             {
154                 ferite_error( script, 0, "You can only have one static constructor in the class '%s'\n", klass->name );
155                 FE_LEAVE_FUNCTION( FE_FALSE );
156             }
157 
158             f->next = function->next;
159             function->next = f;
160         }
161         else
162           ferite_hash_add( script, target_hash, f->name, f );
163 
164         f->is_static = is_static;
165         f->klass = klass;
166 
167         FE_LEAVE_FUNCTION( FE_TRUE );
168     }
169     FE_LEAVE_FUNCTION( FE_FALSE );
170 }
171 
172 /**
173  * @function ferite_register_class_variable
174  * @declaration int ferite_register_class_variable( FeriteScript *script, FeriteClass *klass, FeriteVariable *variable, int is_static )
175  * @brief Register a variable within a class
176  * @param FeriteScript *script    The current script
177  * @param FeriteClass *klass  The class to attach the variable to
178  * @param FeriteVariable *variable  The variable to register
179  * @param int is_static Boolean, 0 = not static, 1 = static. Allows the specifcation of whether or not the variable has static access within the class
180  * @return 1 on success, 0 otherwise
181  */
ferite_register_class_variable(FeriteScript * script,FeriteClass * klass,FeriteVariable * variable,int is_static)182 int ferite_register_class_variable( FeriteScript *script, FeriteClass *klass, FeriteVariable *variable, int is_static )
183 {
184     FE_ENTER_FUNCTION;
185 
186     if( klass != NULL )
187     {
188         if( variable != NULL )
189         {
190             if( is_static )
191             {
192                 ferite_add_variable_to_hash( script, klass->class_vars, variable );
193                 MARK_VARIABLE_AS_STATIC( variable );
194             }
195             else
196                 ferite_add_variable_to_hash( script, klass->object_vars, variable );
197         }
198         else
199         {
200             ferite_error( script, 0, "Can't register a NULL variable in class %s", klass->name );
201             FE_LEAVE_FUNCTION( FE_FALSE );
202         }
203     }
204     else
205     {
206         ferite_error( script, 0, "Can't register a variable in a non existant class" );
207         FE_LEAVE_FUNCTION( FE_FALSE );
208     }
209     FE_LEAVE_FUNCTION( FE_TRUE );
210 }
211 
212 /**
213  * @function ferite_class_finish
214  * @declaration void ferite_class_finish( FeriteScript *script, FeriteClass *klass )
215  * @brief Finish the class off and call any relevent functions needed.
216  * @param FeriteScript *script The script the class sits in
217  * @param FeriteClass *klass The class to finish off
218  */
ferite_class_finish(FeriteScript * script,FeriteClass * klass)219 void ferite_class_finish( FeriteScript *script, FeriteClass *klass )
220 {
221     FE_ENTER_FUNCTION;
222     ferite_class_call_static_constructor( script, klass );
223     FE_LEAVE_FUNCTION(NOWT);
224 }
225 
226 /**
227  * @function ferite_class_call_static_constructor
228  * @declaration void ferite_class_call_static_constructor( FeriteScript *script, FeriteClass *klass )
229  * @brief Call the static constructor on the class
230  * @param FeriteScript *script The script in which the class resides
231  * @param FeriteClass *klass The class
232  * @description This function will traverse the heirachy within the class system to allow for parents to
233  *				be called.
234  */
ferite_class_call_static_constructor(FeriteScript * script,FeriteClass * klass)235 void ferite_class_call_static_constructor( FeriteScript *script, FeriteClass *klass )
236 {
237     FeriteFunction *function = NULL;
238     FeriteVariable *return_value = NULL;
239 
240     FE_ENTER_FUNCTION;
241     function = ferite_find_static_constructor( script, klass, NULL );
242     if( function != NULL )
243     {
244         return_value = ferite_call_function( script, klass, NULL, function, NULL );
245         ferite_variable_destroy( script, return_value );
246     }
247     FE_LEAVE_FUNCTION(NOWT);
248 }
249 
250 /**
251 * @function ferite_class_call_static_destructor
252  * @declaration void ferite_class_call_static_destructor( FeriteScript *script, FeriteClass *klass )
253  * @brief Call the static destructor on the class
254  * @param FeriteScript *script The script in which the class resides
255  * @param FeriteClass *klass The class
256  * @description This function will traverse the heirachy within the class system to allow for parents to
257  *				be called.
258  */
ferite_class_call_static_destructor(FeriteScript * script,FeriteClass * klass)259 void ferite_class_call_static_destructor( FeriteScript *script, FeriteClass *klass )
260 {
261     FeriteFunction *function = NULL;
262     FeriteVariable *return_value = NULL;
263 
264     FE_ENTER_FUNCTION;
265     function = ferite_class_get_function( script, klass, "destructor" );
266     if( function != NULL )
267     {
268         return_value = ferite_call_function( script, klass, NULL, function, NULL );
269         ferite_variable_destroy( script, return_value );
270     }
271     FE_LEAVE_FUNCTION(NOWT);
272 }
273 
274 
275 /**
276  * @function ferite_delete_class
277  * @declaration void ferite_delete_class( FeriteScript *script, FeriteClass *klass )
278  * @brief Clean up and free the memory a class takes up
279  * @param FeriteScript *script The current script
280  * @param FeriteClass *klass The class to be deleted
281  */
ferite_delete_class(FeriteScript * script,FeriteClass * klass)282 void ferite_delete_class( FeriteScript *script, FeriteClass *klass )
283 {
284     FE_ENTER_FUNCTION;
285 
286     if( klass != NULL )
287     {
288         FUD(("Deleting Class: %s\n", klass->name ));
289         ferite_class_call_static_destructor( script, klass );
290 
291         ffree( klass->name );
292         ferite_delete_variable_hash( script, klass->object_vars );
293 		ferite_delete_variable_hash( script, klass->class_vars );
294         ferite_delete_function_hash( script, klass->object_methods );
295         ferite_delete_function_hash( script, klass->class_methods );
296         ferite_delete_stack( script, klass->impl_list );
297         ffree( klass );
298     }
299 
300     FE_LEAVE_FUNCTION( NOWT );
301 }
302 
303 /**
304  * @function ferite_find_class
305  * @declaration FeriteClass *ferite_find_class( FeriteScript *script, FeriteNamespace *ns, char *name )
306  * @brief Find a class within a namespace
307  * @param FeriteScript *script The current script
308  * @param FeriteNamespace *ns     The top level namespace to look in
309  * @param char *name   The name of the class to find.
310  * @return The class on success or NULL otherwise
311  */
ferite_find_class(FeriteScript * script,FeriteNamespace * ns,char * name)312 FeriteClass *ferite_find_class( FeriteScript *script, FeriteNamespace *ns, char *name )
313 {
314     FeriteClass *ptr = NULL;
315     FeriteNamespaceBucket *nsb = NULL;
316 
317     FE_ENTER_FUNCTION;
318     FUD(("Trying to find class %s\n", name));
319     nsb = ferite_find_namespace( script, ns, name, FENS_CLS );
320     if( nsb != NULL )
321     {
322         ptr = nsb->data;
323         FE_LEAVE_FUNCTION( ptr );
324     }
325     nsb = ferite_find_namespace( script, script->mainns, name, FENS_CLS );
326     if( nsb != NULL )
327     {
328         ptr = nsb->data;
329         FE_LEAVE_FUNCTION( ptr );
330     }
331     FE_LEAVE_FUNCTION( NULL );
332 }
333 
334 /**
335  * @function ferite_find_class_id
336  * @declaration long ferite_find_class_id( FeriteScript *script, FeriteNamespace *ns, char *name )
337  * @brief Find a class within a namespace, and return it's unique id
338  * @param FeriteScript *script The current script
339  * @param FeriteNamespace *ns     The top level namespace to look in
340  * @param char *name   The name of the class to find.
341  * @return The id or 0 otherwise
342  */
ferite_find_class_id(FeriteScript * script,FeriteNamespace * ns,char * name)343 long ferite_find_class_id( FeriteScript *script, FeriteNamespace *ns, char *name )
344 {
345     FeriteClass *ptr = NULL;
346 
347     FE_ENTER_FUNCTION;
348 
349     FUD(("Trying to find class %s\n", name));
350     ptr = ferite_find_class( script, ns, name );
351     if( ptr )
352     {
353         FE_LEAVE_FUNCTION( ptr->id );
354     }
355     FE_LEAVE_FUNCTION( 0 );
356 }
357 
ferite_duplicate_object_variable_list(FeriteScript * script,FeriteClass * klass)358 FeriteObjectVariable *ferite_duplicate_object_variable_list( FeriteScript *script, FeriteClass *klass )
359 {
360     FeriteObjectVariable *ov = NULL;
361 
362     FE_ENTER_FUNCTION;
363     ov = fmalloc( sizeof( FeriteObjectVariable ) );
364     ov->variables = ferite_duplicate_variable_hash( script, klass->object_vars );
365     ov->parent = NULL;
366     if( klass->parent != NULL )
367       ov->parent = ferite_duplicate_object_variable_list( script, klass->parent );
368     ov->klass = klass;
369     FE_LEAVE_FUNCTION( ov );
370 }
371 
ferite_delete_object_variable_list(FeriteScript * script,FeriteObjectVariable * ov)372 void ferite_delete_object_variable_list( FeriteScript *script, FeriteObjectVariable *ov )
373 {
374     FE_ENTER_FUNCTION;
375 
376     if( ov->parent != NULL )
377       ferite_delete_object_variable_list( script, ov->parent );
378 
379     ferite_delete_variable_hash( script, ov->variables );
380     ffree( ov );
381 
382     FE_LEAVE_FUNCTION(NOWT);
383 }
384 
385 /**
386  * @function ferite_delete_class_object
387  * @declaration void ferite_delete_class_object( FeriteObject *object )
388  * @brief Dispose of an object, the only part of ferite that should be calling this is the garbage collector.
389  * @param object The object to be nuked.
390  * @description
391  * If you want to get rid of an object the accepted method is as follows:<nl/>
392  * <nl/>
393  *    object->refcount = 0;<nl/>
394  *    ferite_check_gc();<nl/>
395  * <nl/>
396  * This will dispose of the object properly. The object destructor will get called if the objects' class is native.
397  */
ferite_delete_class_object(FeriteScript * script,FeriteObject * object,int do_destructor)398 void ferite_delete_class_object( FeriteScript *script, FeriteObject *object, int do_destructor )
399 {
400     FeriteFunction *func = NULL;
401     FeriteVariable *retv;
402     FeriteVariable **params;
403     FeriteClass *klass;
404 
405     FE_ENTER_FUNCTION;
406     if( object != NULL )
407     {
408         if( do_destructor && object->klass != NULL && object->klass->object_methods != NULL )
409         {
410             FUD(( "ferite_delete_class_object: trying to delete %s[%s]\n", object->name, object->klass->name ));
411             for( klass = object->klass; func == NULL && klass != NULL; klass = klass->parent )
412               func = ferite_hash_get( script, klass->object_methods, "destructor" );
413 
414             if( func != NULL )
415             {
416                 params = fmalloc( sizeof( FeriteVariable * ) * 3 );
417                 params[0] = NULL;
418                 params[1] = NULL;
419                 params[2] = NULL;
420 
421                 /* we have the destructor */
422                 FUD(( "OPS: Calling destructor (%s) in class %s\n", func->name, object->klass->name));
423                 if( func->type == FNC_IS_EXTRL )
424                   retv = (func->fncPtr)( script, object, NULL, func, params );
425                 else
426                   retv = ferite_script_function_execute( script, object, NULL, func, params );
427 
428                 ffree( params );
429                 ferite_variable_destroy( script, retv );
430             }
431         }
432 
433         FUD(( "Deleting class %s's variable hash %p\n", object->name, object->variables ));
434         if( object->variables != NULL )
435           ferite_delete_object_variable_list( script, object->variables );
436 
437         if( object->name != NULL )
438           ffree( object->name );
439 
440        /* Object cache */
441         if( script && script->objects->stack_ptr < script->objects->size-1 )
442           ferite_stack_push( script->objects, object );
443         else
444           ffree( object );
445     }
446     else
447       ferite_error( script, 0, "Error: trying to delete null object\n" );
448     FE_LEAVE_FUNCTION( NOWT );
449 }
450 
451 /**
452  * @function ferite_object_get_var
453  * @declaration FeriteVariable *ferite_object_get_var( FeriteScript *script, FeriteObject *object, char *name )
454  * @brief Get a member variable from an object
455  * @param FeriteScript *script The script
456  * @param FeriteObject *object The object to get the variable from
457  * @param char *name The name of the member to get
458  * @return The variable on success, NULL otherwise
459  */
ferite_object_get_var(FeriteScript * script,FeriteObject * object,char * name)460 FeriteVariable *ferite_object_get_var( FeriteScript *script, FeriteObject *object, char *name )
461 {
462     FeriteVariable *ptr = NULL;
463     FeriteObjectVariable *ov = NULL;
464 
465     FE_ENTER_FUNCTION;
466     if( object != NULL )
467     {
468         ov = object->variables;
469         for( ov = object->variables; ov != NULL; ov = ov->parent )
470         {
471             ptr = ferite_hash_get( script, ov->variables, name );
472             if( ptr != NULL && !FE_VAR_IS_STATIC(ptr) )
473             {
474                 FE_LEAVE_FUNCTION( ptr );
475             }
476         }
477     }
478     FE_LEAVE_FUNCTION( ptr );
479 }
480 
481 /**
482  * @function ferite_object_variable_class
483  * @declaration FeriteClass *ferite_object_variable_class( FeriteScript *script, FeriteObject *object, char *name )
484  * @brief Obtain the class in which a named variable appears within an object
485  * @param FeriteScript *script The script
486  * @param FeriteObject *object The object to look in
487  * @param char *name The name of the member to get
488  * @return The class if the varible exists and the class can be found, NULL otherwise
489  * @description Due to the way that variables are stored within the class hierachy, it is useful to find out to which
490  *              class a variable exists.
491  */
ferite_object_variable_class(FeriteScript * script,FeriteObject * object,char * name)492 FeriteClass *ferite_object_variable_class( FeriteScript *script, FeriteObject *object, char *name )
493 {
494     FeriteVariable *ptr = NULL;
495     FeriteObjectVariable *ov = NULL;
496 
497     FE_ENTER_FUNCTION;
498     if( object != NULL )
499     {
500         ov = object->variables;
501         for( ov = object->variables; ov != NULL; ov = ov->parent )
502         {
503             ptr = ferite_hash_get( script, ov->variables, name );
504             if( ptr != NULL && !FE_VAR_IS_STATIC(ptr) )
505             {
506                 FE_LEAVE_FUNCTION( ov->klass );
507             }
508         }
509     }
510     FE_LEAVE_FUNCTION( NULL );
511 }
512 
513 /**
514 * @function ferite_class_variable_class
515  * @declaration FeriteClass *ferite_class_variable_class( FeriteScript *script, FeriteClass *klass, char *name )
516  * @brief Obtain the class in which a named variable appears within a class tree
517  * @param FeriteScript *script The script
518  * @param FeriteClass *klass The class to look in
519  * @param char *name The name of the member to get
520  * @return The class if the varible exists and the class can be found, NULL otherwise
521  * @description Due to the way that variables are stored within the class hierachy, it is useful to find out to which
522  *              class a variable exists.
523  */
ferite_class_variable_class(FeriteScript * script,FeriteClass * klass,char * name)524 FeriteClass *ferite_class_variable_class( FeriteScript *script, FeriteClass *klass, char *name )
525 {
526     FeriteVariable *ptr = NULL;
527     FeriteClass *k = NULL;
528 
529     FE_ENTER_FUNCTION;
530     if( klass != NULL )
531     {
532         for( k = klass; k != NULL; k = k->parent )
533         {
534             ptr = ferite_hash_get( script, k->class_vars, name );
535             if( ptr != NULL )
536             {
537                 FE_LEAVE_FUNCTION( k );
538             }
539         }
540     }
541     FE_LEAVE_FUNCTION( NULL );
542 }
543 
544 /**
545  * @function ferite_class_get_var
546  * @declaration FeriteVariable *ferite_class_get_var( FeriteScript *script, FeriteClass *klass, char *name )
547  * @brief Get a member variable from an class
548  * @param FeriteScript *script The script
549  * @param FeriteClass *klass The class to get the variable from
550  * @param char *name The name of the member to get
551  * @return The variable on success, NULL otherwise
552  */
ferite_class_get_var(FeriteScript * script,FeriteClass * klass,char * name)553 FeriteVariable *ferite_class_get_var( FeriteScript *script, FeriteClass *klass, char *name )
554 {
555     FeriteVariable *ptr = NULL;
556     FeriteClass *k = NULL;
557 
558     FE_ENTER_FUNCTION;
559     if( klass != NULL )
560     {
561         for( k = klass; k != NULL; k = k->parent )
562         {
563             ptr = ferite_hash_get( script, k->class_vars, name );
564             if( ptr != NULL && FE_VAR_IS_STATIC(ptr) )
565             {
566                 FE_LEAVE_FUNCTION( ptr );
567             }
568         }
569     }
570     FE_LEAVE_FUNCTION( ptr );
571 }
572 
573 /**
574  * @function ferite_object_get_function
575  * @declaration FeriteFunction *ferite_object_get_function( FeriteScript *script, FeriteObject *object, char *name )
576  * @brief Get a function from an object
577  * @param FeriteScript *script The script
578  * @param FeriteObject *object The object to get the function from
579  * @param char *name The name of the function
580  * @return The function on success, NULL otherwise
581  */
ferite_object_get_function(FeriteScript * script,FeriteObject * object,char * name)582 FeriteFunction *ferite_object_get_function( FeriteScript *script, FeriteObject *object, char *name )
583 {
584     FeriteFunction *ptr = NULL;
585     FeriteClass *cls = NULL;
586 
587     FE_ENTER_FUNCTION;
588     if( object != NULL )
589     {
590         cls = object->klass;
591         while( cls != NULL )
592         {
593             ptr = ferite_hash_get( script, cls->object_methods, name );
594             if( ptr != NULL )
595             {
596                 FE_LEAVE_FUNCTION( ptr );
597             }
598             cls = cls->parent;
599         }
600     }
601     FE_LEAVE_FUNCTION( ptr );
602 }
603 /**
604  * @function ferite_object_get_function_for_params
605  * @declaration FeriteFunction *ferite_object_get_function_for_params( FeriteScript *script, FeriteObject *object, char *name, FeriteVariable **params )
606  * @brief Get a function from an object for a specific set of parameters
607  * @param FeriteScript *script The script
608  * @param FeriteObject *object The object to get the function from
609  * @param char *name The name of the function
610  * @param FeriteVariable **params The parameters that wish to be passed to the function
611  * @return The function on success, NULL otherwise
612  * @description This function will traverse the inheritance tree the object is from and look for a function that matches the given name and
613                 parameters.
614  */
ferite_object_get_function_for_params(FeriteScript * script,FeriteObject * object,char * name,FeriteVariable ** params)615 FeriteFunction *ferite_object_get_function_for_params( FeriteScript *script, FeriteObject *object, char *name, FeriteVariable **params )
616 {
617     FeriteFunction *ptr = NULL;
618     FeriteFunction *retf = NULL;
619     FeriteClass *klass = NULL;
620     FeriteVariable **plist = NULL;
621 
622     FE_ENTER_FUNCTION;
623     if( object != NULL )
624     {
625         plist = params;
626         if( params == NULL )
627         {
628             plist = ferite_create_parameter_list( 1 );
629             plist[0] = NULL;
630         }
631 
632         klass = object->klass;
633         while( klass != NULL )
634         {
635             for( ptr = ferite_hash_get( script, klass->object_methods, name ); ptr != NULL; ptr = ptr->next )
636             {
637                 if( ferite_check_params( script, plist, ptr ) == 1 )
638                 {
639                     retf = ptr;
640                     break;
641                 }
642             }
643             if( retf != NULL )
644                 break;
645 
646             klass = klass->parent;
647         }
648         if( params == NULL )
649             ffree( plist );
650     }
651     FE_LEAVE_FUNCTION( retf );
652 }
653 
654 /**
655  * @function ferite_class_get_function
656  * @declaration FeriteFunction *ferite_class_get_function( FeriteScript *script, FeriteClass *cls, char *name )
657  * @brief Get a function from a class
658  * @param FeriteScript *script The script
659  * @param FeriteClass *cls The class to get the function from
660  * @param char *name The name of the function
661  * @return The function on success, NULL otherwise
662  */
ferite_class_get_function(FeriteScript * script,FeriteClass * cls,char * name)663 FeriteFunction *ferite_class_get_function(FeriteScript *script, FeriteClass *cls, char *name)
664 {
665     FeriteFunction *ptr = NULL;
666     FeriteClass *klass = cls;
667     int doneTest = FE_FALSE;
668 
669     FE_ENTER_FUNCTION;
670     while(klass != NULL)
671     {
672         ptr = ferite_hash_get(script, klass->class_methods, name);
673         if( ptr != NULL )
674         {
675             FE_LEAVE_FUNCTION( ptr );
676         }
677 	if( !doneTest )
678 	{
679 	    if( strcmp( name, "destructor" ) == 0 ) /* We only look in the current class for the destructor, evil but necessary */
680 		break;
681 	    doneTest = FE_TRUE;
682 	}
683         klass = klass->parent;
684     }
685     FE_LEAVE_FUNCTION( NULL );
686 }
687 /**
688 * @function ferite_class_get_function_for_params
689  * @declaration FeriteFunction *ferite_class_get_function_for_params( FeriteScript *script, FeriteClass *klass, char *name, FeriteVariable **params )
690  * @brief Get a function from an object for a specific set of parameters
691  * @param FeriteScript *script The script
692  * @param FeriteClass *klass The class to get the function from
693  * @param char *name The name of the function
694  * @param FeriteVariable **params The parameters that wish to be passed to the function
695  * @return The function on success, NULL otherwise
696  * @description This function will traverse the inheritance tree the class is from and look for a function that matches the given name and
697                 parameters.
698  */
ferite_class_get_function_for_params(FeriteScript * script,FeriteClass * klass,char * name,FeriteVariable ** params)699 FeriteFunction *ferite_class_get_function_for_params( FeriteScript *script, FeriteClass *klass, char *name, FeriteVariable **params )
700 {
701     FeriteFunction *ptr = NULL;
702     FeriteFunction *retf = NULL;
703     FeriteVariable **plist = NULL;
704     int doneTest = FE_FALSE;
705 
706     FE_ENTER_FUNCTION;
707     if( klass != NULL )
708     {
709         plist = params;
710         if( params == NULL )
711         {
712             plist = ferite_create_parameter_list( 1 );
713             plist[0] = NULL;
714         }
715 
716         while( klass != NULL )
717         {
718             for( ptr = ferite_hash_get( script, klass->class_methods, name ); ptr != NULL; ptr = ptr->next )
719             {
720                 if( ferite_check_params( script, plist, ptr ) == 1 )
721                 {
722                     retf = ptr;
723                     break;
724                 }
725             }
726             if( retf != NULL )
727                 break;
728 
729             if( !doneTest )
730             {
731                 if( strcmp( name, "destructor" ) == 0 )
732                     /* We only look in the current class for the destructor, evil but necessary */
733                     break;
734                 doneTest = FE_TRUE;
735             }
736             klass = klass->parent;
737         }
738         if( params == NULL )
739             ffree( plist );
740     }
741     FE_LEAVE_FUNCTION( retf );
742 }
743 
744 /**
745  * @function ferite_class_has_function
746  * @declaration int ferite_class_has_function(FeriteScript *script, FeriteClass *cls, char *name)
747  * @brief Check to see if a class has the named function
748  * @param FeriteScript *script The script
749  * @param FeriteClass *cls The class to check in
750  * @param char *name The name of the function
751  * @return FE_TRUE on success, FE_FALSE otherwise
752  */
ferite_class_has_function(FeriteScript * script,FeriteClass * cls,char * name)753 int ferite_class_has_function(FeriteScript *script, FeriteClass *cls, char *name)
754 {
755     FeriteFunction *ptr = ferite_class_get_function( script, cls, name );
756 
757     FE_ENTER_FUNCTION;
758     if( ptr == NULL )
759     {
760         FE_LEAVE_FUNCTION( FE_FALSE );
761     }
762     FE_LEAVE_FUNCTION( FE_TRUE );
763 }
764 
765 /**
766  * @function ferite_object_has_var
767  * @declaration int ferite_object_has_var(FeriteScript* script, FeriteObject* obj, char *id)
768  * @brief Check to see whether an object has a certain variable
769  * @param FeriteScript *script The script
770  * @param FeriteObject *obj The object to check
771  * @param char *id The name of the variable
772  * @return If the variable exists FE_TRUE is returned, otherwise FE_FALSE
773  */
ferite_object_has_var(FeriteScript * script,FeriteObject * obj,char * id)774 int ferite_object_has_var(FeriteScript* script, FeriteObject* obj, char *id)
775 {
776     FeriteVariable *ptr = NULL;
777 
778     FE_ENTER_FUNCTION;
779     ptr = ferite_object_get_var( script, obj, id );
780     if( ptr != NULL )
781     {
782         FE_LEAVE_FUNCTION( FE_TRUE );
783     }
784     FE_LEAVE_FUNCTION( FE_FALSE );
785 }
786 
787 /**
788  * @function ferite_object_set_var
789  * @declaration void ferite_object_set_var( FeriteScript* script, FeriteObject* obj, char *id, FeriteVariable *d_var )
790  * @brief Set the value of an objects variable
791  * @param FeriteScript *script The script
792  * @param FeriteObject *obj The object whose member is to be set
793  * @param char *name The name of the variable
794  * @param FeriteVariable *d_var The data used
795  * @description If the variable exists, the value is updated, otherwise a new variable is added to the
796  *              objects variables
797  */
ferite_object_set_var(FeriteScript * script,FeriteObject * obj,char * id,FeriteVariable * d_var)798 void ferite_object_set_var( FeriteScript* script, FeriteObject* obj, char *id, FeriteVariable *d_var )
799 {
800     FeriteVariable *ptr = NULL;
801     FeriteObjectVariable *ov = NULL;
802 
803     FE_ENTER_FUNCTION;
804     UNMARK_VARIABLE_AS_DISPOSABLE( d_var );
805 
806     for( ov = obj->variables; ov != NULL; ov = ov->parent )
807     {
808         ptr = ferite_hash_get( script, ov->variables, id );
809         if( ptr != NULL )
810         {
811             ferite_hash_update( script, ov->variables, id, (void*)d_var );
812             ferite_variable_destroy( script, ptr );
813             FE_LEAVE_FUNCTION(NOWT);
814         }
815     }
816 
817     ferite_hash_add( script, obj->variables->variables, id, d_var );
818     FE_LEAVE_FUNCTION(NOWT);
819 }
820 
821 /**
822  * @function ferite_object_call_super
823  * @declaration void ferite_object_call_super( FeriteScript *script, FeriteObject *object, FeriteVariable **params )
824  * @brief Call the objects parent class's constructor usefull for writing native classes
825  * @param FeriteScript *script The script
826  * @param FeriteVariable The container on which to call the super constructor
827  * @param FeriteVariable **params The parameters to pass to the function call
828  */
ferite_object_call_super(FeriteScript * script,FeriteVariable * container,FeriteVariable ** params)829 FeriteVariable *ferite_object_call_super( FeriteScript *script, FeriteVariable *container, FeriteVariable **params )
830 {
831     FeriteFunction *function = NULL;
832     FeriteClass *old_parent = NULL, *klass = NULL;
833     FeriteVariable *rval = NULL;
834 
835     FE_ENTER_FUNCTION;
836 
837     if( container->type == F_VAR_OBJ )
838       klass = VAO(container)->klass;
839     else if( container->type == F_VAR_CLASS )
840       klass = VAC(container);
841     else
842     {
843         ferite_error( script, 0, "Can't call super on non-class/object container\n" );
844         FE_LEAVE_FUNCTION( NULL );
845     }
846 
847     if( klass->parent == NULL )
848     {
849         /* Our fake variable */
850         rval = ferite_create_void_variable( script, "ferite_call_super", FE_STATIC );
851         MARK_VARIABLE_AS_DISPOSABLE( rval );
852 
853         /* return no parental constructor to call */
854         FE_LEAVE_FUNCTION( rval );
855     }
856 
857     if( container->type == F_VAR_OBJ )
858     {
859         FeriteClass *k = VAO(container)->klass;
860         VAO(container)->klass = k->parent;
861         function = ferite_find_constructor( script, VAO(container), params );
862         VAO(container)->klass = k;
863     }
864     else
865       function = ferite_find_static_constructor( script, klass->parent, params );
866 
867     if( function != NULL )
868     {
869         if( container->type == F_VAR_OBJ )
870         {
871             /* this basically makes the constructor being called play like it's own class */
872             old_parent = VAO(container)->klass;
873             VAO(container)->klass = old_parent->parent;
874         }
875 
876         rval = ferite_call_function( script, VAP(container), NULL, function, params );
877 
878         if( container->type == F_VAR_OBJ )
879         {
880             /* swap it back */
881             VAO(container)->klass = old_parent;
882         }
883     }
884     else
885     {
886         /* No super constructor, lets just return void */
887         rval = ferite_create_void_variable( script, "ferite_call_super", FE_STATIC );
888         MARK_VARIABLE_AS_DISPOSABLE( rval );
889     }
890 
891     FE_LEAVE_FUNCTION( rval );
892 }
893 
894 /**
895  * @function ferite_object_is_sublass
896  * @declaration int ferite_object_is_sublass( FeriteObject *obj, char *name )
897  * @brief See if the object is from a subclass of the named class
898  * @param FeriteObject *obj The object to check
899  * @param char *name The name of the class
900  * @return FE_TRUE if the object is, FE_FALSE otherwise
901  */
ferite_object_is_sublass(FeriteObject * obj,char * name)902 int ferite_object_is_sublass( FeriteObject *obj, char *name )
903 {
904     FeriteClass *ptr;
905 
906     FE_ENTER_FUNCTION;
907     if( obj != NULL )
908     {
909         for( ptr = obj->klass; ptr != NULL; ptr = ptr->parent )
910         {
911             if( strcmp( ptr->name, name ) == 0 )
912             {
913                 FE_LEAVE_FUNCTION( FE_TRUE );
914             }
915         }
916     }
917     FE_LEAVE_FUNCTION( FE_FALSE );
918 }
919 
920 /**
921  * @function ferite_class_is_subclass
922  * @declaration int ferite_class_is_subclass( FeriteClass *klass, FeriteClass *subklass )
923  * @brief Check to see if one class is the sub-class of another
924  * @param FeriteClass *klass The class to check against
925  * @param FeriteClass *subklass The class to check with
926  * @return FE_TRUE if it is a sub-class, FE_FALSE otherwise
927  */
ferite_class_is_subclass(FeriteClass * klass,FeriteClass * subklass)928 int ferite_class_is_subclass( FeriteClass *klass, FeriteClass *subklass )
929 {
930     FeriteClass *ptr = NULL;
931 
932     FE_ENTER_FUNCTION;
933     if( klass != NULL && subklass != NULL )
934     {
935         for( ptr = subklass; ptr != NULL; ptr = ptr->parent )
936         {
937             if( ptr != NULL && ptr == klass )
938             {
939                 FE_LEAVE_FUNCTION( FE_TRUE );
940             }
941         }
942     }
943     FE_LEAVE_FUNCTION( FE_FALSE );
944 }
945 
946 /**
947  * @function ferite_find_constructor
948  * @declaration FeriteFunction *ferite_find_constructor( FeriteScript *script, FeriteClass *klass )
949  * @brief Get a pointer to the first constructor in a class tree
950  * @param FeriteScript *script The script
951  * @param FeriteClass *klass The class to start with
952  * @return A pointer to the function, or NULL otherwise
953  */
ferite_find_constructor(FeriteScript * script,FeriteObject * object,FeriteVariable ** params)954 FeriteFunction *ferite_find_constructor( FeriteScript *script, FeriteObject *object, FeriteVariable **params )
955 {
956     FeriteClass *ptr = NULL;
957     FeriteFunction *func = NULL;
958 
959     FE_ENTER_FUNCTION;
960     func = ferite_object_get_function_for_params( script, object, "constructor", params );
961     FE_LEAVE_FUNCTION( func );
962 }
963 
964 /**
965  * @function ferite_find_static_constructor
966  * @declaration FeriteFunction *ferite_find_static_constructor( FeriteScript *script, FeriteClass *klass )
967  * @brief Get a pointer to the first static constructor in a class tree
968  * @param FeriteScript *script The script
969  * @param FeriteClass *klass The class to start with
970  * @return A pointer to the function, or NULL otherwise
971  */
ferite_find_static_constructor(FeriteScript * script,FeriteClass * klass,FeriteVariable ** params)972 FeriteFunction *ferite_find_static_constructor( FeriteScript *script, FeriteClass *klass, FeriteVariable **params )
973 {
974     FeriteClass *ptr = NULL;
975     FeriteFunction *func = NULL;
976 
977     FE_ENTER_FUNCTION;
978     func = ferite_class_get_function_for_params( script, klass, "constructor", params );
979     FE_LEAVE_FUNCTION( func );
980 }
981 
982 /**
983  * @function ferite_class_dup
984  * @declaration FeriteClass *ferite_class_dup( FeriteScript *script, FeriteClass *klass, FeriteNamespace *container )
985  * @brief Create a complete duplicate of a class
986  * @param FeriteScript *script The script
987  * @param FeriteClass *klass The class to duplicate
988  * @param FeriteNamespace *container A pointer to the namespace that will hold the class
989  * @return A pointer to a new FeriteClass structure
990  */
ferite_class_dup(FeriteScript * script,FeriteClass * klass,FeriteNamespace * container)991 FeriteClass *ferite_class_dup( FeriteScript *script, FeriteClass *klass, FeriteNamespace *container )
992 {
993     FeriteClass *ptr = NULL;
994 
995     FE_ENTER_FUNCTION;
996     if( klass != NULL )
997     {
998         ptr = fmalloc(sizeof(FeriteClass));
999         ptr->name = NULL /*REMOVE fstrdup( klass->name )*/;
1000         ptr->id = klass->id;
1001         /* FIXME: how to get the parent class done correctly */
1002         ptr->parent = NULL;
1003         ptr->class_vars = ferite_duplicate_variable_hash( script, klass->class_vars );
1004         /*REMOVE ptr->variables = ferite_duplicate_variable_hash( script, klass->variables );
1005         ptr->object_methods = ferite_hash_dup( script, klass->object_methods, (void *(*)(FeriteScript *,void *,void*))ferite_function_dup, ptr );
1006         ptr->class_methods = ferite_hash_dup( script, klass->class_methods, (void *(*)(FeriteScript *,void *,void*))ferite_function_dup, ptr );
1007         ptr->next = NULL;*/
1008         ptr->container = container;
1009     }
1010     FE_LEAVE_FUNCTION(ptr);
1011 }
1012 
1013 /**
1014  * @function ferite_state_to_str
1015  * @declaration char *ferite_state_to_str( int state )
1016  * @brief Get a textual description of a object/class members state value
1017  * @param int state The state to give back
1018  * @return A point to a constant string
1019  */
ferite_state_to_str(int state)1020 char *ferite_state_to_str( int state )
1021 {
1022     FE_ENTER_FUNCTION;
1023     switch( state )
1024     {
1025       case FE_ITEM_IS_PRIVATE:
1026         FE_LEAVE_FUNCTION( "private" );
1027         break;
1028       case FE_ITEM_IS_PROTECTED:
1029         FE_LEAVE_FUNCTION( "protected" );
1030         break;
1031       case FE_ITEM_IS_PUBLIC:
1032         FE_LEAVE_FUNCTION( "public" );
1033         break;
1034     }
1035     FE_LEAVE_FUNCTION( "unknown" );
1036 }
1037 
1038 /**
1039  * @function ferite_generate_namespace_fqn
1040  * @declaration char *ferite_generate_namespace_fqn( FeriteScript *script, FeriteNamespace *ns )
1041  * @brief Generate a C string containing the full path to a specified namespace
1042  * @param FeriteScript *script The script context
1043  * @param FeriteNamespace *ns The namespace whose path is required.
1044  * @return A c string containing the path
1045  * @warning It is up to the calling function to free up the memory returned
1046  */
ferite_generate_namespace_fqn(FeriteScript * script,FeriteNamespace * ns)1047 char *ferite_generate_namespace_fqn( FeriteScript *script, FeriteNamespace *ns )
1048 {
1049     char *name = NULL;
1050 
1051     FE_ENTER_FUNCTION;
1052     if( ns->name == NULL )
1053     {
1054         name = fmalloc( 1024 );
1055         memset( name, '\0', 1024 );
1056     }
1057     else
1058     {
1059         name = ferite_generate_namespace_fqn( script, ns->container );
1060         if( strlen( name ) > 0 )
1061             strcat( name, "." );
1062         strcat( name, ns->name );
1063     }
1064     FE_LEAVE_FUNCTION( name );
1065 }
1066 
1067 /**
1068 * @function ferite_generate_class_fqn
1069  * @declaration char *ferite_generate_class_fqn( FeriteScript *script, FeriteClass *klass )
1070  * @brief Generate a C string containing the full path to a specified class
1071  * @param FeriteScript *script The script context
1072  * @param FeriteClass *klass The class whose path is required.
1073  * @return A c string containing the path
1074  * @warning It is up to the calling function to free up the memory returned
1075  */
ferite_generate_class_fqn(FeriteScript * script,FeriteClass * klass)1076 char *ferite_generate_class_fqn( FeriteScript *script, FeriteClass *klass )
1077 {
1078     char *name = ferite_generate_namespace_fqn( script, klass->container );
1079 
1080     FE_ENTER_FUNCTION;
1081     if( strlen( name ) > 0 )
1082         strcat( name, "." );
1083     strcat( name, klass->name );
1084     FE_LEAVE_FUNCTION( name );
1085 }
1086 
1087 /**
1088  * @end
1089  */
1090