Implement LPC Type objects

Add a new type 'lpctype' to contain a type object, describing a compile-time
type, together with a decltype() operator and to_lpctype() and check_type()
efuns.
This commit is contained in:
Alexander Motzkau 2022-09-25 22:20:32 +02:00
parent 73ed64c46b
commit fb691fa2ea
40 changed files with 1487 additions and 183 deletions

31
doc/LPC/decltype Normal file
View file

@ -0,0 +1,31 @@
NAME
decltype
SYNTAX
decltype(<expr>)
DESCRIPTION
The expression will compiled just to determine the resulting type.
The expression itself will not be executed. The result of this
operator is the lpctype value representing the expression result.
NOTE
The result of this operator depends on a lot of factors, for example
if any type information is preserved for any inherited programs that
are referenced in the expression. But it also depends on the type
inference capabilities of the LPC compiler. So future versions of
LDMud might have different results on the same expression.
EXAMPLES
int var;
decltype(var) /* result: [int] */
string fun();
decltype(fun()) /* result: [string], fun() will not be called. */
HISTORY
Introduced in LDMud 3.6.7.
SEE ALSO
lpctypes(LPC)

48
doc/LPC/lpctypes Normal file
View file

@ -0,0 +1,48 @@
CONCEPT
lpctypes
INTRODUCTION
An lpctype variable stores an LPC type like int or string*.
They are mainly used in the check_types() efun, but also for
introspection with functionlist() and variable_list().
DEFINITION
An lpctype literal can be created either by writing the type
in brackets or using the decltype(LPC) operator:
lpctype t1 = [int];
lpctype t2 = decltype(t1); /* t2 will be [lpctype] */
The type in brackets can be any type declaration, similar to
the type declarations of variable or function result.
OPERATIONS
lpctypes support the following operations:
t1 | t2
Create a union type of both types.
t1 & t2
Create the intersection of both types. If the intersection
is empty [void] will be returned.
t1 in t2
Yields 1 if t1 is a subset of t2.
EXAMPLE
void process(mixed value)
{
if (check_type(value, [int|float]))
process_number(value);
else
process_anything(value);
}
HISTORY
The type was introduced in LDMud 3.6.7.
SEE ALSO
check_type(E), decltype(LPC)

View file

@ -148,6 +148,8 @@ DESCRIPTION
o struct A collection of values. See structs(LPC).
o lpctype A type itself. See lpctypes(LPC).
o union A range of types, either of which the variable
can contain at runtime. See unions(LPC).
@ -168,5 +170,6 @@ HISTORY
SEE ALSO
alists(LPC), arrays(LPC), mappings(LPC), closures(LPC), coroutines(LPC),
objects(LPC), structs(LPC), unions(LPC), typeof(E), get_type_info(E),
inheritance(LPC), pragma(LPC), modifiers(LPC), escape(LPC)
objects(LPC), structs(LPC), unions(LPC), lpctypes(E), typeof(E),
get_type_info(E), inheritance(LPC), pragma(LPC), modifiers(LPC),
escape(LPC)

View file

@ -27,6 +27,9 @@ BESCHREIBUNG
RETURN_FUNCTION_NAME liefert den Funktionsnamen
RETURN_FUNCTION_FLAGS liefert die Flags der Funktion
RETURN_FUNCTION_TYPE liefert den Rueckgabetyp der Funktion
als Zahl.
RETURN_FUNCTION_LPCTYPE liefert den Rueckgabetyp der Funktion
als lpctype.
RETURN_FUNCTION_NUMARG liefert die Anzahl moeglicher
Argumente.
@ -51,6 +54,9 @@ BESCHREIBUNG
TYPE_MOD_NOMASK die Funktion ist nomask deklariert
TYPE_MOD_PUBLIC die Funktion ist public deklariert
GESCHICHTE
LDMud 3.6.7 fuehrte RETURN_FUNCTION_LPCTYPE ein.
SIEHE AUCH
inherit_list(E), function_exists(E), variable_list(E),
call_resolved(E)

View file

@ -31,7 +31,8 @@ BESCHREIBUNG
Auswahl der gesammelten Information:
RETURN_FUNCTION_NAME liefert den Namen der Variablen
RETURN_FUNCTION_FLAGS liefert die Flags der Variablen (s. unten)
RETURN_FUNCTION_TYPE liefert den Rueckgabetyp
RETURN_FUNCTION_TYPE liefert den Variablentyp als Zahl.
RETURN_FUNCTION_LPCTYPE liefert den Variablentyp als lpctype.
RETURN_VARIABLE_VALUE liefert den Wert der Variablen
Auswahl der Variablen, die ausgewertet werden:
@ -54,6 +55,7 @@ BESCHREIBUNG
GESCHICHTE
Eingefuehrt in LDMud 3.2.10.
LDMud 3.6.7 fuehrte RETURN_FUNCTION_LPCTYPE ein.
SIEHE AUCH
inherit_list(E), functionlist(E), variable_exists(E)

16
doc/efun/check_type Normal file
View file

@ -0,0 +1,16 @@
SYNOPSIS
int check_type(mixed arg, lpctype type)
DESCRIPTION
Returns 1 if the first argument <arg> fulfills the type <type>,
0 otherwise.
This check is similar to runtime type checks. The target type
doesn't need to match exactly, but a variable of that type should
be able to hold the argument.
HISTORY
Introduced in LDMud 3.6.7
SEE ALSO
get_type_info(E), lpctypes(LPC)

View file

@ -25,7 +25,8 @@ DESCRIPTION
Control of returned information:
RETURN_FUNCTION_NAME include the function name
RETURN_FUNCTION_FLAGS include the function flags
RETURN_FUNCTION_TYPE include the return type
RETURN_FUNCTION_TYPE include the return type as an integer
RETURN_FUNCTION_LPCTYPE include the return type as lpctype value
RETURN_FUNCTION_NUMARG include the number of arguments.
The name RETURN_FUNCTION_ARGTYPE is defined but not implemented.
@ -46,6 +47,8 @@ DESCRIPTION
TYPE_MOD_NO_MASK function is nomask
TYPE_MOD_PUBLIC function is public
HISTORY
LDMud 3.6.7 introduced RETURN_FUNCTION_LPCTYPE.
SEE ALSO
inherit_list(E), function_exists(E), variable_list(E),

View file

@ -59,4 +59,4 @@ HISTORY
LDMud 3.3.708 added flag setting '4' for lfun/context closures.
SEE ALSO
debug_info(E), typeof(E), to_object(E)
debug_info(E), typeof(E), to_object(E), check_type(E)

View file

@ -5,6 +5,7 @@ SYNOPSIS
mixed * to_array(quotedarray)
mixed * to_array(mixed *)
mixed * to_array(struct)
mixed * to_array(lpctype)
(int*)<value>
@ -21,6 +22,9 @@ DESCRIPTION
Structs are converted into a normal array.
Union lpc types are split into their union member types
(in no particular order).
BUGS
The cast notation only works if the precise type of <value>
is known at compile-time. This will not be fixed - use the

12
doc/efun/to_lpctype Normal file
View file

@ -0,0 +1,12 @@
SYNOPSIS
lpctype to_lpctype(string type)
DESCRIPTION
Interprets the given string as an lpc type. This efun basically
returns the same type as the [<type>] literal.
HISTORY
Introduced in LDMud 3.6.7.
SEE ALSO
to_string(E), lpctypes(LPC)

View file

@ -27,7 +27,8 @@ DESCRIPTION
Control of returned information:
RETURN_FUNCTION_NAME include the variable name
RETURN_FUNCTION_FLAGS include the variable flags
RETURN_FUNCTION_TYPE include the return type
RETURN_FUNCTION_TYPE include the variable type as an integer
RETURN_FUNCTION_LPCTYPE include the variable type as an lpctype
RETURN_VARIABLE_VALUE include the variable value
Control of listed variables:
@ -50,6 +51,7 @@ DESCRIPTION
HISTORY
Introduced in LDMud 3.2.10.
LDMud 3.6.7 introduced RETURN_FUNCTION_LPCTYPE.
SEE ALSO
inherit_list(E), functionlist(E), variable_exists(E)

View file

@ -226,11 +226,12 @@ class SValuePrinter:
T_LWOBJECT = 0xd
T_COROUTINE = 0xe
T_PYTHON = 0xf
T_CALLBACK = 0x10
T_ERROR_HANDLER = 0x11
T_BREAK_ADDR = 0x12
T_ARG_FRAME = 0x13
T_NULL = 0x14
T_LPCTYPE = 0x10
T_CALLBACK = 0x11
T_ERROR_HANDLER = 0x12
T_BREAK_ADDR = 0x13
T_ARG_FRAME = 0x14
T_NULL = 0x15
LVALUE_UNPROTECTED = 0x00
LVALUE_UNPROTECTED_CHAR = 0x01
@ -260,6 +261,7 @@ class SValuePrinter:
T_LWOBJECT: "T_LWOBJECT",
T_COROUTINE: "T_COROUTINE",
T_PYTHON: "T_PYTHON",
T_LPCTYPE: "T_LPCTYPE",
T_CALLBACK: "T_CALLBACK",
T_ERROR_HANDLER: "T_ERROR_HANDLER",
T_BREAK_ADDR: "T_BREAK_ADDR",
@ -358,9 +360,10 @@ class SValuePrinter:
return [(".u.strct", val["u"]["strct"])]
elif stype == self.T_PYTHON:
return [(".u.generic", val["u"]["generic"])]
elif stype == self.T_LPCTYPE:
return [(".u.lpctype", val["u"]["lpctype"])]
elif stype == self.T_CALLBACK:
return [(".u.cb", val["u"]["cb"]),
(".x.extern_args", val["x"]["extern_args"])]
return [(".u.cb", val["u"]["cb"])]
elif stype == self.T_ERROR_HANDLER:
return [(".u.error_handler", val["u"]["error_handler"])]
elif stype == self.T_BREAK_ADDR:
@ -495,6 +498,8 @@ class TypePrinter:
TYPE_SYMBOL = 8
TYPE_QUOTED_ARRAY = 9
TYPE_BYTES = 10
TYPE_COROUTINE = 11
TYPE_LPCTYPE = 12
OBJECT_REGULAR = 0
OBJECT_LIGHTWEIGHT= 1
@ -511,6 +516,8 @@ class TypePrinter:
TYPE_SYMBOL: "symbol",
TYPE_QUOTED_ARRAY: "quoted_array",
TYPE_BYTES: "bytes",
TYPE_COROUTINE: "coroutine",
TYPE_LPCTYPE: "lpctype",
}
def __init__(self, val):

View file

@ -31,7 +31,7 @@
#define RETURN_FUNCTION_NAME 0x01
#define RETURN_FUNCTION_FLAGS 0x02
#define RETURN_FUNCTION_TYPE 0x04
#define RETURN_FUNCTION_LPCTYPE 0x20
/* Additional return value flag types for functionlist() */
@ -45,8 +45,8 @@
/* Masks of the flag unions allowed for various efuns: */
#define RETURN_FUNCTION_MASK 0x0f /* functionlist() */
#define RETURN_VARIABLE_MASK 0x0f /* variable_list() */
#define RETURN_FUNCTION_MASK 0x2f /* functionlist() */
#define RETURN_VARIABLE_MASK 0x2f /* variable_list() */
/* Return value flag types for function_exists() */

View file

@ -18,6 +18,7 @@
#define TYPE_BYTES 12
#define TYPE_LWOBJECT 13
#define TYPE_COROUTINE 14
#define TYPE_LPCTYPE 15
#define TYPE_MOD_POINTER 0x0040 /* Pointer to a basic type */
@ -39,6 +40,7 @@
#define T_LWOBJECT 0xd
#define T_COROUTINE 0xe
#define T_PYTHON 0xf
#define T_LPCTYPE 0x10
/* Closure types, stored as secondary type info */

View file

@ -3215,6 +3215,8 @@ sameval (svalue_t *arg1, svalue_t *arg2)
return arg1->u.lwob == arg2->u.lwob;
} else if (arg1->type == T_COROUTINE && arg2->type == T_COROUTINE) {
return arg1->u.coroutine == arg2->u.coroutine;
} else if (arg1->type == T_LPCTYPE && arg2->type == T_LPCTYPE) {
return arg1->u.lpctype == arg2->u.lpctype;
} else
return 0;
} /* sameval() */

View file

@ -102,6 +102,7 @@ svalue_size (svalue_t *v, mp_int * pTotal)
case T_OBJECT:
case T_NUMBER:
case T_FLOAT:
case T_LPCTYPE:
return 0;
case T_STRING:

View file

@ -6374,6 +6374,14 @@ f_to_string (svalue_t *sp)
break;
}
case T_LPCTYPE:
{
string_t * rc = new_unicode_mstring(get_lpctype_name(sp->u.lpctype));
free_lpctype(sp->u.lpctype);
put_string(sp, rc);
break;
}
case T_SYMBOL:
{
/* Easy: the symbol value is a string */
@ -6403,6 +6411,7 @@ f_to_array (svalue_t *sp)
* mixed *to_array(quotedarray)
* mixed *to_array(mixed *)
* mixed *to_array(struct)
* lpctype *to_array(lpctype)
*
* Strings and symbols are converted to an int array that
* consists of the args characters.
@ -6499,11 +6508,70 @@ f_to_array (svalue_t *sp)
case T_POINTER:
/* Good as it is */
break;
case T_LPCTYPE:
{
lpctype_t *t = sp->u.lpctype;
vector_t *vec;
size_t pos = 0;
len = 1;
for (lpctype_t *cur = t; cur->t_class == TCLASS_UNION; cur = cur->t_union.head)
len++;
vec = allocate_array(len);
while (true)
{
if (t->t_class == TCLASS_UNION)
{
put_ref_lpctype(vec->item + pos, t->t_union.member);
t = t->t_union.head;
}
else
{
put_ref_lpctype(vec->item + pos, t);
break;
}
pos++;
}
free_lpctype(sp->u.lpctype);
put_array(sp, vec);
break;
}
}
return sp;
} /* f_to_array() */
/*-------------------------------------------------------------------------*/
svalue_t *
f_to_lpctype (svalue_t *sp)
/* EFUN to_lpctype()
*
* lpctype to_lpctype(string type)
*
* Interpret <type> as an lpctype and return the corresponding type object.
*
* We could use the LPC parser for this, but it has a high overhead.
* So we implement our own type parser here.
*/
{
const char *str = get_txt(sp->u.str);
const char *end = str + mstrsize(sp->u.str);
lpctype_t *result = parse_lpctype(&str, end);
if (!result)
errorf("Syntax error.\n");
if (str < end)
{
free_lpctype(result);
errorf("Extraneous characters at the end.\n");
}
free_mstring(sp->u.str);
put_lpctype(sp, result);
return sp;
} /* f_to_lpctype() */
/*-------------------------------------------------------------------------*/
/* -- struct mtos_member_s --
@ -8230,6 +8298,24 @@ v_get_type_info (svalue_t *sp, int num_arg)
return sp;
} /* v_get_type_info() */
/*-------------------------------------------------------------------------*/
svalue_t *
f_check_type (svalue_t *sp)
/* EFUN check_type()
*
* int check_type(mixed arg, lpctype type)
*
* Does RTTC of <type> against <arg> and return 1 if correct.
*/
{
bool result = check_rtt_compatibility(sp[0].u.lpctype, sp-1);
sp = pop_n_elems(2, sp);
push_number(sp, result ? 1 : 0);
return sp;
} /* f_check_type() */
/*-------------------------------------------------------------------------*/
svalue_t *
v_map (svalue_t *sp, int num_arg)
@ -8474,6 +8560,7 @@ v_member (svalue_t *sp, int num_arg)
case T_COROUTINE:
case T_POINTER:
case T_STRUCT:
case T_LPCTYPE:
#ifdef USE_PYTHON
case T_PYTHON:
#endif
@ -8787,6 +8874,7 @@ v_rmember (svalue_t *sp, int num_arg)
case T_COROUTINE:
case T_POINTER:
case T_STRUCT:
case T_LPCTYPE:
#ifdef USE_PYTHON
case T_PYTHON:
#endif

View file

@ -54,10 +54,12 @@ extern svalue_t *f_to_int (svalue_t *sp);
extern svalue_t *f_to_float (svalue_t *sp);
extern svalue_t *f_to_string (svalue_t *sp);
extern svalue_t *f_to_object (svalue_t *sp);
extern svalue_t *f_to_lpctype(svalue_t *sp);
extern svalue_t *f_copy (svalue_t *sp);
extern svalue_t *f_deep_copy (svalue_t *sp);
extern svalue_t *v_filter (svalue_t *sp, int num_arg);
extern svalue_t *v_get_type_info (svalue_t *sp, int num_arg);
extern svalue_t *f_check_type (svalue_t *sp);
extern svalue_t *v_map (svalue_t *sp, int num_arg);
extern svalue_t *v_member (svalue_t *sp, int num_arg);
extern svalue_t *v_rmember (svalue_t *sp, int num_arg);

View file

@ -654,16 +654,24 @@ struct program_s
/* Index of the heart beat function. -1 means no heart beat
*/
/* The types of all function arguments are saved in the
* .argument_types[]. To look up the arguments types for
* function <n>, retrieve the start index from the .type_start[]
* as .type_start[n]. If this index is INDEX_START_NONE, the function
* has no type information.
lpctype_t **types;
/* A list of all types used in the program.
*/
/* The types of all function arguments are saved in the .argument_types[].
* To look up the arguments types for function <n>:
* - Retrieve the start index from the .type_start[] as i=.type_start[n].
* If this index is INDEX_START_NONE, the function has no type
* information.
* - Get the type indices from .argument_types[] for the arguments
* starting with i as j=.argument_types[i+x] (x is argument position).
* - Get the type from .types as .types[j].
*
* This is also used for foreach() variables.
* Both arrays will only be allocated if '#pragma save_types' has
* been specified.
*/
lpctype_t **argument_types;
unsigned short *argument_types;
unsigned short *type_start;
/* TODO: Some code relies on this being unsigned short */
@ -708,8 +716,8 @@ struct program_s
/* Number of (directly) inherited programs */
unsigned short num_structs;
/* Number of listed struct definitions */
unsigned int num_argument_types;
/* Number of argument types in .argument_types */
unsigned int num_types;
/* Number of types in .types */
};
/* Constants for flags in program_s. */
@ -810,7 +818,7 @@ struct function_s
uint32 fx; /* Function index, this is not an offset. */
/* These entries are used by simul_efun.c. */
lpctype_t **argtypes;
unsigned short *argtypes;
/* Argument types for this simul_efun. */
uint32 next_sefun;

View file

@ -267,6 +267,7 @@
pop_n
put_array_element
type_check
push_type
call_other_cached
call_strict_cached
#ifdef USE_PYTHON
@ -303,6 +304,7 @@ int stringp(mixed);
int bytesp(mixed);
int structp(mixed);
int symbolp(mixed);
int lpctypep(mixed);
int typeof(mixed|mixed &);
mixed negate(int|float);
@ -370,9 +372,10 @@ int sgn(int|float);
int to_int(int|string|float|closure);
float to_float(float|string|int);
string to_string(mixed);
mixed *to_array(string|bytes|mixed*|symbol|quoted_array|struct);
mixed *to_array(string|bytes|mixed*|symbol|quoted_array|struct|lpctype);
mixed to_struct(mapping|mixed*|struct, void|struct);
object to_object(null|object|string|closure);
lpctype to_lpctype(string);
bytes to_bytes(string|bytes|int*, void|string);
string to_text(string|bytes|int*, void|string);
@ -384,6 +387,7 @@ mixed copy(mixed);
mixed deep_copy(mixed);
int ed(void|string, void|null|string) no_lightweight;
mixed get_type_info(mixed, void|int);
int check_type(mixed, lpctype);
mixed quote(mixed *|quoted_array|symbol|string);
mixed unquote(quoted_array|symbol);

View file

@ -1163,11 +1163,11 @@ clear_program_ref (program_t *p, Bool clear_ref)
}
}
if (p->argument_types)
if (p->types)
{
lpctype_t** arg_type = p->argument_types;
for (i = p->num_argument_types; --i >= 0; arg_type++)
clear_lpctype_ref(*arg_type);
lpctype_t** t = p->types;
for (i = p->num_types; --i >= 0; t++)
clear_lpctype_ref(*t);
}
} /* clear_program_ref() */
@ -1299,11 +1299,11 @@ gc_mark_program_ref (program_t *p)
str = p->includes[i].filename; MARK_MSTRING_REF(str);
}
if (p->argument_types)
if (p->types)
{
lpctype_t** arg_type = p->argument_types;
for (i = p->num_argument_types; --i >= 0; arg_type++)
count_lpctype_ref(*arg_type);
lpctype_t** t = p->types;
for (i = p->num_types; --i >= 0; t++)
count_lpctype_ref(*t);
}
}
else
@ -1516,6 +1516,10 @@ clear_ref_in_vector (svalue_t *svp, size_t num)
clear_coroutine_ref(p->u.coroutine);
continue;
case T_LPCTYPE:
clear_lpctype_ref(p->u.lpctype);
continue;
case T_LVALUE:
switch (p->x.lvalue_type)
{
@ -1721,6 +1725,10 @@ gc_count_ref_in_vector (svalue_t *svp, size_t num
MARK_MSTRING_REF(p->u.str);
continue;
case T_LPCTYPE:
count_lpctype_ref(p->u.lpctype);
continue;
case T_LVALUE:
switch (p->x.lvalue_type)
{

View file

@ -137,6 +137,10 @@ svalue_hash (svalue_t *svp, int bits)
case T_COROUTINE:
result = (p_int)svp->u.coroutine;
break;
case T_LPCTYPE:
result = (p_int)svp->u.lpctype;
break;
}
#if SIZEOF_CHAR_P > 4

View file

@ -438,6 +438,7 @@ static const char * svalue_typename[]
, /* T_LWOBJECT */ "lwobject"
, /* T_COROUTINE */ "coroutine"
, /* T_PYTHON */ "python-object"
, /* T_LPCTYPE */ "lpctype"
, /* T_CALLBACK */ "callback"
, /* T_ERROR_HANDLER */ "error-handler"
, /* T_BREAK_ADDR */ "break-address"
@ -1237,6 +1238,10 @@ int_free_svalue (svalue_t *v)
break;
#endif
case T_LPCTYPE:
free_lpctype(v->u.lpctype);
break;
case T_CALLBACK:
free_callback(v->u.cb);
xfree(v->u.cb);
@ -1713,6 +1718,10 @@ internal_assign_svalue_no_free (svalue_t *to, svalue_t *from)
(void)ref_mapping(to->u.map);
break;
case T_LPCTYPE:
ref_lpctype(to->u.lpctype);
break;
case T_LVALUE:
switch(to->x.lvalue_type)
{
@ -8203,6 +8212,10 @@ check_rtt_compatibility_inl(lpctype_t *formaltype, svalue_t *svp, lpctype_t **sv
valuetype = get_struct_type(bsvp->u.strct->type);
break;
case T_LPCTYPE:
valuetype = lpctype_lpctype;
break;
#ifdef USE_PYTHON
case T_PYTHON:
valuetype = get_python_type(bsvp->x.python_type);
@ -8298,7 +8311,7 @@ check_function_args(int fx, program_t *progp, bytecode_p funstart)
&& progp->type_start && progp->type_start[fx] != INDEX_START_NONE)
{
// check for the correct argument types
lpctype_t **arg_type = progp->argument_types + progp->type_start[fx];
unsigned short *arg_type_idx = progp->argument_types + progp->type_start[fx];
svalue_t *firstarg = inter_sp - csp->num_local_variables + 1;
function_t *header = current_prog->function_headers + FUNCTION_HEADER_INDEX(funstart);
@ -8306,16 +8319,17 @@ check_function_args(int fx, program_t *progp, bytecode_p funstart)
int i = 0;
while (i < formal_args)
{
lpctype_t *arg_type = progp->types[arg_type_idx[i]];
// do the types match (in case of structs also the structure)
// or is the formal argument of type TYPE_ANY (mixed)?
if (!check_rtt_compatibility(arg_type[i], firstarg+i))
if (!check_rtt_compatibility(arg_type, firstarg+i))
{
// How many control stack frames to remove.
int num_csf = 0;
// Determine the lpctype of arg_type[i] for a better error message.
// Determine the lpctype of arg[i] for a better error message.
static char buff[512];
lpctype_t *realtype = get_rtt_type(arg_type[i], firstarg+i);
lpctype_t *realtype = get_rtt_type(arg_type, firstarg+i);
get_lpctype_name_buf(realtype, buff, sizeof(buff));
free_lpctype(realtype);
@ -8371,7 +8385,7 @@ check_function_args(int fx, program_t *progp, bytecode_p funstart)
// warnf will return (errors are caught).
warnf("Bad arg %d to %s(): got '%s', expected '%s'.\n"
, i+1, get_txt(header->name), buff,
get_lpctype_name(arg_type[i]));
get_lpctype_name(arg_type));
for (int j = num_csf; j >= 0; j--)
*(++csp) = saved_csf[j];
@ -8385,7 +8399,7 @@ check_function_args(int fx, program_t *progp, bytecode_p funstart)
errorf("Bad arg %d to %s(): got '%s', expected '%s'.\n"
, i+1, get_txt(header->name), buff,
get_lpctype_name(arg_type[i]));
get_lpctype_name(arg_type));
}
}
++i;
@ -13538,6 +13552,10 @@ again:
i = (sp-1)->u.map == sp->u.map;
break;
case T_LPCTYPE:
i = (sp-1)->u.lpctype == sp->u.lpctype;
break;
#ifdef USE_PYTHON
case T_PYTHON:
i = (sp-1)->u.generic == sp->u.generic;
@ -13636,6 +13654,10 @@ again:
i = (sp-1)->u.map != sp->u.map;
break;
case T_LPCTYPE:
i = (sp-1)->u.lpctype != sp->u.lpctype;
break;
#ifdef USE_PYTHON
case T_PYTHON:
i = (sp-1)->u.generic != sp->u.generic;
@ -13763,8 +13785,20 @@ again:
break;
}
case T_LPCTYPE:
if (item->type == T_LPCTYPE)
{
if (item->u.lpctype == lpctype_void)
result = 1;
else
result = lpctype_contains(item->u.lpctype, container->u.lpctype) ? 1 : 0;
}
else
OP_ARG_ERROR(1, TF_LPCTYPE, item);
break;
default:
OP_ARG_ERROR(2, TF_POINTER|TF_MAPPING|TF_STRING|TF_BYTES, sp);
OP_ARG_ERROR(2, TF_POINTER|TF_MAPPING|TF_STRING|TF_BYTES|TF_LPCTYPE, sp);
/* NOTREACHED */
}
@ -13803,6 +13837,7 @@ again:
* vector & mapping -> vector
* mapping & vector -> mapping
* mapping & mapping -> mapping
* lpctype & lpctype -> lpctype
*
*/
@ -13862,8 +13897,22 @@ again:
break;
}
TYPE_TEST_EXP_LEFT((sp-1), TF_NUMBER|TF_STRING|TF_BYTES|TF_POINTER|TF_MAPPING);
TYPE_TEST_EXP_RIGHT(sp, TF_NUMBER|TF_STRING|TF_BYTES|TF_POINTER|TF_MAPPING);
if (sp->type == T_LPCTYPE && (sp-1)->type == T_LPCTYPE)
{
lpctype_t * result = get_common_type(sp[-1].u.lpctype, sp[0].u.lpctype);
free_lpctype(sp[-1].u.lpctype);
free_lpctype(sp[0].u.lpctype);
sp--;
if (result)
sp->u.lpctype = result;
else
sp->u.lpctype = lpctype_void;
break;
}
TYPE_TEST_EXP_LEFT((sp-1), TF_NUMBER|TF_STRING|TF_BYTES|TF_POINTER|TF_MAPPING|TF_LPCTYPE);
TYPE_TEST_EXP_RIGHT(sp, TF_NUMBER|TF_STRING|TF_BYTES|TF_POINTER|TF_MAPPING|TF_LPCTYPE);
ERRORF(("Arguments to & don't match: %s vs %s\n"
, typename(sp[-1].type), typename(sp->type)
));
@ -13876,8 +13925,9 @@ again:
* the result on the stack.
*
* Possible type combinations:
* int | int -> int
* array | array -> array
* int | int -> int
* array | array -> array
* lpctype | lpctype -> lpctype
*
* TODO: Extend this to mappings.
*/
@ -13890,7 +13940,7 @@ again:
}
#endif
TYPE_TEST_EXP_LEFT((sp-1), TF_NUMBER|TF_POINTER);
TYPE_TEST_EXP_LEFT((sp-1), TF_NUMBER|TF_POINTER|TF_LPCTYPE);
if ((sp-1)->type == T_NUMBER)
{
TYPE_TEST_RIGHT(sp, T_NUMBER);
@ -13906,6 +13956,16 @@ again:
sp--;
sp->u.vec = join_array(sp->u.vec, (sp+1)->u.vec);
}
else if (sp->type == T_LPCTYPE && (sp-1)->type == T_LPCTYPE)
{
TYPE_TEST_RIGHT(sp, T_LPCTYPE);
lpctype_t * result = get_union_type(sp[-1].u.lpctype, sp[0].u.lpctype);
free_lpctype(sp[-1].u.lpctype);
free_lpctype(sp[0].u.lpctype);
sp--;
sp->u.lpctype = result;
}
break;
}
@ -15327,6 +15387,7 @@ again:
* array & mapping -> array
* mapping & array -> mapping
* mapping & mapping -> mapping
* lpctype & lpctype -> lpctype
*/
svalue_t *argp;
@ -15487,8 +15548,26 @@ again:
/* NOTREACHED */
}
break;
case T_LPCTYPE:
if (sp[-1].type == T_LPCTYPE)
{
lpctype_t * result = get_common_type(argp->u.lpctype, sp[-1].u.lpctype);
free_lpctype(argp->u.lpctype);
if (result)
argp->u.lpctype = result;
else
argp->u.lpctype = lpctype_void;
}
else
{
OP_ARG_ERROR(2, TF_LPCTYPE, sp-1);
}
break;
default:
OP_ARG_ERROR(1, TF_NUMBER|TF_STRING|TF_BYTES|TF_POINTER, argp);
OP_ARG_ERROR(1, TF_NUMBER|TF_STRING|TF_BYTES|TF_POINTER|TF_LPCTYPE, argp);
/* NOTREACHED */
}
@ -15609,8 +15688,22 @@ again:
}
break;
case T_LPCTYPE:
if (sp[-1].type == T_LPCTYPE)
{
lpctype_t * result = get_union_type(argp->u.lpctype, sp[-1].u.lpctype);
free_lpctype(argp->u.lpctype);
argp->u.lpctype = result;
}
else
{
OP_ARG_ERROR(2, TF_LPCTYPE, sp-1);
}
break;
default:
OP_ARG_ERROR(1, TF_NUMBER|TF_POINTER, argp);
OP_ARG_ERROR(1, TF_NUMBER|TF_POINTER|TF_LPCTYPE, argp);
/* NOTREACHED */
}
@ -18541,7 +18634,7 @@ again:
/* Do runtime type checks. */
for (int i = 0; i <= left; i++)
{
lpctype_t* exptype = current_prog->argument_types[typeidx + i];
lpctype_t* exptype = current_prog->types[current_prog->argument_types[typeidx + i]];
svalue_t * val = i ? (values + i - 1) : (indices->item + ix);
if (!check_rtt_compatibility(exptype, val))
{
@ -18709,7 +18802,7 @@ again:
if (typeidx != USHRT_MAX && current_prog->argument_types)
{
/* Do runtime type checks. */
lpctype_t* exptype = current_prog->argument_types[typeidx];
lpctype_t* exptype = current_prog->types[current_prog->argument_types[typeidx]];
if (!check_rtt_compatibility(exptype, val))
{
char buf[512];
@ -18998,10 +19091,9 @@ again:
CASE(F_TYPE_CHECK); /* --- type_check <op> <ix> --- */
{
/* Check the top value off the stack against the type
* at prog->argument_types[<ix>]. Raise an error if
* it doesn't match. Do nothing otherwise.
* <op> contains a value of enum type_check_operation to
* give a specific error message.
* at prog->types[<ix>]. Raise an error if it doesn't match.
* Do nothing otherwise. <op> contains a value of
* enum type_check_operation to give a specific error message.
*/
unsigned short ix, op = LOAD_UINT8(pc);
@ -19010,10 +19102,10 @@ again:
LOAD_SHORT(ix, pc);
/* Types were saved? */
if (!current_prog->argument_types)
if (!current_prog->types)
break;
exptype = current_prog->argument_types[ix];
exptype = current_prog->types[ix];
if (!check_rtt_compatibility(exptype, sp))
{
static char buff[512];
@ -19056,6 +19148,23 @@ again:
break;
}
CASE(F_PUSH_TYPE); /* --- push_type <ix> --- */
{
/* Push the type at prog->types[<ix>] as an lpctype onto
* the stack.
*/
unsigned short ix;
LOAD_SHORT(ix, pc);
/* Types were saved? */
if (!current_prog->types)
break;
push_ref_lpctype(sp, current_prog->types[ix]);
break;
}
/* --- Efuns: Miscellaneous --- */
CASE(F_CLONEP); /* --- clonep --- */
@ -19379,6 +19488,25 @@ again:
break;
}
CASE(F_LPCTYPEP); /* --- lpctypep --- */
{
/* EFUN lpctypep()
*
* int lpctypep(mixed)
*
* Returns 1 if the argument is an LPC type object.
*/
int i;
CALL_PYTHON_TYPE_EFUN(F_LPCTYPEP, 1);
i = sp->type == T_LPCTYPE;
free_svalue(sp);
put_number(sp, i);
break;
}
CASE(F_TYPEOF); /* --- typeof --- */
{
/* EFUN typeof()

View file

@ -536,6 +536,7 @@ static struct s_reswords reswords[]
, { "closure", L_CLOSURE_DECL }
, { "continue", L_CONTINUE }
, { "coroutine", L_COROUTINE }
, { "decltype", L_DECLTYPE }
, { "default", L_DEFAULT }
, { "do", L_DO }
, { "else", L_ELSE }
@ -550,6 +551,7 @@ static struct s_reswords reswords[]
, { "inherit", L_INHERIT }
, { "int", L_INT }
, { "lwobject", L_LWOBJECT }
, { "lpctype", L_LPCTYPE }
, { "mapping", L_MAPPING }
, { "mixed", L_MIXED }
, { "nomask", L_NO_MASK }
@ -911,6 +913,7 @@ init_lexer(void)
add_permanent_define_str("__LPC_LWOBJECTS__", -1, "1");
add_permanent_define_str("__LPC_INLINE_CLOSURES__", -1, "1");
add_permanent_define_str("__LPC_COROUTINES__", -1, "1");
add_permanent_define_str("__LPC_LPCTYPES__", -1, "1");
add_permanent_define_str("__LPC_ARRAY_CALLS__", -1, "1");
#ifdef USE_TLS
add_permanent_define_str("__TLS__", -1, "1");
@ -1869,7 +1872,7 @@ undefined_function:
* Check it with a privilege violation.
*/
if (!privileged && efun_override == OVERRIDE_EFUN && p->u.global.sim_efun != I_GLOBAL_SEFUN_OTHER
&& get_simul_efun_header(p)->flags & TYPE_MOD_NO_MASK)
&& get_simul_efun_header(p, NULL)->flags & TYPE_MOD_NO_MASK)
{
svalue_t *res;
@ -5175,7 +5178,7 @@ closure (char *in_yyp)
*/
if (efun_override == OVERRIDE_EFUN
&& p->u.global.sim_efun != I_GLOBAL_SEFUN_OTHER
&& (get_simul_efun_header(p)->flags & TYPE_MOD_NO_MASK)
&& (get_simul_efun_header(p, NULL)->flags & TYPE_MOD_NO_MASK)
&& (p->u.global.efun != I_GLOBAL_EFUN_OTHER
#ifdef USE_PYTHON
|| is_python_efun(p)

View file

@ -519,6 +519,7 @@ static long lpc_types[MAX_ARGTYPES];
# define LPC_T_BYTES (1 << 13)
# define LPC_T_LWOBJECT (1 << 14)
# define LPC_T_COROUTINE (1 << 15)
# define LPC_T_LPCTYPE (1 << 16)
static int last_current_type = 0;
@ -902,8 +903,8 @@ move_to_arg_types ()
%token NAME ID
%token VOID INT STRING BYTES BYTES_OR_STRING OBJECT MAPPING FLOAT CLOSURE SYMBOL QUOTED_ARRAY
%token MIXED UNKNOWN NUL STRUCT LWOBJECT OBJECT_OR_LWOBJECT COROUTINE MAPPING_OR_CLOSURE
%token INT_OR_STRING STRING_OR_STRING_ARRAY CATCH_MSG_ARG
%token MIXED UNKNOWN NUL STRUCT LWOBJECT OBJECT_OR_LWOBJECT COROUTINE LPCTYPE
%token MAPPING_OR_CLOSURE INT_OR_STRING STRING_OR_STRING_ARRAY CATCH_MSG_ARG
%token DEFAULT NO_LIGHTWEIGHT
@ -919,7 +920,7 @@ move_to_arg_types ()
%token LVALUE
%type <number> VOID MIXED UNKNOWN NUL STRUCT
%type <number> INT STRING BYTES OBJECT MAPPING FLOAT CLOSURE SYMBOL QUOTED_ARRAY LWOBJECT COROUTINE
%type <number> INT STRING BYTES OBJECT MAPPING FLOAT CLOSURE SYMBOL QUOTED_ARRAY LWOBJECT COROUTINE LPCTYPE
%type <number> basic basic_utype
/* Value is the basic type value
*/
@ -1239,7 +1240,7 @@ type: basic opt_star opt_ref { $$ = (lpc_type_t){$1, $2 | $3, NULL}; }
;
basic: VOID | INT | STRING | BYTES | MAPPING | FLOAT | MIXED | OBJECT | CLOSURE |
UNKNOWN | SYMBOL | QUOTED_ARRAY | STRUCT | LWOBJECT | COROUTINE | NUL ;
UNKNOWN | SYMBOL | QUOTED_ARRAY | STRUCT | LWOBJECT | COROUTINE | LPCTYPE | NUL ;
opt_star : '*' { $$ = MF_TYPE_MOD_POINTER; }
| '*' '*' { $$ = MF_TYPE_MOD_POINTER_POINTER; }
@ -1562,6 +1563,7 @@ static struct type types[]
, { "struct", STRUCT }
, { "lwobject", LWOBJECT }
, { "coroutine", COROUTINE }
, { "lpctype", LPCTYPE }
};
static struct type visibility[]
@ -2981,6 +2983,7 @@ etype (long n)
CONVERT(LPC_T_STRUCT, "TF_STRUCT");
CONVERT(LPC_T_LWOBJECT, "TF_LWOBJECT");
CONVERT(LPC_T_COROUTINE, "TF_COROUTINE");
CONVERT(LPC_T_LPCTYPE, "TF_LPCTYPE");
# undef CONVERT
@ -3028,6 +3031,7 @@ type2flag (lpc_type_t t)
case STRUCT: return LPC_T_STRUCT; break;
case LWOBJECT:return LPC_T_LWOBJECT; break;
case COROUTINE: return LPC_T_COROUTINE; break;
case LPCTYPE: return LPC_T_LPCTYPE; break;
default: yyerror("(type2flag) Bad type!"); return 0;
}
} /* type2flag() */
@ -3067,6 +3071,7 @@ lpctypestr (lpc_type_t t)
case FLOAT: p = "&_lpctype_float"; break;
case CLOSURE: p = "&_lpctype_closure"; break;
case COROUTINE: p="&_lpctype_coroutine"; break;
case LPCTYPE: p = "&_lpctype_lpctype"; break;
case SYMBOL: p = "&_lpctype_symbol"; break;
case MIXED: p = "&_lpctype_mixed"; break;
case UNKNOWN: p = "&_lpctype_unknown"; break;

View file

@ -688,8 +688,8 @@ _free_prog (program_t *progp, Bool free_all, const char * file, int line
}
/* Free all argument types. */
for (i = progp->num_argument_types; --i >= 0; )
free_lpctype(progp->argument_types[i]);
for (i = progp->num_types; --i >= 0; )
free_lpctype(progp->types[i]);
/* Free the strings, variable names and include filenames. */
do_free_sub_strings( progp->num_strings, progp->strings
@ -1785,7 +1785,8 @@ f_functionlist (svalue_t *sp)
* Control of returned information:
* RETURN_FUNCTION_NAME include the function name
* RETURN_FUNCTION_FLAGS include the function flags
* RETURN_FUNCTION_TYPE include the return type
* RETURN_FUNCTION_TYPE include the return type as int
* RETURN_FUNCTION_LPCTYPE include the return type as lpctype
* RETURN_FUNCTION_NUMARG include the number of arguments.
*
* The name RETURN_FUNCTION_ARGTYPE is defined but not implemented.
@ -2013,6 +2014,11 @@ f_functionlist (svalue_t *sp)
svp->u.number = header->num_arg;
}
if (mode_flags & RETURN_FUNCTION_LPCTYPE) {
svp--;
put_ref_lpctype(svp, header->type);
}
if (mode_flags & RETURN_FUNCTION_TYPE) {
svp--;
svp->u.number = get_type_compat_int(header->type);
@ -2294,7 +2300,8 @@ f_variable_list (svalue_t *sp)
* Control of returned information:
* RETURN_FUNCTION_NAME include the variable name
* RETURN_FUNCTION_FLAGS include the variable flags
* RETURN_FUNCTION_TYPE include the return type
* RETURN_FUNCTION_TYPE include the return type as int
* RETURN_FUNCTION_LPCTYPE include the return type as lpctype
* RETURN_VARIABLE_VALUE include the variable value
*
* Control of listed variables:
@ -2510,6 +2517,12 @@ f_variable_list (svalue_t *sp)
assign_svalue_no_free(svp, variables+i);
}
if (mode_flags & RETURN_FUNCTION_LPCTYPE)
{
svp--;
put_ref_lpctype(svp, var->type.t_type);
}
if (mode_flags & RETURN_FUNCTION_TYPE)
{
svp--;
@ -4811,6 +4824,7 @@ e_say (svalue_t *v, vector_t *avoid)
case T_POINTER:
case T_MAPPING:
case T_STRUCT:
case T_LPCTYPE:
#ifdef USE_PYTHON
case T_PYTHON:
#endif
@ -5018,6 +5032,7 @@ e_tell_room (object_t *room, svalue_t *v, vector_t *avoid)
case T_POINTER:
case T_MAPPING:
case T_STRUCT:
case T_LPCTYPE:
#ifdef USE_PYTHON
case T_PYTHON:
#endif
@ -6966,6 +6981,26 @@ save_svalue (svalue_t *v, char delimiter, Bool writable)
break;
}
case T_LPCTYPE:
if (!recall_pointer(v->u.lpctype))
{
const char *t = get_lpctype_name(v->u.lpctype);
if (t)
{
L_PUTC_PROLOG
L_PUTC('[')
while (*t)
L_PUTC(*t++);
L_PUTC(']')
L_PUTC_EPILOG
}
else if (writable)
rc = MY_FALSE;
else
MY_PUTC('0');
}
break;
#ifdef USE_PYTHON
case T_PYTHON:
{
@ -7270,6 +7305,10 @@ register_svalue (svalue_t *svp)
register_closure(svp);
break;
case T_LPCTYPE:
register_pointer(ptable, svp->u.lpctype);
break;
case T_LVALUE:
if (save_version < SAVE_FORMAT_LVALUES)
{
@ -8026,6 +8065,28 @@ skip_element (char **str)
return true;
}
case '[': /* lpctype */
while (true)
{
/* We look for ']', and skip any strings. */
char *end = strchr(pt, ']');
char *s = strchr(pt, '"');
if (!end)
return false;
if (!s || s > end)
{
*str = end+1;
return true;
}
if (!skip_element(&s))
return false;
pt = s;
}
break; /* NOTREACHED */
case '#': /* A closure: skip the header and restart this check
* again from the data part.
*/
@ -9817,6 +9878,34 @@ restore_svalue (svalue_t *svp, char **pt, char delimiter)
}
break;
case '[': /* lpctype */
{
lpctype_t *result = NULL;
char *end = cp;
if (skip_element(&end))
{
const char *s = cp + 1;
result = parse_lpctype(&s, end - 1);
*pt = end;
if (s < end - 1)
{
free_lpctype(result);
result = NULL;
}
}
if (!result)
{
*svp = const0;
return MY_FALSE;
}
put_lpctype(svp, result);
break;
}
case '-': /* A number */
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':

View file

@ -2082,7 +2082,7 @@ ldmud_program_lfun_get_arguments (ldmud_program_and_index_t *lfun, void *closure
arg->type = NULL;
if (progp->argument_types && progp->type_start && progp->type_start[fx] != INDEX_START_NONE)
arg->type = lpctype_to_pythontype(progp->argument_types[progp->type_start[fx] + i]);
arg->type = lpctype_to_pythontype(progp->types[progp->argument_types[progp->type_start[fx] + i]]);
PyList_SET_ITEM(result, i, (PyObject*)arg);
}
@ -9871,6 +9871,7 @@ lpctype_to_pythontype (lpctype_t *type)
Py_INCREF(&PyBytes_Type);
return (PyObject *)&PyBytes_Type;
case TYPE_LPCTYPE: // TODO
case TYPE_UNKNOWN:
case TYPE_ANY:
break;

View file

@ -401,12 +401,16 @@ enum e_saved_areas {
* variables and functions of an obsolete virtually inherited
* program.
*/
, A_ARGUMENT_TYPES
/* (lpctype_t*) Types of the arguments of all functions with
* typechecking. The argument types for a specific function
* can be found using the ARGUMENT_INDEX. All entries
* are counted references.
, A_TYPES
/* (lpctype_t*) Types used in the program. All entries are
* counted references.
*/
, A_ARGUMENT_TYPE_INDEX
/* (unsigned short) Index into A_TYPES for every entry in
* A_ARGUMENT_TYPES. This is only created during epilog.
*/
, A_ARGUMENT_INDEX
/* (unsigned short) Index of the first argument type of function <n>.
* INDEX_START_NONE is used for functions with no type information.
@ -431,7 +435,8 @@ typedef variable_t A_VIRTUAL_VAR_t;
typedef char A_LINENUMBERS_t;
typedef inherit_t A_INHERITS_t;
typedef unsigned short A_UPDATE_INDEX_MAP_t;
typedef lpctype_t* A_ARGUMENT_TYPES_t;
typedef lpctype_t* A_TYPES_t;
typedef unsigned short A_ARGUMENT_TYPE_INDEX_t;
typedef unsigned short A_ARGUMENT_INDEX_t;
typedef include_t A_INCLUDES_t;
typedef struct_def_t A_STRUCT_DEFS_t;
@ -459,10 +464,18 @@ enum e_internal_areas {
* therefore not virtual) global variables.
*/
, A_ARGUMENT_TYPES
/* (lpctype_t*) Types of the arguments of all functions with
* typechecking. The argument types for a specific function
* can be found using the ARGUMENT_INDEX. All entries
* are counted references.
*/
, A_INLINE_PROGRAM
/* (bytecode_t, char): Program and linenumbers saved from the compiled
* but not yet inserted inline closures.
*/
, A_INLINE_CLOSURE
/* (inline_closure_t): The currently pending inline closures. The lexical
* nesting is achieved with the .prev/.next pointers in the
@ -553,6 +566,7 @@ typedef function_t A_FUNCTIONS_t;
typedef int A_STRING_NEXT_t;
typedef local_variable_t A_LOCAL_VARIABLES_t;
typedef global_variable_t A_GLOBAL_VARIABLES_t;
typedef lpctype_t* A_ARGUMENT_TYPES_t;
typedef bytecode_t A_INLINE_PROGRAM_t;
typedef inline_closure_t A_INLINE_CLOSURE_t;
typedef struct_member_t A_STRUCT_MEMBERS_t;
@ -632,6 +646,17 @@ static mem_block_t mem_block[NUMAREAS];
/* Lookup the start index of the types for function number <n>.
*/
#define PROG_TYPE_COUNT GET_BLOCK_COUNT(A_TYPES)
/* Number of lpctype_t* stored so far in A_TYPES.
*/
#define PROG_TYPE(n) GET_BLOCK(A_TYPES)[n]
/* Get the lpctype_t* with index <n>.
*/
#define ARGUMENT_TYPE_INDEX(n) GET_BLOCK(A_ARGUMENT_TYPE_INDEX)[n]
/* Get the index into A_TYPES at position <n>.
*/
#define ARGTYPE_COUNT GET_BLOCK_COUNT(A_ARGUMENT_TYPES)
/* Number of lpctype_t* stored so far in A_ARGUMENT_TYPES.
@ -1307,6 +1332,11 @@ static code_context_t* string_context;
* NULL when we are compiling a program.
*/
static int compiling_decltype;
/* The level of decltype() operations that are currently compilied.
* When this is > 0, code generation can be omitted.
*/
/* A few standard types we often need.
* We'll initialize them later (using the type functions, so all pointers
* are correctly set) and then put them into a static storage (and set
@ -1358,6 +1388,8 @@ static void use_variable (ident_t* name, enum variable_usage usage);
static void warn_variable_usage (string_t* name, enum variable_usage usage, const char* prefix);
static Bool add_lvalue_code (lvalue_block_t lv, int instruction);
static void insert_pop_value(void);
static int get_type_index(lpctype_t *t);
static int ins_prog_type(lpctype_t *t);
static void add_type_check (lpctype_t *expected, enum type_check_operation op);
static int insert_inherited(char *super_name, string_t *real_name, program_t **super_p, function_t *fun_p, int num_arg);
/* Returnvalues from insert_inherited(): */
@ -1705,6 +1737,7 @@ add_to_mem_block (int n, void *data, size_t size)
sizeof(BLOCK_NAME##_t) * count); \
}
DEFINE_ADD_TO_BLOCK_BY_VALUE(ADD_PROG_TYPE, A_TYPES)
DEFINE_ADD_TO_BLOCK_BY_VALUE(ADD_ARGUMENT_TYPE, A_ARGUMENT_TYPES)
DEFINE_ADD_TO_BLOCK_BY_VALUE(ADD_ARGUMENT_INDEX, A_ARGUMENT_INDEX)
DEFINE_ADD_TO_BLOCK_BY_PTR(ADD_FUNCTION, A_FUNCTIONS)
@ -1826,7 +1859,7 @@ get_lpctype_name_buf (lpctype_t *type, char *buf, size_t bufsize)
static char *type_name[] = { "unknown", "int", "string", "void",
"mapping", "float", "mixed", "closure",
"symbol", "quoted_array", "bytes",
"coroutine" };
"coroutine", "lpctype" };
if (bufsize <= 0)
return 0;
@ -1894,20 +1927,27 @@ get_lpctype_name_buf (lpctype_t *type, char *buf, size_t bufsize)
if (type->t_object.program_name)
{
size_t proglen = mstrsize(type->t_object.program_name);
size_t len = proglen + obtypenamelen + 4;
if (len < bufsize)
if (obtypenamelen + 6 < bufsize)
{
char *bufptr = buf + obtypenamelen;
size_t proglen;
memcpy(buf, obtypename, obtypenamelen);
memcpy(bufptr, " \"/", 3);
bufptr += 3;
memcpy(bufptr, get_txt(type->t_object.program_name), proglen);
buf[len-1] = '"';
buf[len] = 0;
return len;
proglen = escape_string(get_txt(type->t_object.program_name), mstrsize(type->t_object.program_name),
bufptr, bufsize - obtypenamelen - 4, true);
if (!proglen || proglen + obtypenamelen + 5 > bufsize)
{
buf[0] = '\0';
return 0;
}
bufptr += proglen;
*bufptr++ = '"';
*bufptr = 0;
return bufptr - buf;
}
else
{
@ -2510,21 +2550,41 @@ binary_op_types_t types_binary_and_assignment[] = {
{ &_lpctype_int, &_lpctype_int, &_lpctype_int, NULL , NULL , NULL },
{ &_lpctype_string, &_lpctype_string, &_lpctype_string, NULL , NULL , NULL },
{ &_lpctype_bytes, &_lpctype_bytes, &_lpctype_bytes, NULL , NULL , NULL },
{ &_lpctype_lpctype, &_lpctype_lpctype, &_lpctype_lpctype, NULL , NULL , NULL },
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
/* Operator type table for assignment with the binary or and xor.
/* Operator type table for assignment with the binary or.
*/
binary_op_types_t types_binary_or_assignment[] = {
{ &_lpctype_int, &_lpctype_int, &_lpctype_int, NULL , NULL , NULL },
{ &_lpctype_any_array, &_lpctype_any_array, NULL, &get_sub_array_type , &get_first_type , &get_common_array_type },
{ &_lpctype_lpctype, &_lpctype_lpctype, &_lpctype_lpctype, NULL , NULL , NULL },
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
/* Operator type table for assignment with the binary xor.
*/
binary_op_types_t types_binary_xor_assignment[] = {
{ &_lpctype_int, &_lpctype_int, &_lpctype_int, NULL , NULL , NULL },
{ &_lpctype_any_array, &_lpctype_any_array, NULL, &get_sub_array_type , &get_first_type , &get_common_array_type },
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
/* Operator type table for the binary or and xor,
* allowing <int>|<int> and <mixed*>|<mixed*>.
/* Operator type table for the binary or,
* allowing <int>|<int>, <mixed*>|<mixed*> and <lpctype>|<lpctype>
*/
binary_op_types_t types_binary_or[] = {
{ &_lpctype_int, &_lpctype_int, &_lpctype_int, NULL , NULL , NULL },
{ &_lpctype_any_array, &_lpctype_any_array, NULL, &get_union_array_type , NULL , NULL },
{ &_lpctype_lpctype, &_lpctype_lpctype, &_lpctype_lpctype, NULL , NULL , NULL },
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
/* Operator type table for the binary xor,
* allowing <int>|<int> and <mixed*>|<mixed*>.
*/
binary_op_types_t types_binary_xor[] = {
{ &_lpctype_int, &_lpctype_int, &_lpctype_int, NULL , NULL , NULL },
{ &_lpctype_any_array, &_lpctype_any_array, NULL, &get_union_array_type , NULL , NULL },
{ NULL, NULL, NULL, NULL, NULL, NULL }
@ -2540,6 +2600,7 @@ binary_op_types_t types_binary_and[] = {
{ &_lpctype_int, &_lpctype_int, &_lpctype_int, NULL , NULL , NULL },
{ &_lpctype_string, &_lpctype_string, &_lpctype_string, NULL , NULL , NULL },
{ &_lpctype_bytes, &_lpctype_bytes, &_lpctype_bytes, NULL , NULL , NULL },
{ &_lpctype_lpctype, &_lpctype_lpctype, &_lpctype_lpctype, NULL , NULL , NULL },
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
@ -2562,6 +2623,7 @@ binary_op_types_t types_in[] = {
{ &_lpctype_int, &_lpctype_bytes, &_lpctype_int, NULL , NULL , NULL },
{ &_lpctype_string, &_lpctype_string, &_lpctype_int, NULL , NULL , NULL },
{ &_lpctype_bytes, &_lpctype_bytes, &_lpctype_int, NULL , NULL , NULL },
{ &_lpctype_lpctype, &_lpctype_lpctype, &_lpctype_int, NULL , NULL , NULL },
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
@ -3326,10 +3388,12 @@ check_assignment_types (fulltype_t src, lpctype_t *dest)
/*-------------------------------------------------------------------------*/
static void
check_function_call_types (fulltype_t *aargs, int num_aarg, function_t *funp, lpctype_t **dargs)
check_function_call_types (fulltype_t *aargs, int num_aarg, function_t *funp, unsigned short *dargs, lpctype_t **types)
/* Checks the actual function arguments (<aargs> with <num_aarg> entries)
* against the function definition <funp> with <dargs> argument type list.
* against the function definition <funp> with <dargs> argument type index
* list into <types>. If <dargs> is NULL, then a direct lookup into <types>
* should be done.
*/
{
@ -3347,21 +3411,25 @@ check_function_call_types (fulltype_t *aargs, int num_aarg, function_t *funp, lp
for (int argno = 1; argno <= num_darg; argno++)
{
if (!check_assignment_types(*aargs, *dargs))
lpctype_t *expected = dargs ? types[*dargs] : *types;
if (!check_assignment_types(*aargs, expected))
{
yyerrorf("Bad type for argument %d of %s %s",
argno,
get_txt(funp->name),
get_two_lpctypes(*dargs, aargs->t_type));
get_two_lpctypes(expected, aargs->t_type));
}
aargs++;
dargs++;
if (dargs)
dargs++;
else
types++;
} /* for (all args) */
if ((funp->flags & TYPE_MOD_XVARARGS) && !missingargs)
{
lpctype_t *flat_type = get_flattened_type(*dargs);
lpctype_t *flat_type = get_flattened_type(dargs ? types[*dargs] : *types);
for (int argno = num_darg+1; argno <= num_aarg; argno++)
{
@ -7648,7 +7716,7 @@ printf("DEBUG: depth %d, locals: %d/%d, break: %d/%d\n",
length = current_inline->length;
end = current_inline->end;
if (!bAbort && !string_context)
if (!bAbort && !string_context && !compiling_decltype)
{
backup_start = INLINE_PROGRAM_SIZE;
#ifdef DEBUG_INLINES
@ -7692,7 +7760,7 @@ printf("DEBUG: program size: %"PRIuMPINT"\n", CURRENT_PROGRAM_SIZE);
/* Move the linenumber data into the backup storage */
start = current_inline->li_start;
length = current_inline->li_length;
if (!bAbort && !string_context)
if (!bAbort && !string_context && !compiling_decltype)
{
backup_start = INLINE_PROGRAM_SIZE;
#ifdef DEBUG_INLINES
@ -7847,7 +7915,7 @@ prepare_inline_closure (lpctype_t *returntype, bool coroutine)
fulltype_t funtype;
ident_t * ident;
if (!string_context)
if (!string_context && !compiling_decltype)
{
/* Create the name of the new inline function.
* We have to make sure the name is really unique.
@ -7920,7 +7988,7 @@ inline_closure_prototype (int num_args)
printf("DEBUG: inline_closure_prototype(%d)\n", num_args);
#endif /* DEBUG_INLINES */
def_function_argument_check(true);
if (!string_context)
if (!string_context && !compiling_decltype)
def_function_prototype(num_args, MY_TRUE);
#ifdef DEBUG_INLINES
@ -8012,7 +8080,7 @@ printf("DEBUG: current depth: %d: %d\n", block_depth, block_scope[bloc
/* Generate the function header and update the ident-table entry.
*/
if (!string_context)
if (!string_context && !compiling_decltype)
{
int fnum = current_inline->ident->u.global.function;
FUNCTION(fnum)->num_opt_arg = current_inline->num_opt_args;
@ -8113,7 +8181,7 @@ printf("DEBUG: -> F_CONTEXT_CLOSURE %d %d %d\n", current_inline->function
, num_explicit_context, context->num_locals - num_explicit_context);
#endif /* DEBUG_INLINES */
if (string_context)
if (string_context && !compiling_decltype)
{
/* We are compiling a lambda closure. Therefore we cannot insert
* the inline closure into a program and need to make that
@ -8406,6 +8474,12 @@ store_lambda_value (svalue_t *svp)
assert(hash < 0x100);
if (compiling_decltype > 0)
{
free_svalue(svp);
return 0;
}
if (lambda_values_table_level*0x100 + hash >= LAMBDA_VALUE_TABLE_SIZE)
{
int needed_entries = (lambda_values_table_level+1)*0x100 - LAMBDA_VALUE_TABLE_SIZE;
@ -8812,6 +8886,7 @@ get_global_variable_lvalue (ident_t *ident)
%token L_CONTINUE
%token L_COROUTINE
%token L_DEC
%token L_DECLTYPE
%token L_DEFAULT
%token L_DO
%token L_DUMMY
@ -8835,6 +8910,7 @@ get_global_variable_lvalue (ident_t *ident)
%token L_LAND
%token L_LE
%token L_LOR
%token L_LPCTYPE
%token L_LSH
%token L_LWOBJECT
%token L_MAPPING
@ -10569,6 +10645,7 @@ single_basic_non_void_type:
| L_STRING_DECL { $$ = pragma_no_bytes_type ? lpctype_string_bytes : lpctype_string; }
| L_CLOSURE_DECL { $$ = lpctype_closure; }
| L_COROUTINE { $$ = lpctype_coroutine; }
| L_LPCTYPE { $$ = lpctype_lpctype; }
| L_SYMBOL_DECL { $$ = lpctype_symbol; }
| L_FLOAT_DECL { $$ = lpctype_float; }
| L_MAPPING { $$ = lpctype_mapping; }
@ -13053,7 +13130,7 @@ expr0:
case F_XOR_EQ:
op_name = "^=";
op_table = types_binary_or_assignment;
op_table = types_binary_xor_assignment;
%ifdef USE_PYTHON
python_left_op = PYTHON_OP_XOR;
python_right_op = PYTHON_OP_RXOR;
@ -13431,7 +13508,7 @@ expr0:
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
| expr0 '^' expr0
{
lpctype_t *result = check_binary_op_types($1.type.t_type, $3.type.t_type, "^", types_binary_or,
lpctype_t *result = check_binary_op_types($1.type.t_type, $3.type.t_type, "^", types_binary_xor,
%ifdef USE_PYTHON
PYTHON_OP_XOR, PYTHON_OP_RXOR, PYTHON_OP_NONE,
%endif
@ -14603,7 +14680,7 @@ expr4:
| L_SIMUL_EFUN_CLOSURE
{
int sefun = $1->u.global.sim_efun;
function_t *fun = get_simul_efun_header($1);
function_t *fun = get_simul_efun_header($1, NULL);
if (fun->flags & TYPE_MOD_DEPRECATED)
{
@ -14705,15 +14782,15 @@ expr4:
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
| '(' note_start comma_expr ')' %prec '~'
| '(' comma_expr ')' %prec '~'
{
/* A nested expression */
$$.type = $3.type;
$$.start = $2;
$$.type = $2.type;
$$.start = $2.start;
$$.lvalue = (lvalue_block_t) {0, 0};
$$.name = $3.name;
$$.needs_use = $3.needs_use;
$$.name = $2.name;
$$.needs_use = $2.needs_use;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
@ -14923,6 +15000,39 @@ expr4:
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
| '[' basic_type ']'
{
$$.type = get_fulltype_flags(lpctype_lpctype, TYPE_MOD_LITERAL);
$$.start = CURRENT_PROGRAM_SIZE;
$$.lvalue = (lvalue_block_t) {0, 0};
$$.name = NULL;
$$.needs_use = true;
ins_prog_type($2);
free_lpctype($2);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
| L_DECLTYPE '('
{
$<address>$ = CURRENT_PROGRAM_SIZE;
compiling_decltype++;
}
expr0 ')'
{
compiling_decltype--;
$$.type = get_fulltype_flags(lpctype_lpctype, TYPE_MOD_LITERAL);
$$.start = CURRENT_PROGRAM_SIZE = $<address>3;
$$.lvalue = (lvalue_block_t) {0, 0};
$$.name = NULL;
$$.needs_use = true;
ins_prog_type($4.type.t_type);
free_fulltype($4.type);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
%// The following expressions can be patched to lvalues for use in index_lvalue.
| L_IDENTIFIER
{
@ -17045,8 +17155,6 @@ function_call:
%line
int f; /* Function index */
int simul_efun;
lpctype_t **arg_types = NULL; /* Argtypes from the program */
int first_arg; /* Startindex in arg_types[] */
Bool ap_needed; /* TRUE if arg frame is needed */
Bool has_ellipsis; /* TRUE if '...' was used */
@ -17085,7 +17193,8 @@ function_call:
PREPARE_INSERT(6)
function_t *funp = get_simul_efun_header($1.real);
const program_t *progp;
function_t *funp = get_simul_efun_header($1.real, &progp);
if (!(funp->flags & TYPE_MOD_VARARGS))
{
@ -17118,7 +17227,7 @@ function_call:
ap_needed = MY_TRUE;
if (funp->offset.argtypes != NULL)
check_function_call_types(get_argument_types_start($4), $4, funp, funp->offset.argtypes);
check_function_call_types(get_argument_types_start($4), $4, funp, funp->offset.argtypes, progp->types);
if (simul_efun == I_GLOBAL_SEFUN_BY_NAME)
{
@ -17169,6 +17278,9 @@ function_call:
function_t *funp;
function_t inherited_function;
unsigned short *arg_types = NULL; /* arg types from the program */
lpctype_t **types = NULL; /* actual types for lookup */
int first_arg; /* Start index in arg_types[] */
ap_needed = MY_TRUE;
@ -17216,6 +17328,7 @@ function_call:
&& NULL != (arg_types = super_prog->argument_types))
{
first_arg = super_prog->type_start[ix];
types = super_prog->types;
}
else
{
@ -17242,6 +17355,7 @@ function_call:
inherited_function.flags = prog->functions[f];
get_function_information(&inherited_function, prog, f);
arg_types = prog->argument_types;
types = prog->types;
if (arg_types != NULL)
first_arg = prog->type_start[f];
else
@ -17251,7 +17365,8 @@ function_call:
else
{
funp = FUNCTION(f);
arg_types = GET_BLOCK(A_ARGUMENT_TYPES);
arg_types = NULL;
types = GET_BLOCK(A_ARGUMENT_TYPES);
first_arg = ARGUMENT_INDEX(f);
}
}
@ -17312,7 +17427,13 @@ function_call:
/* Check the argument types.
*/
if (exact_types && first_arg != INDEX_START_NONE)
check_function_call_types(get_argument_types_start($4), $4, funp, arg_types + first_arg);
{
if (arg_types)
arg_types += first_arg;
else
types += first_arg;
check_function_call_types(get_argument_types_start($4), $4, funp, arg_types, types);
}
} /* if (inherited lfun) */
@ -17794,7 +17915,7 @@ function_call:
, get_txt(funp->name));
if (funp->offset.argtypes != NULL)
check_function_call_types(get_argument_types_start(num_arg), num_arg, funp, funp->offset.argtypes);
check_function_call_types(get_argument_types_start(num_arg), num_arg, funp, funp->offset.argtypes, simul_efun_table[sefun].program->types);
if (!(funp->flags & (TYPE_MOD_VARARGS|TYPE_MOD_XVARARGS))
&& !has_ellipsis)
@ -18007,7 +18128,7 @@ function_name:
if ( !strcmp($1, "efun")
&& fun->type == I_TYPE_GLOBAL
&& fun->u.global.sim_efun != I_GLOBAL_SEFUN_OTHER
&& (get_simul_efun_header(fun)->flags & TYPE_MOD_NO_MASK)
&& (get_simul_efun_header(fun, NULL)->flags & TYPE_MOD_NO_MASK)
&& master_ob
&& (!EVALUATION_TOO_LONG())
)
@ -18707,9 +18828,60 @@ insert_pop_value (void)
last_expression = -1;
} /* insert_pop_value() */
/*-------------------------------------------------------------------------*/
static int
get_type_index (lpctype_t *t)
/* Add <t> to the programs type list.
*/
{
int idx;
for (idx = 0; idx < PROG_TYPE_COUNT; idx++)
if (PROG_TYPE(idx) == t)
break;
if (idx == PROG_TYPE_COUNT)
{
if (idx > (long)USHRT_MAX)
return -1;
ADD_PROG_TYPE(ref_lpctype(t));
}
return idx;
} /* get_type_index() */
/*-------------------------------------------------------------------------*/
static int
ins_prog_type (lpctype_t *t)
/* Add the type <t> to the program types and insert codes to put it
* on the stack into the current bytecode. The references are not adopted.
* Returns the number of bytes written to the bytecode.
*/
{
if (string_context)
{
svalue_t sv = svalue_lpctype(ref_lpctype(t));
return ins_lambda_value(&sv);
}
else
{
PREPARE_INSERT(3);
add_f_code(F_PUSH_TYPE);
add_short(get_type_index(t));
CURRENT_PROGRAM_SIZE += 3;
return 3;
}
} /* ins_prog_type() */
/*-------------------------------------------------------------------------*/
static void
add_type_check (lpctype_t *expected, enum type_check_operation op)
/* Adds an instruction for type checking the topmost value
* on the stack against <expected>.
*/
@ -18730,25 +18902,7 @@ add_type_check (lpctype_t *expected, enum type_check_operation op)
return;
/* Now get an index for the type in our type list. */
for (idx = 0; idx < ARGTYPE_COUNT; idx++)
if (ARGUMENT_TYPE(idx) == expected)
break;
if (idx == ARGTYPE_COUNT)
{
/* Check that there is space in the argument type list. */
if (arg_types_exhausted)
return;
if (idx > (long)USHRT_MAX)
{
arg_types_exhausted = true;
yywarnf("Type buffer exhausted, cannot store and verify argument types.");
return;
}
ADD_ARGUMENT_TYPE(ref_lpctype(expected));
}
idx = get_type_index(expected);
ins_f_code(F_TYPE_CHECK);
ins_byte(op);
@ -19676,7 +19830,8 @@ inherit_functions (program_t *from, uint32 inheritidx)
A_ARGUMENT_INDEX_t argindex = INDEX_START_NONE; /* Presume not available. */
if (from->type_start != 0)
{
if (from->type_start[i] != INDEX_START_NONE)
unsigned short arg_type_idx = from->type_start[i];
if (arg_type_idx != INDEX_START_NONE)
{
/* They are available for function number 'i'. Copy types of
* all arguments, and remember where they started.
@ -19684,18 +19839,9 @@ inherit_functions (program_t *from, uint32 inheritidx)
argindex = ARGTYPE_COUNT;
if (fun_p->num_arg)
{
int ix;
ix = ARGTYPE_COUNT;
add_to_mem_block(
A_ARGUMENT_TYPES,
&from->argument_types[from->type_start[i]],
(sizeof (A_ARGUMENT_TYPES_t)) * fun_p->num_arg
);
for ( ; (size_t)ix < ARGTYPE_COUNT; ix++)
ref_lpctype(ARGUMENT_TYPE(ix));
reserve_mem_block(A_ARGUMENT_TYPES, sizeof(A_ARGUMENT_TYPES_t) * fun_p->num_arg);
for (int pos = 0; pos < fun_p->num_arg; pos++)
ADD_ARGUMENT_TYPE(ref_lpctype(from->types[from->argument_types[arg_type_idx+pos]]));
}
}
@ -21649,6 +21795,7 @@ prolog (const char * fname, Bool isMasterObj)
string_context = NULL;
lambda_values_table_level = 0;
lambda_values_offset = 0;
compiling_decltype = 0;
free_all_local_names(); /* In case of earlier error */
@ -21834,6 +21981,9 @@ epilog_free_all (void)
);
/* Free the type information */
for (size_t i = 0; i < PROG_TYPE_COUNT; i++)
free_lpctype(PROG_TYPE(i));
for (size_t i = 0; i < ARGTYPE_COUNT; i++)
free_lpctype(ARGUMENT_TYPE(i));
@ -22255,6 +22405,22 @@ epilog (void)
} /* if (parse successful) */
/* Save argument types into A_TYPES. */
if (pragma_save_types)
{
extend_mem_block(A_ARGUMENT_TYPE_INDEX, ARGTYPE_COUNT * sizeof(A_ARGUMENT_TYPE_INDEX_t));
for (i = 0; i < ARGTYPE_COUNT; i++)
ARGUMENT_TYPE_INDEX(i) = get_type_index(ARGUMENT_TYPE(i));
}
else
mem_block[A_ARGUMENT_INDEX].current_size = 0;
if (PROG_TYPE_COUNT > USHRT_MAX)
yyerror("Too many types");
for (i = 0; (size_t)i < ARGTYPE_COUNT; i++)
free_lpctype(ARGUMENT_TYPE(i));
mem_block[A_ARGUMENT_TYPES].current_size = 0;
/* Remove the concrete struct definition from the lpctype object
* and free the reference we took.
*/
@ -22318,13 +22484,6 @@ epilog (void)
size = align(sizeof (program_t));
if (!pragma_save_types)
{
for (i = 0; (size_t)i < ARGTYPE_COUNT; i++)
free_lpctype(ARGUMENT_TYPE(i));
mem_block[A_ARGUMENT_TYPES].current_size = 0;
mem_block[A_ARGUMENT_INDEX].current_size = 0;
}
for (i = 0; i< NUMPAREAS; i++)
{
if (i != A_LINENUMBERS)
@ -22510,20 +22669,34 @@ epilog (void)
prog->includes = NULL;
p += align(mem_block[A_INCLUDES].current_size);
/* Add the argument type information
/* Add the type information
*/
if (pragma_save_types)
prog->num_types = PROG_TYPE_COUNT;
if (prog->num_types)
{
if (mem_block[A_ARGUMENT_TYPES].current_size)
memcpy(p, mem_block[A_ARGUMENT_TYPES].block,
mem_block[A_ARGUMENT_TYPES].current_size);
prog->argument_types = (A_ARGUMENT_TYPES_t *)p;
prog->num_argument_types = ARGTYPE_COUNT;
p += align(mem_block[A_ARGUMENT_TYPES].current_size);
memcpy(p, mem_block[A_TYPES].block
, mem_block[A_TYPES].current_size);
prog->types = (A_TYPES_t *)p;
}
else
{
assert(GET_BLOCK_COUNT(A_ARGUMENT_TYPE_INDEX) == 0);
prog->types = NULL;
}
p += align(mem_block[A_TYPES].current_size);
/* Add argument type information.
*/
if (GET_BLOCK_COUNT(A_ARGUMENT_TYPE_INDEX))
{
memcpy(p, mem_block[A_ARGUMENT_TYPE_INDEX].block
, mem_block[A_ARGUMENT_TYPE_INDEX].current_size);
prog->argument_types = (A_ARGUMENT_TYPE_INDEX_t *)p;
p += align(mem_block[A_ARGUMENT_TYPE_INDEX].current_size);
if (mem_block[A_ARGUMENT_INDEX].current_size)
memcpy(p, mem_block[A_ARGUMENT_INDEX].block,
mem_block[A_ARGUMENT_INDEX].current_size);
memcpy(p, mem_block[A_ARGUMENT_INDEX].block
, mem_block[A_ARGUMENT_INDEX].current_size);
prog->type_start = (A_ARGUMENT_INDEX_t *)p;
p += align(mem_block[A_ARGUMENT_INDEX].current_size);
}
@ -22531,10 +22704,6 @@ epilog (void)
{
prog->argument_types = NULL;
prog->type_start = NULL;
prog->num_argument_types = 0;
for (i = 0; (size_t)i < ARGTYPE_COUNT; i++)
free_lpctype(ARGUMENT_TYPE(i));
}
/* Add the lightweight object call cache.

View file

@ -490,13 +490,26 @@ query_simul_efun_file_name(void)
return simul_efun_file_name;
}
/*-------------------------------------------------------------------------*/
program_t *
get_simul_efun_program ()
/* Return the program of the primary simul_efun object.
*/
{
return simul_efun_program;
} /* get_simul_efun_program() */
/*-------------------------------------------------------------------------*/
function_t *
get_simul_efun_header (ident_t* name)
get_simul_efun_header (ident_t* name, const program_t **progp)
/* Return the function header for the simul-efun <name>.
* <name> must be a valid simul-efun identifier (i.e. there
* must be a simul-efun object with such a function).
* If <progp> is not NULL, the corresponding program pointer
* will be returned there.
*/
{
@ -511,10 +524,12 @@ get_simul_efun_header (ident_t* name)
if (fx == -1)
fatal("Can't find simul_efun %s", get_txt(name->name));
return get_function_header(simul_efun_object->prog, fx);
return get_function_header_extended(simul_efun_object->prog, fx, progp, NULL);
}
else
{
if (progp)
*progp = simul_efun_table[name->u.global.sim_efun].program;
return &simul_efun_table[name->u.global.sim_efun].function;
}
} /* get_simul_efun_header() */

View file

@ -49,7 +49,8 @@ extern ident_t *all_simul_efuns;
extern void invalidate_simul_efuns (void);
extern Bool assert_simul_efun_object(void);
extern string_t *query_simul_efun_file_name(void);
extern function_t *get_simul_efun_header(ident_t* name) __attribute__((nonnull));
extern program_t *get_simul_efun_program();
extern function_t *get_simul_efun_header(ident_t* name, const program_t **progp) __attribute__((nonnull(1)));
extern void sefun_driver_info (svalue_t *svp, int value) __attribute__((nonnull(1)));
#ifdef GC_SUPPORT

View file

@ -87,6 +87,7 @@
#include "mstrings.h"
#include "object.h"
#include "pkg-python.h"
#include "prolang.h"
#include "ptrtable.h"
#include "random.h"
#include "sent.h"
@ -1195,6 +1196,15 @@ svalue_to_string ( fmt_state_t *st
break;
}
case T_LPCTYPE:
{
stradd(st, &str, "[");
stradd(st, &str, get_lpctype_name(obj->u.lpctype));
stradd(st, &str, "]");
break;
}
#ifdef USE_PYTHON
case T_PYTHON:
{

View file

@ -493,6 +493,161 @@ get_escaped_character (p_int c, char* buf, size_t buflen)
return 0;
} /* get_escaped_character() */
/*--------------------------------------------------------------------*/
bool
string_needs_escape (const char * text, size_t len, bool allow_unicode)
/* Checks whether <text> (of size <len>) contains characters that would
* need escaping. If <allow_unicode> is false, all characters > 0x7f
* will need escape. Returns the number of additional bytes needed.
*/
{
for (size_t i = 0; i < len; i++)
{
char c = text[i];
if (c < 0x20)
return true;
if (c > 0x7f)
{
if (allow_unicode)
continue;
else
return true;
}
if (isescaped(c))
return true;
}
return false;
} /* string_needs_escape() */
/*--------------------------------------------------------------------*/
size_t
escape_string (const char * text, size_t len, char * buf, size_t buflen, bool allow_unicode)
/* Escapes all characters of <text> (of size <len>) and puts the result
* into <buf> (of size <buflen>)). If the target buffer is not big enough,
* this function will return 0, otherwise returns the number of bytes
* written. No final zero byte is written.
*/
{
if (len > buflen)
return 0;
if (!string_needs_escape(text, len, allow_unicode))
{
memcpy(buf, text, len);
return len;
}
else
{
char * dest = buf;
for (size_t i = 0; i < len; )
{
p_int c;
size_t clen = utf8_to_unicode(text + i, len - i, &c);
if (!clen)
{
c = *(unsigned char*)text;
i++;
}
else
i += clen;
if (!allow_unicode || c < 0x80)
{
int s = get_escaped_character(c, dest, buf + buflen - dest);
if (!s)
return 0;
dest += s;
}
else if (dest + 4 <= buf + buflen)
dest += unicode_to_utf8(c, dest);
else
return 0;
}
return dest - buf;
}
} /* escape_string() */
/*--------------------------------------------------------------------*/
size_t
unescape_string (const char * text, size_t len, char * buf, size_t buflen)
/* Copies <text> (of size <len>) into <buf> (of size <buflen>) and
* thereby unescaping any escaped characters. If the target buffer is
* not big enough, this function will return 0, otherwise returns the
* number of bytes written (or 0 for any other error).
*/
{
char *dest = buf, *end = buf + buflen;
for (int i = 0; i < len ; i++)
{
if (dest == end)
return 0;
if (text[i] == '\\')
{
switch (text[++i])
{
case '0': *dest++ = '\0'; break;
case 'a': *dest++ = '\007'; break;
case 'b': *dest++ = '\b' ; break;
case 'e': *dest++= '\033'; break;
case 't': *dest++ = '\t' ; break;
case 'n': *dest++ = '\n' ; break;
case 'r': *dest++ = '\r' ; break;
case 'u':
case 'U':
case 'x':
{
int num_digits = (text[i] == 'x') ? 2 : (text[i] == 'u') ? 4 : 8;
int check_digits = (text[i] != 'x');
int value = 0;
while (num_digits > 0)
{
int c = text[i+1];
if (c >= '0' && c <= '9')
value = (value<<4) + (c - '0');
else if (c >= 'a' && c <= 'f')
value = (value<<4) + (c - 'a');
else
break;
i++;
num_digits--;
}
/* All digits for u/U, at least one digit for x. */
if (check_digits ? (num_digits > 0) : (num_digits == 2))
return 0;
/* Unicode range. */
if (value >= 0x110000)
return 0;
if (dest + 4 > end)
return 0;
dest += unicode_to_utf8(value, dest);
break;
}
default:
*dest++ = text[i];
break;
}
}
else
*dest++ = text[i];
}
return dest - buf;
} /* unescape_string() */
/*====================================================================*/

View file

@ -35,6 +35,9 @@ extern void strbuf_copy (strbuf_t *buf, char *cbuf);
extern string_t * trim_all_spaces (const string_t * txt);
extern char * xstrncpy(char * dest, const char * src, size_t num);
extern size_t get_escaped_character(p_int c, char* buf, size_t buflen);
extern bool string_needs_escape(const char * text, size_t len, bool allow_unicode);
extern size_t escape_string(const char * text, size_t len, char * buf, size_t buflen, bool allow_unicode);
extern size_t unescape_string(const char * text, size_t len, char * buf, size_t buflen);
extern size_t parse_input_encoding(string_t* encoding, bool* ignore, bool* replace);

View file

@ -70,6 +70,9 @@ union u {
coroutine_t *coroutine;
/* T_COROUTINE: pointer to the coroutine structure.
*/
lpctype_t *lpctype;
/* T_LPCTYPE: pointer to the type object.
*/
#ifdef FLOAT_FORMAT_2
double float_number;
/* T_FLOAT: the double value for this float in FLOAT_FORMAT_2.
@ -223,33 +226,34 @@ struct svalue_s
#ifdef USE_PYTHON
#define T_PYTHON 0xf /* a Python object */
#endif
#define T_LPCTYPE 0x10
#define T_CALLBACK 0x10
#define T_CALLBACK 0x11
/* A callback structure referenced from the stack to allow
* proper cleanup during error recoveries. The interpreter
* knows how to free it, but that's all.
*/
#define T_ERROR_HANDLER 0x11
#define T_ERROR_HANDLER 0x12
/* Not an actual value, this is used internally for cleanup
* operations. See the description of the error_handler() member
* for details.
*/
#define T_BREAK_ADDR 0x12
#define T_BREAK_ADDR 0x13
/* Not an actual type, it's used internally for saving
* the address where break statements within switch statements
* should branch to.
*/
#define T_ARG_FRAME 0x13
#define T_ARG_FRAME 0x14
/* Not an actual type, it's used internally for saving
* the surrounding argument frame pointer, when a new
* argument frame is created.
*/
#undef T_NULL /* There is some T_NULL definition in system headers. */
#define T_NULL 0x14
#define T_NULL 0x15
/* Not an actual type, this is used in the efun_lpc_types[] table
* to encode the acceptance of '0' instead of the real datatype.
*/
@ -434,6 +438,7 @@ struct svalue_s
#define TF_BYTES (1 << T_BYTES)
#define TF_LWOBJECT (1 << T_LWOBJECT)
#define TF_COROUTINE (1 << T_COROUTINE)
#define TF_LPCTYPE (1 << T_LPCTYPE)
#define TF_ANYTYPE (~0)
/* This is used in the efun_lpc_types[]
@ -650,6 +655,15 @@ static INLINE svalue_t svalue_coroutine(coroutine_t * const cr)
return (svalue_t){ T_COROUTINE, {}, {.coroutine = cr } };
}
static INLINE svalue_t svalue_lpctype(lpctype_t * const t)
__attribute__((nonnull(1))) __attribute__((const));
static INLINE svalue_t svalue_lpctype(lpctype_t * const t)
/* Return an svalue for the lpctype <t>.
*/
{
return (svalue_t){ T_LPCTYPE, {}, {.lpctype = t } };
}
static INLINE svalue_t svalue_callback(callback_t * const cb)
__attribute__((nonnull(1))) __attribute__((const));
static INLINE svalue_t svalue_callback(callback_t * const cb)
@ -759,6 +773,15 @@ static INLINE void put_coroutine(svalue_t * const dest, coroutine_t * const cr)
*dest = svalue_coroutine(cr);
}
static INLINE void put_lpctype(svalue_t * const dest, lpctype_t * const t)
__attribute__((nonnull(1,2)));
static INLINE void put_lpctype(svalue_t * const dest, lpctype_t * const t)
/* Put the lpctype <t> into <dest>, which is considered empty.
*/
{
*dest = svalue_lpctype(t);
}
static INLINE void put_callback(svalue_t * const dest, callback_t * const cb)
__attribute__((nonnull(1,2)));
static INLINE void put_callback(svalue_t * const dest, callback_t * const cb)
@ -819,6 +842,9 @@ static INLINE void put_callback(svalue_t * const dest, callback_t * const cb)
#define push_coroutine(sp,val) \
( (sp)++, put_coroutine(sp,val) )
#define push_lpctype(sp,val) \
( (sp)++, put_lpctype(sp,val) )
#define push_callback(sp,val) \
( (sp)++, put_callback(sp,val) )

View file

@ -335,9 +335,10 @@ locate_out (program_t *prog)
prog->includes = MAKEOFFSET(include_t *, includes);
if (prog->lwo_call_cache)
prog->lwo_call_cache = MAKEOFFSET(call_cache_t *, lwo_call_cache);
prog->types = MAKEOFFSET(lpctype_t **, types);
if (prog->type_start)
{
prog->argument_types = MAKEOFFSET(lpctype_t **, argument_types);
prog->argument_types = MAKEOFFSET(unsigned short *, argument_types);
prog->type_start = MAKEOFFSET(unsigned short *, type_start);
}
return MY_TRUE;
@ -383,9 +384,10 @@ locate_in (program_t *prog)
prog->includes = MAKEPTR(include_t*, includes);
if (prog->lwo_call_cache)
prog->lwo_call_cache = MAKEPTR(call_cache_t *, lwo_call_cache);
prog->types = MAKEPTR(lpctype_t **, types);
if (prog->type_start)
{
prog->argument_types = MAKEPTR(lpctype_t **, argument_types);
prog->argument_types = MAKEPTR(unsigned short *, argument_types);
prog->type_start = MAKEPTR(unsigned short *, type_start);
}
@ -1077,6 +1079,7 @@ swap_svalues (svalue_t *svp, mp_int num, varblock_t *block)
case T_LWOBJECT:
case T_CLOSURE:
case T_COROUTINE:
case T_LPCTYPE:
case T_LVALUE:
#ifdef USE_PYTHON
case T_PYTHON:
@ -1240,6 +1243,7 @@ check_swapped_values (mp_int num, unsigned char * p)
case T_LWOBJECT:
case T_CLOSURE:
case T_COROUTINE:
case T_LPCTYPE:
case T_LVALUE:
#ifdef USE_PYTHON
case T_PYTHON:
@ -1407,6 +1411,7 @@ dump_swapped_values (mp_int num, unsigned char * p, int indent)
case T_LWOBJECT:
case T_CLOSURE:
case T_COROUTINE:
case T_LPCTYPE:
case T_LVALUE:
#ifdef USE_PYTHON
case T_PYTHON:
@ -1599,6 +1604,7 @@ free_swapped_svalues (svalue_t *svp, mp_int num, unsigned char *p)
case T_MAPPING:
case T_NUMBER:
case T_FLOAT:
case T_LPCTYPE:
case T_LVALUE:
#ifdef USE_PYTHON
case T_PYTHON:
@ -2116,6 +2122,7 @@ read_unswapped_svalues (svalue_t *svp, mp_int num, unsigned char *p)
case T_LWOBJECT:
case T_CLOSURE:
case T_COROUTINE:
case T_LPCTYPE:
case T_LVALUE:
#ifdef USE_PYTHON
case T_PYTHON:

View file

@ -5,17 +5,22 @@
*/
#include <assert.h>
#include <wctype.h>
#include "gcollect.h"
#include "lex.h"
#include "lwobject.h"
#include "main.h"
#include "object.h"
#include "pkg-python.h"
#include "types.h"
#include "simul_efun.h"
#include "simulate.h"
#include "structs.h"
#include "xalloc.h"
#include "i-current_object.h"
#include "../mudlib/sys/driver_info.h"
/* Base types are statically allocated. */
@ -27,6 +32,7 @@ lpctype_t _lpctype_mixed = { 0, { TCLASS_PRIMARY, true }, {TYPE_ANY},
lpctype_t _lpctype_closure = { 0, { TCLASS_PRIMARY, true }, {TYPE_CLOSURE}, NULL, NULL };
lpctype_t _lpctype_symbol = { 0, { TCLASS_PRIMARY, true }, {TYPE_SYMBOL}, NULL, NULL };
lpctype_t _lpctype_coroutine = { 0, { TCLASS_PRIMARY, true }, {TYPE_COROUTINE}, NULL, NULL };
lpctype_t _lpctype_lpctype = { 0, { TCLASS_PRIMARY, true }, {TYPE_LPCTYPE}, NULL, NULL };
lpctype_t _lpctype_quoted_array = { 0, { TCLASS_PRIMARY, true }, {TYPE_QUOTED_ARRAY}, NULL, NULL };
lpctype_t _lpctype_void = { 0, { TCLASS_PRIMARY, true }, {TYPE_VOID}, NULL, NULL };
lpctype_t _lpctype_bytes = { 0, { TCLASS_PRIMARY, true }, {TYPE_BYTES}, NULL, NULL };
@ -44,6 +50,7 @@ lpctype_t *lpctype_mixed = &_lpctype_mixed;
lpctype_t *lpctype_closure = &_lpctype_closure;
lpctype_t *lpctype_symbol = &_lpctype_symbol;
lpctype_t *lpctype_coroutine = &_lpctype_coroutine;
lpctype_t *lpctype_lpctype = &_lpctype_lpctype;
lpctype_t *lpctype_quoted_array = &_lpctype_quoted_array;
lpctype_t *lpctype_any_struct = &_lpctype_any_struct;
lpctype_t *lpctype_any_object = &_lpctype_any_object;
@ -514,9 +521,9 @@ get_union_type (lpctype_t *head, lpctype_t* member)
lpctype_t *insert = head;
lpctype_t *result, *next_member;
if (member == NULL)
if (member == NULL || member == lpctype_void)
return ref_lpctype(head);
if (head == NULL)
if (head == NULL || head == lpctype_void)
return ref_lpctype(member);
if (head == lpctype_unknown || member == lpctype_unknown)
return lpctype_unknown;
@ -1150,6 +1157,292 @@ is_compatible_lwobject (lwobject_t* lwob, lpctype_t *t)
return is_compatible_program(lwob->prog, OBJECT_LIGHTWEIGHT, t);
} /* is_compatible_lwobject() */
/*-------------------------------------------------------------------------*/
struct lpctypename_s
{
const char *name;
lpctype_t *type;
};
static struct lpctypename_s lpctypenames[] = {
{ "status", &_lpctype_int },
{ "int", &_lpctype_int },
{ "string", &_lpctype_string },
{ "void", &_lpctype_void },
{ "mapping", &_lpctype_mapping },
{ "float", &_lpctype_float },
{ "mixed", &_lpctype_mixed },
{ "closure", &_lpctype_closure },
{ "symbol", &_lpctype_symbol },
{ "quoted_array", &_lpctype_quoted_array },
{ "bytes", &_lpctype_bytes },
{ "coroutine", &_lpctype_coroutine },
{ "lpctype", &_lpctype_lpctype },
{ "struct", &_lpctype_any_struct },
{ "object", &_lpctype_any_object },
{ "lwobject", &_lpctype_any_lwobject },
{ NULL, NULL }
};
/*-------------------------------------------------------------------------*/
static const char*
skip_whitespace(const char* str, const char* end)
/* Skip any whitespace characters and return a pointer to the first
* non-whitespace character.
*/
{
while (str != end)
{
p_int c;
size_t clen = utf8_to_unicode(str, end - str, &c);
if (!clen || !iswspace(c))
return str;
str += clen;
}
return end;
} /* skip_whitespace() */
/*-------------------------------------------------------------------------*/
static const char*
skip_alunum(const char* str, const char* end)
/* Skip any alpha-numeric characters (incl. underscore) and return a pointer
* to the first non-matching character.
*/
{
while (str != end)
{
p_int c;
size_t clen = utf8_to_unicode(str, end - str, &c);
if (!clen || !(c < 128 ? isalunum(c) : iswalnum((wint_t)c)))
return str;
str += clen;
}
return end;
} /* skip_alunum() */
/*-------------------------------------------------------------------------*/
lpctype_t *
parse_lpctype (const char** start, const char* end)
/* Parse the string starting at <*start> as a type, not going beyond <end>.
* Return the resulting type (or NULL upon an error). On success <*start>
* will then point to the next unprocessed character.
*/
{
const char* str = *start;
lpctype_t *result = NULL;
while (true)
{
lpctype_t *part;
p_int c;
size_t clen = utf8_to_unicode(str, end - str, &c);
if (!clen)
break;
if (c == '<')
{
str += clen;
part = parse_lpctype(&str, end);
if (part == NULL)
break;
if (*str != '>')
break;
str++;
}
else if (iswspace(c))
{
str += clen;
continue;
}
else if (c < 128 ? isalunum(c) : iswalnum((wint_t)c))
{
const char* keyword = str;
/* Skip to end of alphanumeric characters. */
str = skip_alunum(str + clen, end);
part = NULL;
for (struct lpctypename_s *lpctypename = lpctypenames; lpctypename->name != NULL; lpctypename++)
{
if (!strncmp(lpctypename->name, keyword, str - keyword)
&& strlen(lpctypename->name) == str - keyword)
{
part = lpctypename->type;
break;
}
}
#ifdef USE_PYTHON
if (part == NULL)
{
ident_t *p = find_shared_identifier_n(keyword, str - keyword, I_TYPE_PYTHON_TYPE, 0);
while (p && p->type != I_TYPE_PYTHON_TYPE)
p = p->inferior;
if (p)
part = get_python_type(p->u.python_type_id);
}
#endif
if (part == NULL)
break;
str = skip_whitespace(str, end);
if (part == lpctype_any_struct)
{
/* Struct needs to have specific a name or 'mixed'. */
const char* structname = str;
str = skip_alunum(str, end);
if (structname == str)
break;
if (str - structname != 5 || memcmp(structname, "mixed", 5))
{
/* First look at the current program. */
program_t *prog = get_current_object_program();
string_t *name;
if (!prog)
break;
name = find_tabled_str_n(structname, str - structname, STRING_UTF8);
if (!name)
break;
part = NULL;
for (int idx = 0; idx < prog->num_structs; idx++)
{
struct_type_t *st = prog->struct_defs[idx].type;
if (st->name->name == name
&& !(prog->struct_defs[idx].flags & (TYPE_MOD_PRIVATE|NAME_HIDDEN)))
{
part = get_struct_type(st);
break;
}
}
if (!part)
{
/* Look at global struct definitions. */
ident_t *p = find_shared_identifier_mstr(name, I_TYPE_GLOBAL, 0);
while (p && p->type != I_TYPE_GLOBAL)
p = p->inferior;
if (p)
{
struct_type_t *st = NULL;
if (p->u.global.sefun_struct_id != I_GLOBAL_SEFUN_STRUCT_NONE)
st = get_simul_efun_program()->struct_defs[p->u.global.sefun_struct_id].type;
else if (p->u.global.std_struct_id != I_GLOBAL_STD_STRUCT_NONE)
st = get_std_struct_type(p->u.global.std_struct_id);
if (st != NULL)
part = get_struct_type(st);
}
}
if (!part)
break;
}
str = skip_whitespace(str, end);
}
else if ((part == lpctype_any_object || part == lpctype_any_lwobject)
&& str != end && *str == '"')
{
/* Specific (lw)object type. */
char buf[512];
const char *obname = ++str;
size_t oblen;
string_t *obstr;
while (str != end)
{
if (*str == '\\')
{
str++;
if (str == end)
break;
}
else if (*str == '"')
break;
str++;
}
if (str == end)
break;
oblen = unescape_string(obname, str - obname, buf, sizeof(buf));
if (!oblen)
break;
obstr = new_n_unicode_mstring(buf, oblen);
if (!obstr)
break;
if (part == lpctype_any_object)
part = get_object_type(obstr);
else
part = get_lwobject_type(obstr);
free_mstring(obstr);
str = skip_whitespace(str+1, end);
}
}
else
break;
/* At this point we have single type in <part>.
* Any whitespaces have been skipped.
*/
if (part != lpctype_void)
{
while (str != end && *str == '*')
{
lpctype_t *dummy = part;
part = get_array_type(part);
free_lpctype(dummy);
str = skip_whitespace(str+1, end);
}
}
if (result == NULL)
result = part;
else if (part == lpctype_void)
break;
else
{
lpctype_t *u = get_union_type(result, part);
free_lpctype(result);
free_lpctype(part);
result = u;
}
if (str == end || *str == '>')
{
*start = str;
return result;
}
if (result == lpctype_void || *str != '|')
break;
str++;
}
/* We get here on errors only. */
free_lpctype(result);
return NULL;
} /* parse_lpctype() */
/*-------------------------------------------------------------------------*/
/* The same definitions are in sys/lpctypes.h for the mudlibs. */
@ -1175,6 +1468,8 @@ get_type_compat_int (lpctype_t *t)
val++;
if (val >= COMPAT_TYPE_STRUCT)
val++;
if (val >= COMPAT_TYPE_LWOBJECT)
val++;
return val;
}

View file

@ -13,6 +13,7 @@
#include "driver.h"
#include "typedefs.h"
#include "svalue.h"
typedef enum type_classes type_classes_t;
typedef enum primary_types primary_types_t;
@ -49,11 +50,11 @@ enum type_classes
TCLASS_PRIMARY,
TCLASS_STRUCT,
TCLASS_OBJECT,
#ifdef USE_PYTHON
TCLASS_PYTHON,
#endif
TCLASS_ARRAY,
TCLASS_UNION
TCLASS_UNION,
#ifdef USE_PYTHON
TCLASS_PYTHON
#endif
};
/* --- Primary type values --- */
@ -71,6 +72,7 @@ enum primary_types
TYPE_QUOTED_ARRAY = 9,
TYPE_BYTES = 10,
TYPE_COROUTINE = 11,
TYPE_LPCTYPE = 12,
};
/* -- Object types -- */
@ -264,7 +266,7 @@ struct fulltype_s
extern lpctype_t *lpctype_int, *lpctype_string, *lpctype_bytes,
*lpctype_mapping, *lpctype_float, *lpctype_mixed,
*lpctype_closure, *lpctype_symbol, *lpctype_coroutine,
*lpctype_quoted_array,
*lpctype_lpctype, *lpctype_quoted_array,
*lpctype_any_struct, *lpctype_any_object,
*lpctype_any_lwobject, *lpctype_void, *lpctype_unknown;
@ -272,7 +274,7 @@ extern lpctype_t *lpctype_int, *lpctype_string, *lpctype_bytes,
extern lpctype_t _lpctype_int, _lpctype_string, _lpctype_bytes,
_lpctype_mapping, _lpctype_float, _lpctype_mixed,
_lpctype_closure, _lpctype_symbol, _lpctype_coroutine,
_lpctype_quoted_array,
_lpctype_lpctype, _lpctype_quoted_array,
_lpctype_any_struct, _lpctype_any_object,
_lpctype_any_lwobject, _lpctype_void, _lpctype_unknown;
@ -296,6 +298,7 @@ extern void _free_lpctype(lpctype_t *t);
extern bool lpctype_contains(lpctype_t* src, lpctype_t* dest);
extern bool is_compatible_object(object_t *ob, lpctype_t *t);
extern bool is_compatible_lwobject(lwobject_t *ob, lpctype_t *t);
extern lpctype_t *parse_lpctype(const char** start, const char* end);
extern int get_type_compat_int(lpctype_t *t);
extern void types_driver_info(svalue_t *svp, int value) __attribute__((nonnull(1)));
@ -364,6 +367,18 @@ static INLINE fulltype_t get_fulltype_flags(lpctype_t *t, typeflags_t f)
return ((fulltype_t) { .t_type = t, .t_flags = f });
}
static INLINE void put_ref_lpctype(svalue_t * const dest, lpctype_t * const t)
__attribute__((nonnull(1,2)));
static INLINE void put_ref_lpctype(svalue_t * const dest, lpctype_t * const t)
/* Put the type <t> into <dest>, which is considered empty,
* and increment the refcount of <t>.
*/
{
*dest = svalue_lpctype(ref_lpctype(t));
}
#define push_ref_lpctype(sp,val) put_ref_lpctype(++(sp),val)
#ifdef GC_SUPPORT
extern void clear_lpctype_ref (lpctype_t *t);

View file

@ -23,6 +23,7 @@ int deep_eq(mixed arg1, mixed arg2)
case T_OBJECT:
case T_LWOBJECT:
case T_COROUTINE:
case T_LPCTYPE:
return arg1 == arg2;
case T_POINTER:

View file

@ -253,6 +253,20 @@ mixed *tests =
({ "call_direct_resolved array 2", 0, (: int* result; return deep_eq(call_direct_resolved(&result, ({clone,clone,object_name(clone),this_object(),0}), "g", 10, ({ 20 })), ({0, 0, 0, 0, 0})) && deep_eq(result, ({ 0, 0, 0, 0, 0})); :) }),
({ "call_direct_resolved array 3", 0, (: int* result; return deep_eq(call_direct_resolved(&result, ({clone,clone,object_name(clone),this_object(),0}), "h", 10, ({ 20 })), ({0, 0, 0, 0, 0})) && deep_eq(result, ({ 0, 0, 0, 0, 0})); :) }),
({ "call_out", 0, (: last_rt_warning = 0; call_out("ThisFunctionDoesNotExist", 10); return sizeof(last_rt_warning); :) }),
({ "check_type 1", 0, (: check_type(10,[int]) == 1 :) }),
({ "check_type 2", 0, (: check_type(10,[int|float]) == 1 :) }),
({ "check_type 3", 0, (: check_type(10,[string]) == 0 :) }),
({ "check_type 4", 0, (: check_type(10,[mixed]) == 1 :) }),
({ "check_type 5", 0, (: check_type(10,[void]) == 0 :) }),
({ "check_type 6", 0, (: check_type(({10}),[int*]) == 1 :) }),
({ "check_type 7", 0, (: check_type(({10}),[string*]) == 0 :) }),
({ "check_type 8", 0, (: check_type(({10}),[<int|string>*]) == 1 :) }),
({ "check_type 9", 0, (: check_type(({10}),[int*|string*]) == 1 :) }),
({ "check_type 10", 0, (: check_type(({10}),[int*|string*]) == 1 :) }),
({ "check_type 11", 0, (: check_type(({10,"X"}),[<int|string>*]) == 1 :) }),
({ "check_type 12", 0, (: check_type(({10,"X"}),[int*|string*]) == 0 :) }),
({ "check_type 13", 0, (: check_type(({10}),[mixed]) == 1 :) }),
({ "check_type 14", 0, (: check_type(({10}),[mixed*]) == 1 :) }),
({ "crypt", TF_ERROR, (: crypt("ABC", "$$") :) }),
({ "ctime", TF_DONTCHECKERROR, (: ctime(-1) :) }), /* This must be the first ctime call of this test suite. */
({ "clone_object 1", 0,
@ -637,6 +651,17 @@ mixed *tests =
use_object_structs: 1))));
:)
}),
({ "compile_string with decltype of these tests", 0,
(:
string file = read_file(__FILE__, 0, 0, "UTF-8");
string header = explode(file, "// String compiler header boundary\n")[1];
string code = explode(file, "// String compiler test boundary\n")[1];
return funcall(compile_string(0, "#define TF_ERROR 1\n#define TF_DONTCHECKERROR 2\n" + header + "decltype(" + code + ")", (<cs_opts>
use_object_functions: 1,
use_object_variables: 1,
use_object_structs: 1))) in [mixed*];
:)
}),
({ "compile_string (simple block)", 0,
(:
return funcall(compile_string(0, "return 42;", (<cs_opts> compile_block: 1)))==42;
@ -1079,6 +1104,48 @@ mixed *tests =
({ "sprintf doc41", 0, (: sprintf("%8.3G",123.5) == " 124" :) }),
({ "sprintf doc42", 0, (: sprintf("%8.6g",123.5) == " 123.5" :) }),
({ "to_array 1", 0, (: deep_eq(to_array([int]), ({ [int] })) :) }),
({ "to_array 2", 0, (: deep_eq(to_array([void]), ({ [void] })) :) }),
({ "to_array 3", 0, (: deep_eq(to_array([int*]), ({ [int*] })) :) }),
({ "to_array 4", 0, (: deep_eq(to_array([<int|string>*]), ({ [<int|string>*] })) :) }),
({ "to_array 5", 0, (: deep_eq(mkmapping(to_array([int|string])), ([ [int], [string] ])) :) }),
({ "to_array 6", 0, (: deep_eq(mkmapping(to_array([int|float|string])), ([ [int], [float], [string] ])) :) }),
({ "to_array 7", 0, (: deep_eq(mkmapping(to_array([int|<int|string>*])), ([ [int], [<int|string>*] ])) :) }),
({ "to_lpctype 1", 0, (: to_lpctype("int") == [int] :) }),
({ "to_lpctype 2", 0, (: to_lpctype("<int>") == [int] :) }),
({ "to_lpctype 3", 0, (: to_lpctype("<<int>>") == [int] :) }),
({ "to_lpctype 4", 0, (: to_lpctype("int|string") == [int|string] :) }),
({ "to_lpctype 5", 0, (: to_lpctype("string|int") == [int|string] :) }),
({ "to_lpctype 6", 0, (: to_lpctype("mixed") == [mixed] :) }),
({ "to_lpctype 7", 0, (: to_lpctype("void") == [void] :) }),
({ "to_lpctype 8", 0, (: to_lpctype("struct mixed") == [struct mixed] :) }),
({ "to_lpctype 9", 0, (: to_lpctype("struct test_struct") == [struct test_struct] :) }),
({ "to_lpctype 10", 0, (: to_lpctype("struct compile_string_options") == [struct compile_string_options] :) }),
({ "to_lpctype 11", 0, (: to_lpctype("int**") == [int**] :) }),
({ "to_lpctype 12", 0, (: to_lpctype("<int*|string>*") == [<int*|string>*] :) }),
({ "to_lpctype 13", 0, (: to_lpctype("object") == [object] :) }),
({ "to_lpctype 14", 0, (: to_lpctype("lwobject") == [lwobject] :) }),
({ "to_lpctype 15", 0, (: to_lpctype("lwobject \"/object/\u00c4\"") == [lwobject "/object/\u00c4"] :) }),
({ "to_lpctype 16", TF_ERROR, (: to_lpctype("stuff") :) }),
({ "to_lpctype 17", TF_ERROR, (: to_lpctype("int what") :) }),
({ "to_lpctype 18", TF_ERROR, (: to_lpctype("<int> what") :) }),
({ "to_lpctype 17", TF_ERROR, (: to_lpctype("int>") :) }),
({ "to_lpctype 18", TF_ERROR, (: to_lpctype("struct whatever") :) }),
({ "to_string(lpctype) 1", 0, (: to_lpctype(to_string([int])) == [int] :) }),
({ "to_string(lpctype) 2", 0, (: to_lpctype(to_string([string|int])) == [int|string] :) }),
({ "to_string(lpctype) 3", 0, (: to_lpctype(to_string([mixed])) == [mixed] :) }),
({ "to_string(lpctype) 4", 0, (: to_lpctype(to_string([void])) == [void] :) }),
({ "to_string(lpctype) 5", 0, (: to_lpctype(to_string([struct mixed])) == [struct mixed] :) }),
({ "to_string(lpctype) 6", 0, (: to_lpctype(to_string([struct test_struct])) == [struct test_struct] :) }),
({ "to_string(lpctype) 7", 0, (: to_lpctype(to_string([struct compile_string_options])) == [struct compile_string_options] :) }),
({ "to_string(lpctype) 8", 0, (: to_lpctype(to_string([int**])) == [int**] :) }),
({ "to_string(lpctype) 9", 0, (: to_lpctype(to_string([<int*|string>*])) == [<int*|string>*] :) }),
({ "to_string(lpctype) 10", 0, (: to_lpctype(to_string([object])) == [object] :) }),
({ "to_string(lpctype) 11", 0, (: to_lpctype(to_string([lwobject])) == [lwobject] :) }),
({ "to_string(lpctype) 12", 0, (: to_lpctype(to_string([lwobject "/object/\u00c4"])) == [lwobject "/object/\u00c4"] :) }),
({ "to_text 1", 0, (: deep_eq(to_array(to_text( ({}) )), ({}) ) :) }),
({ "to_text 2", 0, (: deep_eq(to_array(to_text( ({0, 65, 66, 67}) )), ({0, 65, 66, 67}) ) :) }),
({ "to_text 3", 0, (: deep_eq(to_array(to_text( copy(({65, 66, 67, "ABC"})) )), ({65, 66, 67}) ) :) }),
@ -1253,6 +1320,17 @@ mixed *tests =
([1:"a";"b", 2:"c";"d"]),
to_struct((["a":1, "b": 4, "c": 16])),
to_struct(({1,2,3})),
[void],
[float],
[float|object],
[lpctype],
[struct mixed],
[struct test_struct],
[struct compile_string_options],
[string**],
[<symbol*|string>*],
[lwobject "/A/\"\n\""],
}))
{
if(!deep_eq(val, restore_value(save_value(val))))
@ -1328,6 +1406,7 @@ mixed *tests =
({ "variable_list 4", 0, (: deep_eq(variable_list(this_object(), RETURN_FUNCTION_NAME | RETURN_FUNCTION_TYPE),
({ "last_rt_warning", TYPE_MOD_POINTER|TYPE_STRING, "json_testdata", TYPE_MAPPING, "json_teststring", TYPE_STRING, "b", TYPE_BYTES, "dhe_testdata", TYPE_STRING, "global_var", TYPE_ANY, "clone", TYPE_OBJECT, "last_privi_op", TYPE_STRING, "last_privi_who", TYPE_ANY, "last_privi_args", TYPE_MOD_POINTER|TYPE_ANY, "tests", TYPE_MOD_POINTER|TYPE_ANY, "args", TYPE_NUMBER })) :) }),
({ "variable_list 5", 0, (: variable_list(this_object(), RETURN_VARIABLE_VALUE)[3] == b"\x00" :) }),
({ "variable_list 6", 0, (: deep_eq(variable_list(this_object(), RETURN_FUNCTION_LPCTYPE), ({ [string*], [mapping], [string], [bytes], [string], [mixed], [object], [string], [mixed], [mixed*], [mixed*], [int] })) :) }),
#ifdef __JSON__
({ "json_parse/_serialize 1", 0,

View file

@ -103,6 +103,55 @@ mixed *tests = ({
({ "&(bytes[]) in &(bytes[]) 1", 0, (: bytes str = b"Some String"; return &(str[5..7]) in &(str[5..7]) && &(str[5..5]) in &(str[5..7]) && &(str[0..0]) in &(str[5..7]); :) }),
({ "&(bytes[]) in &(bytes[]) 2", 0, (: bytes str = b"Some String"; return !(&(str[0..4]) in &(str[5..7])) && !(&(str[4..5]) in &(str[5..7])) && !(&(str[5..8]) in &(str[5..7])); :) }),
({ "[int] == [int]", 0, (: [int] == [int] :) }),
({ "[int] == [string]", 0, (: !([int] == [string]) :) }),
({ "[int|string] == [string|int]", 0, (: [int|string] == [string|int] :) }),
({ "[int|string] == [object|int]", 0, (: !([int|string] == [object|int]) :) }),
({ "[int] == [mixed]", 0, (: !([int] == [mixed]) :) }),
({ "[int] == [void]", 0, (: !([int] == [void]) :) }),
({ "[int] != [int]", 0, (: !([int] != [int]) :) }),
({ "[int] != [string]", 0, (: [int] != [string] :) }),
({ "[int|string] != [string|int]", 0, (: !([int|string] != [string|int]) :) }),
({ "[int|string] != [object|int]", 0, (: [int|string] != [object|int] :) }),
({ "[int] != [mixed]", 0, (: [int] != [mixed] :) }),
({ "[int] != [void]", 0, (: [int] != [void] :) }),
({ "[int] | [float]", 0, (: ([int] | [float]) == [int|float] :) }),
({ "[int] | [int|float]", 0, (: ([int] | [int|float]) == [int|float] :) }),
({ "[int|string] | [int|float]", 0, (: ([int|string] | [int|float]) == [int|string|float] :) }),
({ "[int] | [mixed]", 0, (: ([int] | [mixed]) == [mixed] :) }),
({ "[int] | [void]", 0, (: ([int] | [void]) == [int] :) }),
({ "[int] & [float]", 0, (: ([int] & [float]) == [void] :) }),
({ "[int] & [int|float]", 0, (: ([int] & [int|float]) == [int] :) }),
({ "[int|string] & [int|float]", 0, (: ([int|string] & [int|float]) == [int] :) }),
({ "[int] & [mixed]", 0, (: ([int] & [mixed]) == [int] :) }),
({ "[int] & [void]", 0, (: ([int] & [void]) == [void] :) }),
({ "[int] |= [float]", 0, (: lpctype val = [int]; val |= [float]; return val == [int|float]; :) }),
({ "[int] |= [int|float]", 0, (: lpctype val = [int]; val |= [int|float]; return val == [int|float]; :) }),
({ "[int|string] |= [int|float]", 0, (: lpctype val = [int|string]; val |= [int|float]; return val == [int|string|float]; :) }),
({ "[int] |= [mixed]", 0, (: lpctype val = [int]; val |= [mixed]; return val == [mixed]; :) }),
({ "[int] |= [void]", 0, (: lpctype val = [int]; val |= [void]; return val == [int]; :) }),
({ "[int] &= [float]", 0, (: lpctype val = [int]; val &= [float]; return val == [void]; :) }),
({ "[int] &= [int|float]", 0, (: lpctype val = [int]; val &= [int|float]; return val == [int]; :) }),
({ "[int|string] &= [int|float]", 0, (: lpctype val = [int|string]; val &= [int|float]; return val == [int]; :) }),
({ "[int] &= [mixed]", 0, (: lpctype val = [int]; val &= [mixed]; return val == [int]; :) }),
({ "[int] &= [void]", 0, (: lpctype val = [int]; val &= [void]; return val == [void]; :) }),
({ "[int] in [float]", 0, (: !([int] in [float]) :) }),
({ "[int] in [int|float]", 0, (: [int] in [int|float] :) }),
({ "[int|string] in [int|float]", 0, (: !([int|string] in [int|float]) :) }),
({ "[int] in [mixed]", 0, (: [int] in [mixed] :) }),
({ "[int] in [void]", 0, (: !([int] in [void]) :) }),
({ "[mixed] in [int]", 0, (: !([mixed] in [int]) :) }),
({ "[void] in [int]", 0, (: [void] in [int] :) }),
({ "decltype(42)", 0, (: decltype(42) == [int] :) }),
({ "decltype(int var)", 0, (: int var; return decltype(var) == [int]; :) }),
({ "decltype(fun())", 0, (: decltype(deep_eq("A","B")) == [int] :) }),
});
void run_test()