mirror of git://git.psyced.org/git/psyclpc
1570 lines
42 KiB
C
1570 lines
42 KiB
C
/*------------------------------------------------------------------
|
|
* Struct Implementation
|
|
*
|
|
*------------------------------------------------------------------
|
|
* Structs implement an aggregate type in LPC with a fixed size,
|
|
* which allows access to its members by their name. The LPC compiler
|
|
* tries to resolve struct member accesses at compile time, but
|
|
* a runtime access by name is also possible.
|
|
*
|
|
* In contrast to C structs (and in analogy to Oberon-2 RECORDs),
|
|
* structs allow single inheritance as long as no member names are
|
|
* duplicated.
|
|
*
|
|
* Structs are tied by the program (object) defining them: the LPC
|
|
* compiler considers two 'struct data' definitions as different, if
|
|
* they appear in different objects. Consequently the method to
|
|
* use existing struct definitions is to inherit them.
|
|
*------------------------------------------------------------------
|
|
* Structs are implemented by three data structures: struct_type_t,
|
|
* struct_t, and struct_member_t.
|
|
*
|
|
* A struct_type_t defines the structure of a struct, and every
|
|
* struct instance carries a pointer to the struct_type_t for
|
|
* identification. Ditto, typetests at runtime are carried out
|
|
* using the struct_type_t pointers.
|
|
*
|
|
* The members descriptions held by a struct_type_t are made up
|
|
* from struct_member_t instances.
|
|
*
|
|
* A struct_t finally is the actual struct instance. The driver
|
|
* treats it like the other by-reference structures.
|
|
*
|
|
* The struct_type_t's are organized in a hash table, using the
|
|
* struct_type_t's own name and the name of the defining program
|
|
* as key. The purpose of the hash table is to overcome one disadvantage
|
|
* of the close bond between structs and their programs: if an object
|
|
* is re-compiled, all structs in there would be redefined and thus
|
|
* receive new typeobjects even if their structure didn't change. An
|
|
* accidental update of a central object like /std/types.c could
|
|
* break a mud!
|
|
*
|
|
* To avoid this, the LPC compiler looks up every newly defined struct
|
|
* in the hash table, and if it exists, checks the two definitions
|
|
* for conformity. If they are the same, the newly defined struct type
|
|
* is deleted, and the old struct type is reactived for the new program.
|
|
*
|
|
*
|
|
* -- struct_type_t --
|
|
*
|
|
* struct_type_t
|
|
* {
|
|
* struct_type_t * next;
|
|
* whash_t hash;
|
|
*
|
|
* string_t * name;
|
|
* p_int ref;
|
|
* struct_type_t * base;
|
|
* string_t * prog_name;
|
|
* string_t * unique_name;
|
|
* int32 prog_id;
|
|
* unsigned short num_members;
|
|
* struct_member_t * member;
|
|
* }
|
|
*
|
|
* .next and .hash are used to manage the struct_type_t in the hash
|
|
* table. .next is the link pointer, .hash is the hash created from .name
|
|
* and .prog_name.
|
|
*
|
|
* .name is the name of the struct.
|
|
*
|
|
* .ref is the number of references to the type. Every struct_t created
|
|
* from the type holds one reference; so do the program structures and
|
|
* (if used) the program argument type list.
|
|
*
|
|
* .base is a counted pointer to the super-struct, or NULL if this struct
|
|
* does not inherit.
|
|
*
|
|
* .prog_name and .prog_id are the name and ID of the program defining the
|
|
* struct. They are required to distinguish between identically named
|
|
* structs defined in different programs (even different incarnations of
|
|
* a program). While a new struct definition is compiled, .prog_name is
|
|
* NULL to identify it as incomplete 'prototype'.
|
|
*
|
|
* .unique_name is created only on first request and gives a unique
|
|
* name for the struct composed of .name, .prog_name and .prog_id. It
|
|
* is used by the driver for diagnostics.
|
|
*
|
|
* .num_members is the number of data members in this struct, including
|
|
* inherited members.
|
|
*
|
|
* .member is allocated to hold the .num_member member descriptions
|
|
* in the order they appear in the struct.
|
|
*
|
|
*
|
|
* -- struct_type_t --
|
|
*
|
|
* struct_member_s
|
|
* {
|
|
* string_t * name;
|
|
* vartype_t type;
|
|
* }
|
|
*
|
|
* .name is the name of the member, .type it's compile-time type.
|
|
*
|
|
*
|
|
* -- struct_t --
|
|
*
|
|
* struct_s
|
|
* {
|
|
* struct_type_t * type;
|
|
* p_int ref;
|
|
* wiz_list_t * user;
|
|
* svalue_t member[.type->num_members];
|
|
* }
|
|
*
|
|
* .type is the pointer to the struct_type_t for this struct.
|
|
* .ref is the number of references to this struct instance.
|
|
* .user is the wizlist entry.
|
|
* .member are the member values.
|
|
*
|
|
*------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "driver.h"
|
|
|
|
#ifdef USE_STRUCTS
|
|
|
|
#include "structs.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "array.h"
|
|
#include "exec.h"
|
|
#include "gcollect.h"
|
|
#include "interpret.h"
|
|
#include "main.h"
|
|
#include "mapping.h"
|
|
#include "mstrings.h"
|
|
#include "object.h"
|
|
#include "simulate.h"
|
|
#include "stdstrings.h"
|
|
#include "wiz_list.h"
|
|
#include "xalloc.h"
|
|
|
|
#include "../mudlib/sys/debug_info.h"
|
|
#include "../mudlib/sys/struct_info.h"
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
#define STRUCT_TYPE_MEMSIZE \
|
|
(sizeof(struct_type_t))
|
|
/* Allocation memory size of a struct typeobject <t>.
|
|
*/
|
|
|
|
#define STRUCT_TYPE_MEMBER_MEMSIZE(n) \
|
|
(sizeof(struct_member_t) * (n))
|
|
/* Allocation memory size of a <n> member definition block.
|
|
*/
|
|
|
|
#define STRUCT_MEMSIZE(t) \
|
|
(sizeof(struct_t) + ((t)->type->num_members - 1) * sizeof(svalue_t))
|
|
/* Memory size of a given struct <t>.
|
|
*/
|
|
|
|
#define STRUCT_MEMSIZE_FROM_TYPE(t) \
|
|
(sizeof(struct_t) + ((t)->num_members - 1) * sizeof(svalue_t))
|
|
/* Memory size of struct of type <t>.
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
static struct_type_t ** table = NULL;
|
|
/* The hash table of all published struct types.
|
|
*/
|
|
|
|
static size_t num_types = 0;
|
|
/* Number of published struct types in the table.
|
|
*/
|
|
|
|
static size_t table_size = 0;
|
|
/* Number of buckets in the table.
|
|
*/
|
|
|
|
/* --- Statistics --- */
|
|
|
|
static mp_int num_struct = 0;
|
|
static mp_int num_struct_type = 0;
|
|
/* Number of structs and struct typeobjects.
|
|
*/
|
|
|
|
static mp_int size_struct = 0;
|
|
static mp_int size_struct_type = 0;
|
|
/* Allocated size of structs and struct typeobjects.
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static INLINE whash_t
|
|
hash2 (string_t * pName, string_t * pProgName)
|
|
|
|
/* Compute the hash from <pName> combined with <pProgName>.
|
|
*/
|
|
|
|
{
|
|
whash_t hash;
|
|
|
|
hash = mstr_get_hash(pName);
|
|
hash = whashmem2("\n", 1, 1, hash);
|
|
hash = whashmem2(get_txt(pProgName), mstrsize(pProgName), MSTRING_HASH_LENGTH, hash);
|
|
|
|
return hash;
|
|
} /* hash2() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static struct_type_t *
|
|
find_by_type (struct_type_t * pSType)
|
|
|
|
/* Lookup the struct type <pSType> in the hash table. If it's in there,
|
|
* return its pointer <pSType> and move it to the head of its chain.
|
|
* If the type is not in the table, return NULL.
|
|
*/
|
|
|
|
{
|
|
struct_type_t * this, * prev;
|
|
size_t ix;
|
|
|
|
if (!table || !table_size)
|
|
return NULL;
|
|
|
|
ix = pSType->hash % table_size;
|
|
|
|
prev = NULL;
|
|
this = table[ix];
|
|
while (this != NULL)
|
|
{
|
|
if (this == pSType)
|
|
{
|
|
if (prev != NULL)
|
|
{
|
|
prev->next = this->next;
|
|
this->next = table[ix];
|
|
table[ix] = this;
|
|
}
|
|
break;
|
|
}
|
|
|
|
prev = this;
|
|
this = this->next;
|
|
}
|
|
|
|
return this;
|
|
} /* find_by_type() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static struct_type_t *
|
|
find_by_name (string_t * pName, string_t * pProgName, whash_t hash)
|
|
|
|
/* Lookup the struct type named <pName> and defined in program <pProgName>
|
|
* in the hash table. <hash> is the hash of the two names combined.
|
|
* If found, move it to the head of its chain and return the type pointer.
|
|
* If not found, return NULL.
|
|
*
|
|
* In either case, *<pHash> is set to the hash value of the entry if != NULL.
|
|
*/
|
|
|
|
{
|
|
struct_type_t * this, * prev;
|
|
size_t ix;
|
|
|
|
if (!table || !table_size)
|
|
return NULL;
|
|
|
|
ix = hash % table_size;
|
|
|
|
prev = NULL;
|
|
this = table[ix];
|
|
while (this != NULL)
|
|
{
|
|
if (this->hash == hash
|
|
&& mstreq(this->name, pName)
|
|
&& mstreq(this->prog_name, pProgName)
|
|
)
|
|
{
|
|
if (prev != NULL)
|
|
{
|
|
prev->next = this->next;
|
|
this->next = table[ix];
|
|
table[ix] = this;
|
|
}
|
|
break;
|
|
}
|
|
|
|
prev = this;
|
|
this = this->next;
|
|
}
|
|
|
|
return this;
|
|
} /* find_by_name() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static void
|
|
add_type (struct_type_t * pSType)
|
|
|
|
/* Add the struct type <pSType> to the hash table.
|
|
* The <pSType>->hash must already been computed.
|
|
*/
|
|
|
|
{
|
|
size_t ix;
|
|
|
|
#ifdef DEBUG
|
|
if (find_by_type(pSType))
|
|
fatal("struct type %s (%s %"PRId32") already in table.\n"
|
|
, get_txt(struct_t_name(pSType))
|
|
, get_txt(struct_t_pname(pSType))
|
|
, struct_t_pid(pSType)
|
|
);
|
|
#endif
|
|
|
|
if (!table)
|
|
{
|
|
memsafe(table = pxalloc(sizeof(*table)), sizeof(*table)
|
|
, "struct type hash table");
|
|
table_size = 1;
|
|
table[0] = pSType;
|
|
pSType->next = NULL;
|
|
num_types = 1;
|
|
return;
|
|
}
|
|
|
|
ix = pSType->hash % table_size;
|
|
pSType->next = table[ix];
|
|
table[ix] = pSType;
|
|
|
|
num_types++;
|
|
|
|
/* If chain lengths grow too much (more than 2 entries per bucket
|
|
* on average), increase the table size
|
|
*/
|
|
if (num_types > 2 * table_size && table_size * 2 < MAX_WHASH)
|
|
{
|
|
size_t new_size = 2 * table_size;
|
|
struct_type_t ** table2;
|
|
|
|
table2 = pxalloc(new_size * sizeof(*table2));
|
|
if (table2)
|
|
{
|
|
memset(table2, 0, new_size * sizeof(*table2));
|
|
|
|
/* Rehash all existing entries */
|
|
for (ix = 0; ix < table_size; ix++)
|
|
{
|
|
struct_type_t * this;
|
|
|
|
while (NULL != (this = table[ix]))
|
|
{
|
|
size_t ix2;
|
|
|
|
table[ix] = this->next;
|
|
ix2 = this->hash % new_size;
|
|
|
|
this->next = table2[ix2];
|
|
table2[ix2] = this;
|
|
}
|
|
} /* for() */
|
|
|
|
pfree(table);
|
|
table = table2;
|
|
table_size = new_size;
|
|
} /* if (table2) */
|
|
} /* if (check for rehash condition) */
|
|
|
|
} /* add_type() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static void
|
|
remove_type (struct_type_t * pSType)
|
|
|
|
/* Remove the struct type <pSType> from the hash table, if it's in there.
|
|
*/
|
|
|
|
{
|
|
size_t ix;
|
|
|
|
if (!find_by_type(pSType))
|
|
return;
|
|
|
|
/* pSType is now at the head of it's chain */
|
|
|
|
ix = pSType->hash % table_size;
|
|
table[ix] = pSType->next;
|
|
|
|
num_types--;
|
|
} /* remove_type() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
struct_t *
|
|
struct_new (struct_type_t *pSType)
|
|
|
|
/* Create a new empty struct instance for struct type <pSType>
|
|
* and return it.
|
|
* Return NULL when out of memory.
|
|
*
|
|
* The returned struct will have one reference.
|
|
*/
|
|
|
|
{
|
|
struct_t *pStruct;
|
|
size_t size;
|
|
|
|
#ifdef DEBUG
|
|
if (pSType == NULL)
|
|
fatal("NULL typeobject pointer passed to struct_new().\n");
|
|
#endif
|
|
|
|
size = STRUCT_MEMSIZE_FROM_TYPE(pSType);
|
|
pStruct = xalloc(size);
|
|
if (pStruct != NULL)
|
|
{
|
|
unsigned short num;
|
|
svalue_t *svp;
|
|
|
|
pStruct->ref = 1;
|
|
pStruct->type = ref_struct_type(pSType);
|
|
if (current_object)
|
|
pStruct->user = current_object->user;
|
|
else
|
|
pStruct->user = &default_wizlist_entry;
|
|
for (num = pSType->num_members, svp = pStruct->member
|
|
; num-- > 0 ; svp++)
|
|
{
|
|
put_number(svp, 0);
|
|
}
|
|
|
|
num_struct++;
|
|
size_struct += size;
|
|
pStruct->user->struct_total += size;
|
|
}
|
|
|
|
return pStruct;
|
|
} /* struct_new() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
struct_type_t *
|
|
struct_new_prototype ( string_t *name )
|
|
|
|
/* Create a new prototype struct typeobject from the given data.
|
|
* The references from the data are adopted, and the result is the
|
|
* new typeobject with one reference.
|
|
* When an error occurs, NULL is returned and the input data is freed.
|
|
*/
|
|
|
|
{
|
|
struct_type_t * pSType;
|
|
|
|
pSType = xalloc(STRUCT_TYPE_MEMSIZE);
|
|
if (pSType != NULL)
|
|
{
|
|
pSType->ref = 1;
|
|
pSType->name = name;
|
|
pSType->prog_name = NULL;
|
|
pSType->unique_name = NULL;
|
|
pSType->hash = 0;
|
|
pSType->prog_id = 0;
|
|
pSType->num_members = 0;
|
|
pSType->member = NULL;
|
|
pSType->base = NULL;
|
|
|
|
num_struct_type++;
|
|
size_struct_type += STRUCT_TYPE_MEMSIZE;
|
|
}
|
|
else
|
|
{
|
|
free_mstring(name);
|
|
}
|
|
|
|
return pSType;
|
|
} /* struct_new_prototype() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
struct_type_t *
|
|
struct_fill_prototype ( struct_type_t *type
|
|
, string_t *prog_name
|
|
, int32 prog_id
|
|
, struct_type_t *base
|
|
, int num_members
|
|
, struct_member_t *member)
|
|
|
|
/* Complete the struct prototype <type> with the given data and return
|
|
* its pointer. If <member> is NULL, the member entries are left empty.
|
|
* The references from the data are adopted, and the result is the
|
|
* new typeobject with one reference.
|
|
* When an error occurs, NULL is returned and the input data is freed,
|
|
* including the prototype.
|
|
*/
|
|
|
|
{
|
|
struct_member_t * pMembers;
|
|
unsigned short num;
|
|
|
|
#ifdef DEBUG
|
|
if (type == NULL)
|
|
fatal("NULL typeobject pointer passed to struct_fill_prototype().\n");
|
|
if (type->prog_name != NULL)
|
|
fatal("Non-prototype typeobject passed to struct_fill_prototype().\n");
|
|
#endif
|
|
|
|
if (num_members != 0)
|
|
pMembers = xalloc(STRUCT_TYPE_MEMBER_MEMSIZE(num_members));
|
|
else
|
|
pMembers = NULL;
|
|
|
|
if (num_members == 0 || pMembers != NULL)
|
|
{
|
|
type->prog_name = prog_name;
|
|
type->next = NULL;
|
|
type->hash = hash2(type->name, type->prog_name);
|
|
type->prog_id = prog_id;
|
|
type->base = base;
|
|
type->num_members = num_members;
|
|
|
|
if (member != NULL)
|
|
{
|
|
for (num = 0; num < num_members; num++)
|
|
{
|
|
pMembers[num] = member[num];
|
|
}
|
|
}
|
|
else
|
|
memset(pMembers, 0, STRUCT_TYPE_MEMBER_MEMSIZE(num_members));
|
|
|
|
type->member = pMembers;
|
|
|
|
size_struct_type += STRUCT_TYPE_MEMBER_MEMSIZE(num_members);
|
|
}
|
|
else
|
|
{
|
|
free_mstring(type->name);
|
|
free_mstring(prog_name);
|
|
|
|
if (base)
|
|
free_struct_type(base);
|
|
|
|
if (member != NULL)
|
|
{
|
|
for (num = 0; num < num_members; num++)
|
|
{
|
|
free_struct_member_data(&member[num]);
|
|
}
|
|
}
|
|
|
|
xfree(type);
|
|
|
|
num_struct_type--;
|
|
size_struct_type -= STRUCT_TYPE_MEMSIZE;
|
|
}
|
|
|
|
return type;
|
|
} /* struct_fill_prototype() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
struct_type_t *
|
|
struct_new_type ( string_t *name
|
|
, string_t *prog_name
|
|
, int32 prog_id
|
|
, struct_type_t *base
|
|
, int num_members
|
|
, struct_member_t *member)
|
|
|
|
/* Create a new struct typeobject from the given data and return
|
|
* its pointer. If <member> is NULL, the member entries are left empty.
|
|
* The references from the data are adopted, and the result is the
|
|
* new typeobject with one reference.
|
|
* When an error occurs, NULL is returned and the input data is freed.
|
|
*/
|
|
|
|
{
|
|
struct_type_t * pSType;
|
|
|
|
pSType = struct_new_prototype(name);
|
|
if (pSType != NULL)
|
|
pSType = struct_fill_prototype(pSType, prog_name, prog_id, base, num_members, member);
|
|
|
|
return pSType;
|
|
} /* struct_new_type() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
struct_type_t *
|
|
struct_lookup_type ( struct_type_t * pSType )
|
|
|
|
/* Lookup the hash table if a struct type with the same name and program
|
|
* name as <pSType> has been published already.
|
|
* Return the (uncounted) pointer to the found type, or NULL if not found.
|
|
*/
|
|
|
|
{
|
|
#ifdef DEBUG
|
|
if (pSType == NULL)
|
|
fatal("NULL typeobject pointer passed to struct_lookup_type().\n");
|
|
if (pSType->prog_name == NULL)
|
|
fatal("prototype typeobject passed to struct_lookup_type().\n");
|
|
#endif
|
|
|
|
return find_by_name(pSType->name, pSType->prog_name, pSType->hash);
|
|
} /* struct_lookup_type() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
struct_publish_type ( struct_type_t * pSType )
|
|
|
|
/* Add the struct type <pSType> to the hash table ("publish it"), replacing
|
|
* an existing entry if necessary.
|
|
* It is safe to publish the same type multiple times.
|
|
*/
|
|
|
|
{
|
|
struct_type_t * old;
|
|
|
|
#ifdef DEBUG
|
|
if (pSType == NULL)
|
|
fatal("NULL typeobject pointer passed to struct_publish_type().\n");
|
|
if (pSType->prog_name == NULL)
|
|
fatal("prototype typeobject passed to struct_publish_type().\n");
|
|
#endif
|
|
|
|
old = find_by_name(pSType->name, pSType->prog_name, pSType->hash);
|
|
if (old)
|
|
remove_type(old);
|
|
add_type(pSType);
|
|
} /* struct_publish_type() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
Bool
|
|
struct_type_equivalent (struct_type_t * pSType1, struct_type_t *pSType2)
|
|
|
|
/* Check if the two types <pSType1> and <pSType2> are of equivalent
|
|
* structure.
|
|
*/
|
|
|
|
{
|
|
int num;
|
|
|
|
if (pSType1 == pSType2)
|
|
return MY_TRUE;
|
|
if (!mstreq(struct_t_name(pSType1), struct_t_name(pSType2))
|
|
|| !mstreq(struct_t_pname(pSType1), struct_t_pname(pSType2))
|
|
|| pSType1->base != pSType2->base
|
|
)
|
|
return MY_FALSE;
|
|
if (struct_t_pid(pSType1) == struct_t_pid(pSType2))
|
|
return MY_TRUE;
|
|
|
|
/* The basics match, now check the members */
|
|
if (pSType1->num_members != pSType2->num_members)
|
|
return MY_FALSE;
|
|
|
|
for (num = 0; num < pSType1->num_members; num++)
|
|
{
|
|
struct_member_t * pMember1 = &pSType1->member[num];
|
|
struct_member_t * pMember2 = &pSType2->member[num];
|
|
if (!mstreq(pMember1->name, pMember2->name)
|
|
|| pMember1->type.type != pMember2->type.type
|
|
)
|
|
return MY_FALSE;
|
|
|
|
/* For structs members, a deep comparison is necessary.
|
|
*/
|
|
if ((pMember1->type.type & PRIMARY_TYPE_MASK) == TYPE_STRUCT)
|
|
{
|
|
Bool rc;
|
|
vartype_t t_member1 = pMember1->type;
|
|
vartype_t t_member2 = pMember2->type;
|
|
|
|
/* Prevent recursion by blocking out the structs */
|
|
pMember1->type.type = TYPE_NUMBER;
|
|
pMember2->type.type = TYPE_NUMBER;
|
|
|
|
rc = struct_type_equivalent( t_member1.t_struct
|
|
, t_member2.t_struct
|
|
);
|
|
|
|
/* Restore the original member types */
|
|
pMember1->type = t_member1;
|
|
pMember2->type = t_member2;
|
|
|
|
if (!rc)
|
|
return MY_FALSE;
|
|
}
|
|
}
|
|
|
|
return MY_TRUE;
|
|
} /* struct_type_equivalent() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
struct_type_update ( struct_type_t * pSType
|
|
, struct_type_t * pOld
|
|
, struct_type_t * pNew)
|
|
|
|
/* In struct type <pSType>, replace all references to <pOld> by <pNew>.
|
|
*/
|
|
|
|
{
|
|
int num;
|
|
|
|
if (pOld == pNew)
|
|
return;
|
|
|
|
if (pSType->base == pOld)
|
|
{
|
|
free_struct_type(pSType->base);
|
|
pSType->base = ref_struct_type(pNew);
|
|
}
|
|
|
|
for (num = 0; num < pSType->num_members; num++)
|
|
{
|
|
struct_member_t * pMember = &pSType->member[num];
|
|
|
|
if ((pMember->type.type & PRIMARY_TYPE_MASK) == TYPE_STRUCT
|
|
&& pMember->type.t_struct == pOld
|
|
)
|
|
{
|
|
free_struct_type(pMember->type.t_struct);
|
|
pMember->type.t_struct = ref_struct_type(pNew);
|
|
}
|
|
}
|
|
} /* struct_type_update() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
struct_t *
|
|
struct_new_anonymous (int num_members)
|
|
|
|
/* Create an empty anonymous struct instance with <num_members>
|
|
* and return its pointer.
|
|
* Return NULL when out of memory.
|
|
*
|
|
* The returned struct will have one reference.
|
|
*/
|
|
|
|
{
|
|
struct_type_t * pType;
|
|
struct_t * pStruct;
|
|
int i;
|
|
char buf[100];
|
|
Bool gotError;
|
|
|
|
(void) ref_mstring(STR_ANONYMOUS);
|
|
(void) ref_mstring(STR_ANONYMOUS);
|
|
pType = struct_new_type( STR_ANONYMOUS
|
|
, STR_ANONYMOUS
|
|
, 0
|
|
, NULL, num_members, NULL);
|
|
if (pType == NULL)
|
|
return NULL;
|
|
|
|
pStruct = struct_new(pType);
|
|
free_struct_type(pType);
|
|
/* struct_new() added one ref, but since this is an anonymous
|
|
* struct, the struct instance will hold the only ref.
|
|
*/
|
|
|
|
if (pStruct == NULL)
|
|
{
|
|
free_struct_type(pType);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Create default members */
|
|
gotError = MY_FALSE;
|
|
for (i = 0; i < num_members; i++)
|
|
{
|
|
sprintf(buf, "m-%d", i);
|
|
pType->member[i].name = new_tabled(buf);
|
|
if (!pType->member[i].name)
|
|
{
|
|
debug_message("(%s:%d) Out of memory (%zu bytes) for member name\n"
|
|
, __FILE__, __LINE__, strlen(buf)
|
|
);
|
|
gotError = MY_TRUE;
|
|
break;
|
|
}
|
|
pType->member[i].type.type = TYPE_ANY;
|
|
pType->member[i].type.t_struct = NULL;
|
|
}
|
|
|
|
if (gotError)
|
|
{
|
|
free_struct(pStruct);
|
|
pStruct = NULL;
|
|
}
|
|
|
|
return pStruct;
|
|
} /* struct_new_anonymous() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
struct_free_empty (struct_t *pStruct)
|
|
|
|
/* Free the struct <pStruct> (which holds no valid svalues) and all referenced
|
|
* data. However, the refcount of <pStruct>->type is not changed.
|
|
* The refcount of <pStruct> is ignored, as all known callers take care
|
|
* of it (in particular a swap during GC leaves behind strange values).
|
|
*/
|
|
|
|
{
|
|
#ifdef DEBUG
|
|
if (!pStruct)
|
|
fatal("NULL pointer passed to struct_free_empty().\n");
|
|
|
|
if (!pStruct->user)
|
|
fatal("No wizlist pointer for struct in struct_free_empty().");
|
|
#endif
|
|
|
|
num_struct--;
|
|
size_struct -= STRUCT_MEMSIZE(pStruct);
|
|
pStruct->user->struct_total -= STRUCT_MEMSIZE(pStruct);
|
|
|
|
/* Don't free_struct_type(pStruct->type) */
|
|
|
|
xfree(pStruct);
|
|
} /* struct_free_empty() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
struct_free (struct_t *pStruct)
|
|
|
|
/* Free the struct <pStruct> and all referenced data.
|
|
*/
|
|
|
|
{
|
|
unsigned short num;
|
|
struct_type_t * pSType;
|
|
|
|
#ifdef DEBUG
|
|
if (!pStruct)
|
|
fatal("NULL pointer passed to struct_free().\n");
|
|
|
|
if (!pStruct->user)
|
|
fatal("No wizlist pointer for struct in struct_free().");
|
|
|
|
if (pStruct->ref != 0)
|
|
fatal("Struct with %"PRIdPINT" refs passed to struct_free().\n"
|
|
, pStruct->ref);
|
|
#endif
|
|
|
|
for (num = struct_size(pStruct); num-- > 0; )
|
|
{
|
|
free_svalue(&pStruct->member[num]);
|
|
}
|
|
|
|
pSType = pStruct->type;
|
|
struct_free_empty(pStruct); /* needs a valid .type */
|
|
free_struct_type(pSType);
|
|
} /* struct_free() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
struct_free_type (struct_type_t *pSType)
|
|
|
|
/* Free the struct typeobject <pSType> and all referenced data.
|
|
*/
|
|
|
|
{
|
|
unsigned short num;
|
|
|
|
#ifdef DEBUG
|
|
if (!pSType)
|
|
fatal("NULL pointer passed to struct_free_type().\n");
|
|
|
|
if (pSType->ref != 0)
|
|
fatal("struct typeobject with %"PRIdPINT" refs passed to struct_free_type().\n"
|
|
, pSType->ref);
|
|
#endif
|
|
|
|
remove_type(pSType); /* In case it was published */
|
|
|
|
num_struct_type--;
|
|
size_struct_type -= STRUCT_TYPE_MEMSIZE
|
|
+ STRUCT_TYPE_MEMBER_MEMSIZE(pSType->num_members);
|
|
|
|
free_mstring(pSType->name);
|
|
if (pSType->prog_name)
|
|
free_mstring(pSType->prog_name);
|
|
if (pSType->unique_name)
|
|
free_mstring(pSType->unique_name);
|
|
if (pSType->base)
|
|
free_struct_type(pSType->base);
|
|
|
|
for (num = 0; num < pSType->num_members; num++)
|
|
{
|
|
free_struct_member_data(&pSType->member[num]);
|
|
}
|
|
|
|
if (pSType->member)
|
|
xfree(pSType->member);
|
|
|
|
xfree(pSType);
|
|
} /* struct_free_type() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
struct_type_t *
|
|
struct_find ( string_t * name, program_t * prog )
|
|
|
|
/* Find struct <name> in the given program <prog> and return it's type
|
|
* pointer.
|
|
* Return NULL if not found.
|
|
*/
|
|
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < prog->num_structs; i++)
|
|
{
|
|
if (mstreq(prog->struct_defs[i].type->name, name))
|
|
return prog->struct_defs[i].type;
|
|
}
|
|
|
|
return NULL;
|
|
} /* struct_find() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
int
|
|
struct_find_member ( struct_type_t * ptype, string_t * name )
|
|
|
|
/* Find the struct member <name> for struct <ptype> and return its index.
|
|
* Return -1 if not found.
|
|
*/
|
|
|
|
{
|
|
int member;
|
|
struct_member_t * pmember;
|
|
|
|
if (struct_t_size(ptype) < 1)
|
|
return -1;
|
|
|
|
for (member = 0, pmember = ptype->member
|
|
; member < struct_t_size(ptype)
|
|
; member++, pmember++
|
|
)
|
|
{
|
|
if (mstreq(pmember->name, name))
|
|
return member;
|
|
}
|
|
|
|
return -1;
|
|
} /* struct_find_member() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
struct_check_for_destr ( struct_t * pStruct )
|
|
|
|
/* Remove all references to destructed objects from <pStruct>.
|
|
*/
|
|
|
|
{
|
|
int member, num_members;
|
|
svalue_t * svp;
|
|
|
|
num_members = struct_size(pStruct);
|
|
|
|
for (member = 0, svp = pStruct->member
|
|
; member < num_members
|
|
; member++, svp++
|
|
)
|
|
{
|
|
if (destructed_object_ref(svp))
|
|
{
|
|
free_svalue(svp);
|
|
put_number(svp, 0);
|
|
}
|
|
}
|
|
} /* struct_check_for_destr() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
mp_int
|
|
total_struct_size (strbuf_t *sbuf, Bool verbose)
|
|
|
|
/* Add the struct handler status suitable for printing to <sbuf>.
|
|
* Result is the amount of memory held by the struct handler.
|
|
*/
|
|
|
|
{
|
|
if (!verbose)
|
|
{
|
|
strbuf_addf(sbuf, "Structs:\t\t\t%8"PRIdMPINT" %9"PRIdMPINT
|
|
" (%"PRIdMPINT" types: %"PRIdMPINT")\n"
|
|
, num_struct, size_struct
|
|
, num_struct_type, size_struct_type
|
|
);
|
|
}
|
|
|
|
return size_struct + size_struct_type;
|
|
} /* total_struct_size() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
struct_dinfo_status (svalue_t *svp, int value)
|
|
|
|
/* Return the struct information for debug_info(DINFO_DATA, DID_STATUS).
|
|
* <svp> points to the svalue block for the result, this function fills in
|
|
* the spots for the object table.
|
|
* If <value> is -1, <svp> points indeed to a value block; other it is
|
|
* the index of the desired value and <svp> points to a single svalue.
|
|
*/
|
|
|
|
{
|
|
#define ST_NUMBER(which,code) \
|
|
if (value == -1) svp[which].u.number = code; \
|
|
else if (value == which) svp->u.number = code
|
|
|
|
ST_NUMBER(DID_ST_STRUCTS, num_struct);
|
|
ST_NUMBER(DID_ST_STRUCTS_SIZE, size_struct);
|
|
|
|
ST_NUMBER(DID_ST_STRUCT_TYPES, num_struct_type);
|
|
ST_NUMBER(DID_ST_STRUCT_TYPES_SIZE, size_struct_type);
|
|
|
|
#undef ST_NUMBER
|
|
} /* string_dinfo_status() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
string_t *
|
|
struct_t_unique_name (struct_type_t *pSType)
|
|
|
|
/* Also aliased to: struct_unique_name(struct_t *)
|
|
*
|
|
* Compose and return the unique name of struct type <pSType>.
|
|
* The returned string reference is not counted.
|
|
*
|
|
* The value save/restore routines rely on the format of this string.
|
|
*/
|
|
|
|
{
|
|
char name[MAXPATHLEN+256];
|
|
|
|
if (pSType->unique_name)
|
|
return pSType->unique_name;
|
|
|
|
snprintf(name, sizeof(name), "%s %s #%"PRId32
|
|
, get_txt(struct_t_name(pSType))
|
|
, get_txt(struct_t_pname(pSType))
|
|
, struct_t_pid(pSType)
|
|
);
|
|
pSType->unique_name = new_mstring(name);
|
|
|
|
return pSType->unique_name;
|
|
} /* struct_t_unique_name() */
|
|
|
|
/*=========================================================================*/
|
|
/* GC SUPPORT */
|
|
|
|
#ifdef GC_SUPPORT
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
clear_struct_type_ref (struct_type_t * pSType)
|
|
|
|
/* Clear all references held by struct typeobject <pSType>
|
|
*/
|
|
|
|
{
|
|
unsigned short num;
|
|
|
|
if (pSType->ref != 0)
|
|
{
|
|
clear_memory_reference(pSType);
|
|
if (pSType->member)
|
|
clear_memory_reference(pSType->member);
|
|
|
|
pSType->ref = 0;
|
|
pSType->name->info.ref = 0;
|
|
if (pSType->prog_name)
|
|
pSType->prog_name->info.ref = 0;
|
|
if (pSType->unique_name)
|
|
pSType->unique_name->info.ref = 0;
|
|
if (pSType->base)
|
|
clear_struct_type_ref(pSType->base);
|
|
|
|
for (num = struct_t_size(pSType); num-- > 0; )
|
|
{
|
|
pSType->member[num].name->info.ref = 0;
|
|
clear_vartype_ref(&pSType->member[num].type);
|
|
}
|
|
}
|
|
|
|
} /* clear_struct_type_ref() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
clear_struct_ref (struct_t * pStruct)
|
|
|
|
/* Clear all references held by struct <pStruct>
|
|
*/
|
|
|
|
{
|
|
if (pStruct->ref != 0)
|
|
{
|
|
clear_memory_reference(pStruct);
|
|
pStruct->ref = 0;
|
|
clear_struct_type_ref(pStruct->type);
|
|
if (struct_size(pStruct))
|
|
{
|
|
clear_ref_in_vector(pStruct->member, struct_size(pStruct));
|
|
}
|
|
}
|
|
} /* clear_struct_ref() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
count_struct_type_ref (struct_type_t * pSType)
|
|
|
|
/* Count all references held by struct typeobject <pSType>
|
|
*/
|
|
|
|
{
|
|
unsigned short num;
|
|
|
|
pSType->ref++;
|
|
|
|
if (test_memory_reference(pSType))
|
|
{
|
|
note_malloced_block_ref(pSType);
|
|
if (pSType->member)
|
|
note_malloced_block_ref(pSType->member);
|
|
|
|
count_ref_from_string(pSType->name);
|
|
if (pSType->prog_name)
|
|
count_ref_from_string(pSType->prog_name);
|
|
if (pSType->unique_name)
|
|
count_ref_from_string(pSType->unique_name);
|
|
if (pSType->base)
|
|
count_struct_type_ref(pSType->base);
|
|
|
|
for (num = struct_t_size(pSType); num-- > 0; )
|
|
{
|
|
count_ref_from_string(pSType->member[num].name);
|
|
count_vartype_ref(&pSType->member[num].type);
|
|
}
|
|
}
|
|
} /* count_struct_type_ref() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
count_struct_ref (struct_t * pStruct)
|
|
|
|
/* Count all references held by struct <pStruct>
|
|
*/
|
|
|
|
{
|
|
pStruct->ref++;
|
|
if (test_memory_reference(pStruct))
|
|
{
|
|
note_malloced_block_ref(pStruct);
|
|
count_struct_type_ref(pStruct->type);
|
|
if (struct_size(pStruct))
|
|
{
|
|
count_ref_in_vector(pStruct->member, struct_size(pStruct));
|
|
}
|
|
}
|
|
} /* count_struct_ref() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
clear_tabled_struct_refs (void)
|
|
|
|
/* Clear all references held by the struct types in the hash table.
|
|
*/
|
|
|
|
{
|
|
|
|
if (table && table_size)
|
|
{
|
|
size_t num;
|
|
|
|
for (num = 0; num < table_size; num++)
|
|
{
|
|
struct_type_t * pSType;
|
|
for (pSType = table[num]; pSType != NULL; pSType = pSType->next)
|
|
clear_struct_type_ref(pSType);
|
|
}
|
|
}
|
|
} /* clear_tabled_struct_refs() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
remove_unreferenced_structs (void)
|
|
|
|
/* Free all structs in the table which are not marked as referenced.
|
|
* This function must be called before freeing all unreferenced strings!
|
|
*/
|
|
|
|
{
|
|
size_t num;
|
|
|
|
if (!table || !table_size)
|
|
return;
|
|
|
|
for (num = 0; num < table_size; num++)
|
|
{
|
|
struct_type_t * this, * prev;
|
|
|
|
for (prev = NULL, this = table[num]; this != NULL; )
|
|
{
|
|
if (!test_memory_reference(this))
|
|
{
|
|
prev = this;
|
|
this = this->next;
|
|
}
|
|
else
|
|
{
|
|
struct_type_t * next = this->next;
|
|
int i;
|
|
|
|
if (prev)
|
|
prev->next = next;
|
|
else
|
|
table[num] = next;
|
|
|
|
num_types--;
|
|
|
|
/* Now we deallocate the memory for all the struct type
|
|
* structure. We rely on the strings (member names) still
|
|
* being around - other referenced memory we ignore.
|
|
*/
|
|
num_struct_type--;
|
|
size_struct_type -= STRUCT_TYPE_MEMBER_MEMSIZE(this->num_members);
|
|
|
|
dprintf2(gcollect_outfd, "struct type %x '%s' was left "
|
|
"unreferenced, freeing now.\n"
|
|
, (p_int) this
|
|
, (p_int) get_txt(this->name)
|
|
);
|
|
|
|
/* Reference all strings and free them, to avoid unnecessary
|
|
* 'string unreferenced' diagnostics.
|
|
*/
|
|
count_ref_from_string(this->name);
|
|
free_mstring(this->name);
|
|
if (this->prog_name)
|
|
{
|
|
count_ref_from_string(this->prog_name);
|
|
free_mstring(this->prog_name);
|
|
}
|
|
if (this->unique_name)
|
|
{
|
|
count_ref_from_string(this->unique_name);
|
|
free_mstring(this->unique_name);
|
|
}
|
|
|
|
for (i = struct_t_size(this); i-- > 0; )
|
|
{
|
|
count_ref_from_string(this->member[i].name);
|
|
free_mstring(this->member[i].name);
|
|
}
|
|
|
|
/* Reference the memory (to update its flags) and free it */
|
|
if (this->member)
|
|
{
|
|
note_malloced_block_ref(this->member);
|
|
xfree(this->member);
|
|
}
|
|
note_malloced_block_ref(this);
|
|
xfree(this);
|
|
|
|
this = next;
|
|
}
|
|
}
|
|
} /* for (num) */
|
|
|
|
} /* remove_unreferenced_structs() */
|
|
|
|
#endif /* GC_SUPPORT */
|
|
|
|
/*=========================================================================*/
|
|
|
|
/* EFUNS */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
svalue_t *
|
|
x_map_struct (svalue_t *sp, int num_arg)
|
|
|
|
/* EFUN map() on structs
|
|
*
|
|
* mixed * map(struct arg, string func, string|object ob, mixed extra...)
|
|
* mixed * map(struct arg, closure cl, mixed extra...)
|
|
* mixed * map(struct arr, mapping map)
|
|
*
|
|
* Map the elements of <arr> through a filter defined by the other
|
|
* arguments, and return an array of the elements returned by the filter.
|
|
*
|
|
* The filter can be a function call:
|
|
*
|
|
* <obj>-><fun>(elem, <extra>...)
|
|
*
|
|
* or a mapping query:
|
|
*
|
|
* <map>[elem]
|
|
*
|
|
* In the mapping case, if <map>[elem] does not exist, the original
|
|
* value is returned in the result.
|
|
*
|
|
* <obj> can both be an object reference or a filename. If <ob> is
|
|
* omitted, or neither an object nor a string, then this_object() is used.
|
|
*
|
|
* As a bonus, all references to destructed objects in <arr> are replaced
|
|
* by proper 0es.
|
|
*/
|
|
|
|
{
|
|
struct_t *st;
|
|
struct_t *res;
|
|
svalue_t *arg;
|
|
svalue_t *v, *w, *x;
|
|
mp_int cnt;
|
|
|
|
inter_sp = sp;
|
|
arg = sp - num_arg + 1;
|
|
|
|
st = arg->u.strct;
|
|
cnt = (mp_int)struct_size(st);
|
|
|
|
if (arg[1].type == T_MAPPING)
|
|
{
|
|
/* --- Map through mapping --- */
|
|
|
|
mapping_t *m;
|
|
|
|
if (num_arg > 2) {
|
|
inter_sp = sp;
|
|
errorf("Too many arguments to map_array()\n");
|
|
}
|
|
m = arg[1].u.map;
|
|
|
|
res = struct_new(st->type);
|
|
if (!res)
|
|
errorf("(map_struct) Out of memory: struct[%"PRIdMPINT"] for result\n", cnt);
|
|
push_struct(inter_sp, res); /* In case of errors */
|
|
|
|
for (w = st->member, x = res->member; --cnt >= 0; w++, x++)
|
|
{
|
|
if (destructed_object_ref(w))
|
|
assign_svalue(w, &const0);
|
|
|
|
v = get_map_value(m, w);
|
|
if (v == &const0)
|
|
assign_svalue_no_free(x, w);
|
|
else
|
|
assign_svalue_no_free(x, v);
|
|
}
|
|
|
|
free_svalue(arg+1); /* the mapping */
|
|
sp = arg;
|
|
}
|
|
else
|
|
{
|
|
/* --- Map through function call --- */
|
|
|
|
callback_t cb;
|
|
int error_index;
|
|
|
|
error_index = setup_efun_callback(&cb, arg+1, num_arg-1);
|
|
if (error_index >= 0)
|
|
{
|
|
vefun_bad_arg(error_index+2, arg);
|
|
/* NOTREACHED */
|
|
return arg;
|
|
}
|
|
inter_sp = sp = arg+1;
|
|
put_callback(sp, &cb);
|
|
num_arg = 2;
|
|
|
|
res = struct_new(st->type);
|
|
if (!res)
|
|
errorf("(map_struct) Out of memory: struct[%"PRIdMPINT"] for result\n", cnt);
|
|
push_struct(inter_sp, res); /* In case of errors */
|
|
|
|
/* Loop through arr and res, mapping the values from arr */
|
|
for (w = st->member, x = res->member; --cnt >= 0; w++, x++)
|
|
{
|
|
if (current_object->flags & O_DESTRUCTED)
|
|
continue;
|
|
|
|
if (destructed_object_ref(w))
|
|
assign_svalue(w, &const0);
|
|
|
|
if (!callback_object(&cb))
|
|
errorf("object used by map_array destructed");
|
|
|
|
push_svalue(w);
|
|
|
|
v = apply_callback(&cb, 1);
|
|
if (v)
|
|
{
|
|
transfer_svalue_no_free(x, v);
|
|
v->type = T_INVALID;
|
|
}
|
|
}
|
|
|
|
free_callback(&cb);
|
|
}
|
|
|
|
/* The arguments have been removed already, now just replace
|
|
* the struct on the stack with the result.
|
|
*/
|
|
free_struct(st);
|
|
arg->u.strct = res; /* Keep svalue type T_STRUCT */
|
|
|
|
return arg;
|
|
} /* x_map_struct () */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
svalue_t *
|
|
f_baseof (svalue_t *sp)
|
|
|
|
/* EFUN baseof()
|
|
*
|
|
* int baseof(struct b, struct s)
|
|
*
|
|
* Test if the type of struct <b> is a base of struct <s> (the values of
|
|
* <b> and <s> are irrelevant). Results is:
|
|
* 0: <b> is not a base of <s>, nor is <b> of equal type as <s> (though <s>
|
|
* might be a base of <b>).
|
|
* 1: <b> is a true base of <s>
|
|
* 2: <b> and <s> are the same struct type
|
|
*/
|
|
|
|
{
|
|
struct_type_t *base, *st;
|
|
int rc;
|
|
|
|
/* Get the arguments from the stack */
|
|
base = sp[-1].u.strct->type;
|
|
st = sp[0].u.strct->type;
|
|
|
|
if (st == base)
|
|
rc = 2;
|
|
else
|
|
{
|
|
rc = 0;
|
|
|
|
while ((st = st->base) != NULL)
|
|
{
|
|
if (st == base)
|
|
{
|
|
rc = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Remove the arguments and push the result */
|
|
free_svalue(sp); sp--;
|
|
free_svalue(sp);
|
|
|
|
put_number(sp, rc);
|
|
|
|
return sp;
|
|
} /* f_baseof() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static vector_t *
|
|
single_struct_info (struct_type_t * st, Bool include_base)
|
|
|
|
/* Create the struct_info() result array for a single struct and return it.
|
|
* If <include_base> is TRUE, all members defined in a possible base
|
|
* struct are included as if they were top-level members.
|
|
*/
|
|
|
|
{
|
|
vector_t * rc;
|
|
size_t offset;
|
|
size_t i;
|
|
|
|
offset = 0;
|
|
if (!include_base && st->base != NULL)
|
|
offset = struct_t_size(st->base);
|
|
rc = allocate_array(struct_t_size(st) - offset + SI_MAX);
|
|
put_ref_string(&rc->item[SI_NAME], struct_t_name(st));
|
|
put_ref_string(&rc->item[SI_PROG_NAME], st->prog_name);
|
|
put_number(&rc->item[SI_PROG_ID], st->prog_id);
|
|
for (i = offset; i < struct_t_size(st); i++)
|
|
{
|
|
vector_t * member;
|
|
struct_member_t * pMember;
|
|
|
|
pMember = &st->member[i];
|
|
|
|
member = allocate_array(SIM_MAX);
|
|
put_array(&rc->item[SI_MEMBER+i-offset], member);
|
|
put_ref_string(&member->item[SIM_NAME], pMember->name);
|
|
put_number(&member->item[SIM_TYPE], pMember->type.type);
|
|
if ((pMember->type.type & PRIMARY_TYPE_MASK) == T_STRUCT)
|
|
put_ref_string(&member->item[SIM_EXTRA]
|
|
, struct_t_name(pMember->type.t_struct)
|
|
);
|
|
}
|
|
|
|
return rc;
|
|
} /* single_struct_info() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
svalue_t *
|
|
f_struct_info (svalue_t *sp)
|
|
|
|
/* EFUN struct_info()
|
|
*
|
|
* mixed * struct_info(struct s, int type)
|
|
*
|
|
* Return an array with information about the struct <s>. The
|
|
* type of information returned is determined by <type>.
|
|
*/
|
|
|
|
{
|
|
struct_type_t *st;
|
|
int rtype;
|
|
svalue_t result;
|
|
|
|
/* Get the arguments from the stack */
|
|
st = sp[-1].u.strct->type;
|
|
rtype = sp[0].u.number;
|
|
|
|
/* Get the basic result information */
|
|
put_array(&result, single_struct_info(st, rtype != SINFO_NESTED));
|
|
|
|
if (st->base != NULL)
|
|
{
|
|
/* Depending on the <rtype> argument, determine the how to handle
|
|
* the base structs (if any).
|
|
*/
|
|
switch(rtype)
|
|
{
|
|
case SINFO_FLAT:
|
|
put_ref_string(&result.u.vec->item[SI_BASE], struct_t_name(st->base));
|
|
break;
|
|
|
|
case SINFO_NESTED:
|
|
{
|
|
svalue_t *rc;
|
|
|
|
for ( rc = &result.u.vec->item[SI_BASE], st = st->base
|
|
; st != NULL
|
|
; st = st->base, rc = &rc->u.vec->item[SI_BASE]
|
|
)
|
|
{
|
|
put_array(rc, single_struct_info(st, MY_FALSE));
|
|
}
|
|
break;
|
|
|
|
}
|
|
default:
|
|
free_svalue(&result);
|
|
errorf("Bad arg 2 to struct_info(): illegal value %"PRIdPINT"\n"
|
|
, sp->u.number);
|
|
/* NOTREACHED */
|
|
return sp;
|
|
}
|
|
}
|
|
|
|
free_svalue(sp); sp--; /* type */
|
|
free_svalue(sp); /* struct */
|
|
|
|
/* Assign the result */
|
|
transfer_svalue_no_free(sp, &result);
|
|
|
|
return sp;
|
|
} /* f_struct_info() */
|
|
|
|
/***************************************************************************/
|
|
|
|
#endif /* USE_STRUCTS */
|