1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header$";
4 #endif
5 
6 /*
7  *   Copyright (c) 2000, 2002 Michael J. Roberts.  All Rights Reserved.
8  *
9  *   Please see the accompanying license file, LICENSE.TXT, for information
10  *   on using and copying this software.
11  */
12 /*
13 Name
14   vmintcls.cpp - T3 metaclass - intrinsic class
15 Function
16 
17 Notes
18 
19 Modified
20   03/08/00 MJRoberts  - Creation
21 */
22 
23 #include <stdlib.h>
24 #include <assert.h>
25 
26 #include "vmtype.h"
27 #include "vmobj.h"
28 #include "vmglob.h"
29 #include "vmintcls.h"
30 #include "vmmeta.h"
31 #include "vmfile.h"
32 #include "vmlst.h"
33 #include "vmstack.h"
34 #include "vmtobj.h"
35 
36 
37 /* ------------------------------------------------------------------------ */
38 /*
39  *   statics
40  */
41 
42 /* metaclass registration object */
43 static CVmMetaclassClass metaclass_reg_obj;
44 CVmMetaclass *CVmObjClass::metaclass_reg_ = &metaclass_reg_obj;
45 
46 
47 /* ------------------------------------------------------------------------ */
48 /*
49  *   create dynamically using stack arguments
50  */
create_from_stack(VMG_ const uchar ** pc_ptr,uint argc)51 vm_obj_id_t CVmObjClass::create_from_stack(VMG_ const uchar **pc_ptr,
52                                            uint argc)
53 {
54     /* it is illegal to create this type of object dynamically */
55     err_throw(VMERR_ILLEGAL_NEW);
56     return VM_INVALID_OBJ;
57 }
58 
59 /*
60  *   create with no initial contents
61  */
create(VMG_ int in_root_set)62 vm_obj_id_t CVmObjClass::create(VMG_ int in_root_set)
63 {
64     vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE);
65     new (vmg_ id) CVmObjClass();
66     return id;
67 }
68 
69 /*
70  *   create with a given dependency index
71  */
create_dyn(VMG_ uint meta_idx)72 vm_obj_id_t CVmObjClass::create_dyn(VMG_ uint meta_idx)
73 {
74     vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE);
75     new (vmg_ id) CVmObjClass(vmg_ FALSE, meta_idx, id);
76     return id;
77 }
78 
79 /*
80  *   create with a given dependency index
81  */
CVmObjClass(VMG_ int in_root_set,uint meta_idx,vm_obj_id_t self)82 CVmObjClass::CVmObjClass(VMG_ int in_root_set, uint meta_idx,
83                          vm_obj_id_t self)
84 {
85     /* calls of this form can't come from the image file */
86     assert(!in_root_set);
87 
88     /* allocate the extension */
89     ext_ = (char *)G_mem->get_var_heap()->alloc_mem(8, this);
90 
91     /* set up the extension - write the length and dependency index */
92     oswp2(ext_, 8);
93     oswp2(ext_ + 2, meta_idx);
94     oswp4(ext_ + 4, VM_INVALID_OBJ);
95 
96     /* register myself with the metaclass table */
97     register_meta(vmg_ self);
98 }
99 
100 /*
101  *   notify of deletion
102  */
notify_delete(VMG_ int in_root_set)103 void CVmObjClass::notify_delete(VMG_ int in_root_set)
104 {
105     /* free our extension */
106     if (ext_ != 0 && !in_root_set)
107         G_mem->get_var_heap()->free_mem(ext_);
108 }
109 
110 /* set a property */
set_prop(VMG_ CVmUndo * undo,vm_obj_id_t self,vm_prop_id_t prop,const vm_val_t * val)111 void CVmObjClass::set_prop(VMG_ CVmUndo *undo,
112                            vm_obj_id_t self, vm_prop_id_t prop,
113                            const vm_val_t *val)
114 {
115     vm_obj_id_t mod_obj;
116 
117     /* if I have a modifier object, pass the setprop to the modifier */
118     if ((mod_obj = get_mod_obj()) != VM_INVALID_OBJ)
119     {
120         /* set the property in the modifier object */
121         vm_objp(vmg_ mod_obj)->set_prop(vmg_ undo, mod_obj, prop, val);
122     }
123     else
124     {
125         /* if we don't have a modifier, we can't set the property */
126         err_throw(VMERR_INVALID_SETPROP);
127     }
128 }
129 
130 /*
131  *   Get a list of our properties
132  */
build_prop_list(VMG_ vm_obj_id_t self,vm_val_t * retval)133 void CVmObjClass::build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval)
134 {
135     vm_obj_id_t mod_obj;
136     vm_meta_entry_t *entry;
137     size_t my_prop_cnt;
138     size_t mod_prop_cnt;
139     vm_val_t mod_val;
140     CVmObjList *lst;
141     CVmObjList *mod_lst;
142 
143     /* presume we won't find any static properties of our own */
144     my_prop_cnt = 0;
145 
146     /* get my metaclass table entry */
147     entry = get_meta_entry(vmg0_);
148 
149     /* if we have an entry, count the properties */
150     if (entry != 0)
151         my_prop_cnt = list_class_props(vmg_ self, entry, 0, 0, FALSE);
152 
153     /* if we have a modifier object, get its property list */
154     if ((mod_obj = get_mod_obj()) != VM_INVALID_OBJ)
155     {
156         /* get the modifier's property list - we'll add it to our own */
157         vm_objp(vmg_ mod_obj)->build_prop_list(vmg_ self, &mod_val);
158 
159         /* get the result as a list object, properly cast */
160         mod_lst = (CVmObjList *)vm_objp(vmg_ mod_val.val.obj);
161 
162         /*
163          *   As an optimization, if we don't have any properties of our own
164          *   to add to the modifier list, just return the modifier list
165          *   directly (thus avoiding unnecessarily creating another copy of
166          *   the list with no changes).
167          */
168         if (my_prop_cnt == 0)
169         {
170             /* the modifier list is the entire return value */
171             *retval = mod_val;
172             return;
173         }
174 
175         /* get the size of the modifier list */
176         mod_prop_cnt = vmb_get_len(mod_lst->get_as_list());
177     }
178     else
179     {
180         /*
181          *   we have no modifier object - for the result list, we simply
182          *   need our own list, so set the modifier list to nil
183          */
184         mod_val.set_nil();
185         mod_prop_cnt = 0;
186     }
187 
188     /* for gc protection, push the modifier's list */
189     G_stk->push(&mod_val);
190 
191     /*
192      *   Allocate a list big enough to hold the modifier's list plus our own
193      *   list.
194      */
195     retval->set_obj(CVmObjList::
196                     create(vmg_ FALSE, my_prop_cnt + mod_prop_cnt));
197 
198     /* push the return value list for gc protection */
199     G_stk->push(retval);
200 
201     /* get it as a list object, properly cast */
202     lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj);
203 
204     /* start the list with our own properties */
205     if (entry != 0)
206         list_class_props(vmg_ self, entry, lst, 0, FALSE);
207 
208     /* copy the modifier list into the results, if there is a modifier list */
209     if (mod_prop_cnt != 0)
210         lst->cons_copy_elements(my_prop_cnt, mod_lst->get_as_list());
211 
212     /* done with the gc protection */
213     G_stk->discard(2);
214 }
215 
216 /*
217  *   List my metaclass's properties.  Returns the number of properties we
218  *   find.  We'll add the properties to the given list; if the list is null,
219  *   we'll simply return the count.
220  */
list_class_props(VMG_ vm_obj_id_t self,vm_meta_entry_t * entry,CVmObjList * lst,size_t starting_idx,int static_only)221 size_t CVmObjClass::list_class_props(VMG_ vm_obj_id_t self,
222                                       vm_meta_entry_t *entry,
223                                       CVmObjList *lst, size_t starting_idx,
224                                       int static_only)
225 {
226     size_t i;
227     size_t cnt;
228     size_t idx;
229 
230     /* count the valid entries */
231     for (i = 1, idx = starting_idx, cnt = 0 ;
232          i <= entry->func_xlat_cnt_ ; ++i)
233     {
234         vm_prop_id_t prop;
235         vm_val_t val;
236         vm_obj_id_t source_obj;
237 
238         /* get this property */
239         prop = entry->xlat_func(i);
240 
241         /*
242          *   If this one's valid, check to see if it's we're interested in
243          *   it.  If we want only statics, include it only if it's
244          *   implemented as a static method on this intrinsic class object;
245          *   otherwise, include it unconditionally.
246          *
247          *   To determine if the property is implemented as a static, call
248          *   the property on self without an argc pointer - this is the
249          *   special form of the call which merely retrieves the value of the
250          *   property without evaluating it.  If the property is defined on
251          *   the object, and the source of the definition is self, include
252          *   this one in the list of directly-defined properties.
253          */
254         if (prop != VM_INVALID_PROP
255             && (!static_only
256                 || (get_prop(vmg_ prop, &val, self, &source_obj, 0)
257                     && source_obj == self)))
258         {
259             /* we're interested - add it to the list if we have one */
260             if (lst != 0)
261             {
262                 /* set up a value containing the property pointer */
263                 val.set_propid(prop);
264 
265                 /* add it to the list at the next free slot */
266                 lst->cons_set_element(idx, &val);
267 
268                 /* advance to the next slot */
269                 ++idx;
270             }
271 
272             /* count it */
273             ++cnt;
274         }
275     }
276 
277     /* return the number of properties we found */
278     return cnt;
279 }
280 
281 
282 /*
283  *   get a property
284  */
get_prop(VMG_ vm_prop_id_t prop,vm_val_t * val,vm_obj_id_t self,vm_obj_id_t * source_obj,uint * argc)285 int CVmObjClass::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val,
286                           vm_obj_id_t self, vm_obj_id_t *source_obj,
287                           uint *argc)
288 {
289     vm_meta_entry_t *entry;
290 
291     /*
292      *   pass the property request as a static property of the metaclass
293      *   with which we're associated
294      */
295     entry = get_meta_entry(vmg0_);
296     if (entry != 0 && entry->meta_->call_stat_prop(vmg_ val, 0, argc, prop))
297     {
298         /* the metaclass object handled it, so we are the definer */
299         *source_obj = self;
300         return TRUE;
301     }
302 
303     /*
304      *   Try handling it through the modifier object, if we have one.  Note
305      *   that we must search our own intrinsic superclass chain, because our
306      *   own true intrinsic class is 'intrinsic class,' but we want to expose
307      *   the nominal superclass hierarchy of the intrinsic class itself (for
308      *   example, we want to search List->Collection->Object, not
309      *   List->IntrinsicClass->Object).
310      */
311     if (get_prop_from_mod(vmg_ prop, val, self, source_obj, argc))
312         return TRUE;
313 
314     /* inherit default handling */
315     return CVmObject::get_prop(vmg_ prop, val, self, source_obj, argc);
316 }
317 
318 /*
319  *   Get a property from an intrinsic class modifier object.  Look up the
320  *   property in my own modifier, and recursively in my superclass modifiers.
321  */
get_prop_from_mod(VMG_ vm_prop_id_t prop,vm_val_t * val,vm_obj_id_t self,vm_obj_id_t * source_obj,uint * argc)322 int CVmObjClass::get_prop_from_mod(VMG_ vm_prop_id_t prop, vm_val_t *val,
323                                    vm_obj_id_t self, vm_obj_id_t *source_obj,
324                                    uint *argc)
325 {
326     vm_obj_id_t sc;
327     vm_obj_id_t mod_obj;
328 
329     /* if we have a modifier object, look it up in the modifier */
330     if ((mod_obj = get_mod_obj()) != VM_INVALID_OBJ
331         && vm_objp(vmg_ mod_obj)->get_prop(vmg_ prop, val, mod_obj,
332                                            source_obj, argc))
333     {
334         /* got it - return it from the modifier */
335         return TRUE;
336     }
337 
338     /*
339      *   it's not in our modifier(s); check with any intrinsic superclass
340      *   modifiers
341      */
342     if (get_superclass_count(vmg_ self) != 0
343         && (sc = get_superclass(vmg_ self, 0)) != VM_INVALID_OBJ)
344     {
345         /* we have a superclass - check it recursively */
346         return ((CVmObjClass *)vm_objp(vmg_ sc))
347             ->get_prop_from_mod(vmg_ prop, val, sc, source_obj, argc);
348     }
349 
350     /*
351      *   we didn't find it, and we have no superclass, so there's nowhere
352      *   else to look - return failure
353      */
354     return FALSE;
355 }
356 
357 /*
358  *   Find the intrinsic class which the given modifier object modifies.  This
359  *   can only be used with a modifier that modifies me or one of my
360  *   superclasses.
361  */
find_mod_src_obj(VMG_ vm_obj_id_t self,vm_obj_id_t mod_obj)362 vm_obj_id_t CVmObjClass::find_mod_src_obj(VMG_ vm_obj_id_t self,
363                                           vm_obj_id_t mod_obj)
364 {
365     vm_obj_id_t my_mod_obj;
366     vm_obj_id_t sc;
367 
368     /*
369      *   Is this one of my modifier objects?  It is if it's my most
370      *   specialized modifier object (i.e., get_mod_obj()), or if my most
371      *   specialized modifier object descends from the given object.
372      */
373     my_mod_obj = get_mod_obj();
374     if (my_mod_obj != VM_INVALID_OBJ
375         && (mod_obj == my_mod_obj
376             || vm_objp(vmg_ my_mod_obj)->is_instance_of(vmg_ mod_obj)))
377     {
378         /* it's one of mine, so I'm the intrinsic class mod_obj modifies */
379         return self;
380     }
381 
382     /*
383      *   It's not one of mine, so check my superclasses recursively.  If we
384      *   have no direct superclass, we've failed to find the object.
385      */
386     if (get_superclass_count(vmg_ self) == 0
387         || (sc = get_superclass(vmg_ self, 0)) == VM_INVALID_OBJ)
388         return VM_INVALID_OBJ;
389 
390     /* ask the superclass to find the modifier */
391     return ((CVmObjClass *)vm_objp(vmg_ sc))
392         ->find_mod_src_obj(vmg_ sc, mod_obj);
393 }
394 
395 /*
396  *   Get my metaclass table entry
397  */
get_meta_entry(VMG0_) const398 vm_meta_entry_t *CVmObjClass::get_meta_entry(VMG0_) const
399 {
400     uint meta_idx;
401 
402     /* get my metaclass table index */
403     meta_idx = get_meta_idx();
404 
405     /* look up my metaclass table entry, if we have one */
406     if (meta_idx < G_meta_table->get_count())
407         return G_meta_table->get_entry(meta_idx);
408     else
409         return 0;
410 }
411 
412 /*
413  *   save to a file
414  */
save_to_file(VMG_ class CVmFile * fp)415 void CVmObjClass::save_to_file(VMG_ class CVmFile *fp)
416 {
417     size_t len;
418 
419     /* write our data */
420     len = osrp2(ext_);
421     fp->write_bytes(ext_, len);
422 }
423 
424 /*
425  *   restore from a file
426  */
restore_from_file(VMG_ vm_obj_id_t self,CVmFile * fp,CVmObjFixup *)427 void CVmObjClass::restore_from_file(VMG_ vm_obj_id_t self,
428                                     CVmFile *fp, CVmObjFixup *)
429 {
430     size_t len;
431 
432     /* read the length */
433     len = fp->read_uint2();
434 
435     /* free any existing extension */
436     if (ext_ != 0)
437     {
438         G_mem->get_var_heap()->free_mem(ext_);
439         ext_ = 0;
440     }
441 
442     /* allocate the space */
443     ext_ = (char *)G_mem->get_var_heap()->alloc_mem(len, this);
444 
445     /* store our length */
446     vmb_put_len(ext_, len);
447 
448     /*
449      *   read the contents (note that we've already read the length prefix,
450      *   so subtract it out of the total remaining to be read)
451      */
452     fp->read_bytes(ext_ + VMB_LEN, len - VMB_LEN);
453 
454     /* register myself with the metaclass table */
455     register_meta(vmg_ self);
456 }
457 
458 /* load from an image file */
load_from_image(VMG_ vm_obj_id_t self,const char * ptr,size_t siz)459 void CVmObjClass::load_from_image(VMG_ vm_obj_id_t self,
460                                   const char *ptr, size_t siz)
461 {
462     /* save a pointer to the image file data as our extension */
463     ext_ = (char *)ptr;
464 
465     /* make sure the length is valid */
466     if (siz < 8)
467         err_throw(VMERR_INVAL_METACLASS_DATA);
468 
469     /* register myself */
470     register_meta(vmg_ self);
471 }
472 
473 /*
474  *   reset to the initial load state
475  */
reset_to_image(VMG_ vm_obj_id_t self)476 void CVmObjClass::reset_to_image(VMG_ vm_obj_id_t self)
477 {
478     /* re-register myself for the re-load */
479     register_meta(vmg_ self);
480 }
481 
482 /*
483  *   Register myself with the metaclass dependency table - this establishes
484  *   the association between the metaclass in the dependency table and this
485  *   instance, so that the metaclass knows about the instance that
486  *   represents it.
487  */
register_meta(VMG_ vm_obj_id_t self)488 void CVmObjClass::register_meta(VMG_ vm_obj_id_t self)
489 {
490     vm_meta_entry_t *entry;
491 
492     /* get my metaclass table entry */
493     entry = get_meta_entry(vmg0_);
494 
495     /*
496      *   if we have a valid entry, store a reference to myself in the
497      *   metaclass table - this will let the metaclass that we represent find
498      *   us when asked for its class object
499      */
500     if (entry != 0)
501         entry->class_obj_ = self;
502 }
503 
504 /*
505  *   Get the number of superclasses
506  */
get_superclass_count(VMG_ vm_obj_id_t) const507 int CVmObjClass::get_superclass_count(VMG_ vm_obj_id_t) const
508 {
509     vm_meta_entry_t *entry;
510 
511     /* get my metaclass table entry */
512     entry = get_meta_entry(vmg0_);
513 
514     /*
515      *   if we have a valid entry, ask the metaclass object to tell us the
516      *   superclass count
517      */
518     if (entry != 0)
519         return entry->meta_->get_supermeta_count(vmg0_);
520     else
521         return 0;
522 }
523 
524 /*
525  *   Get a superclass
526  */
get_superclass(VMG_ vm_obj_id_t,int sc_idx) const527 vm_obj_id_t CVmObjClass::get_superclass(VMG_ vm_obj_id_t, int sc_idx) const
528 {
529     vm_meta_entry_t *entry;
530 
531     /* get my metaclass table entry */
532     entry = get_meta_entry(vmg0_);
533 
534     /*
535      *   if we have a valid entry, ask the metaclass object to retrieve the
536      *   superclass
537      */
538     if (entry != 0)
539         return entry->meta_->get_supermeta(vmg_ sc_idx);
540     else
541         return VM_INVALID_OBJ;
542 }
543 
544 /*
545  *   Determine if I'm an instance of the given object
546  */
is_instance_of(VMG_ vm_obj_id_t obj)547 int CVmObjClass::is_instance_of(VMG_ vm_obj_id_t obj)
548 {
549     vm_meta_entry_t *entry;
550 
551     /* get my metaclass table entry */
552     entry = get_meta_entry(vmg0_);
553 
554     /* if we have a valid entry, ask the metaclass object */
555     if (entry != 0)
556     {
557         /* ask the metaclass if it's an instance of the given object */
558         return entry->meta_->is_meta_instance_of(vmg_ obj);
559     }
560     else
561     {
562         /* not a valid metaclass - we can't make sense of this */
563         return FALSE;
564     }
565 }
566