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