From fb691fa2ea7a9d9daab6d19ea76dee2d014ae18e Mon Sep 17 00:00:00 2001 From: Alexander Motzkau Date: Sun, 25 Sep 2022 22:20:32 +0200 Subject: [PATCH] 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. --- doc/LPC/decltype | 31 +++ doc/LPC/lpctypes | 48 +++++ doc/LPC/types | 7 +- doc/efun.de/functionlist.de | 6 + doc/efun.de/variable_list.de | 4 +- doc/efun/check_type | 16 ++ doc/efun/functionlist | 5 +- doc/efun/get_type_info | 2 +- doc/efun/to_array | 4 + doc/efun/to_lpctype | 12 ++ doc/efun/variable_list | 4 +- gdb/ldmud.py | 21 +- mudlib/sys/functionlist.h | 6 +- mudlib/sys/lpctypes.h | 2 + src/array.c | 2 + src/dumpstat.c | 1 + src/efuns.c | 88 +++++++++ src/efuns.h | 2 + src/exec.h | 26 ++- src/func_spec | 6 +- src/gcollect.c | 24 ++- src/i-svalue_hash.h | 4 + src/interpret.c | 172 ++++++++++++++--- src/lex.c | 7 +- src/make_func.y | 13 +- src/object.c | 97 +++++++++- src/pkg-python.c | 3 +- src/prolang.y | 365 +++++++++++++++++++++++++---------- src/simul_efun.c | 19 +- src/simul_efun.h | 3 +- src/sprintf.c | 10 + src/strfuns.c | 155 +++++++++++++++ src/strfuns.h | 3 + src/svalue.h | 36 +++- src/swap.c | 11 +- src/types.c | 299 +++++++++++++++++++++++++++- src/types.h | 27 ++- test/inc/deep_eq.inc | 1 + test/t-efuns.c | 79 ++++++++ test/t-operators.c | 49 +++++ 40 files changed, 1487 insertions(+), 183 deletions(-) create mode 100644 doc/LPC/decltype create mode 100644 doc/LPC/lpctypes create mode 100644 doc/efun/check_type create mode 100644 doc/efun/to_lpctype diff --git a/doc/LPC/decltype b/doc/LPC/decltype new file mode 100644 index 00000000..8c740390 --- /dev/null +++ b/doc/LPC/decltype @@ -0,0 +1,31 @@ +NAME + decltype + +SYNTAX + decltype() + +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) diff --git a/doc/LPC/lpctypes b/doc/LPC/lpctypes new file mode 100644 index 00000000..2e2d7e9a --- /dev/null +++ b/doc/LPC/lpctypes @@ -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) diff --git a/doc/LPC/types b/doc/LPC/types index 415df926..ca176190 100644 --- a/doc/LPC/types +++ b/doc/LPC/types @@ -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) diff --git a/doc/efun.de/functionlist.de b/doc/efun.de/functionlist.de index 2845648d..70d131fb 100644 --- a/doc/efun.de/functionlist.de +++ b/doc/efun.de/functionlist.de @@ -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) diff --git a/doc/efun.de/variable_list.de b/doc/efun.de/variable_list.de index c57822ff..a05b5c18 100644 --- a/doc/efun.de/variable_list.de +++ b/doc/efun.de/variable_list.de @@ -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) diff --git a/doc/efun/check_type b/doc/efun/check_type new file mode 100644 index 00000000..01977cda --- /dev/null +++ b/doc/efun/check_type @@ -0,0 +1,16 @@ +SYNOPSIS + int check_type(mixed arg, lpctype type) + +DESCRIPTION + Returns 1 if the first argument fulfills the 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) diff --git a/doc/efun/functionlist b/doc/efun/functionlist index 0885dc8c..7ac8e34f 100644 --- a/doc/efun/functionlist +++ b/doc/efun/functionlist @@ -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), diff --git a/doc/efun/get_type_info b/doc/efun/get_type_info index f776f146..a0de5ada 100644 --- a/doc/efun/get_type_info +++ b/doc/efun/get_type_info @@ -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) diff --git a/doc/efun/to_array b/doc/efun/to_array index e686ee87..8f30a7fc 100644 --- a/doc/efun/to_array +++ b/doc/efun/to_array @@ -5,6 +5,7 @@ SYNOPSIS mixed * to_array(quotedarray) mixed * to_array(mixed *) mixed * to_array(struct) + mixed * to_array(lpctype) (int*) @@ -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 is known at compile-time. This will not be fixed - use the diff --git a/doc/efun/to_lpctype b/doc/efun/to_lpctype new file mode 100644 index 00000000..dc681c7c --- /dev/null +++ b/doc/efun/to_lpctype @@ -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 [] literal. + +HISTORY + Introduced in LDMud 3.6.7. + +SEE ALSO + to_string(E), lpctypes(LPC) diff --git a/doc/efun/variable_list b/doc/efun/variable_list index c7e98ec0..0991f9ca 100644 --- a/doc/efun/variable_list +++ b/doc/efun/variable_list @@ -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) diff --git a/gdb/ldmud.py b/gdb/ldmud.py index b1e34b3c..933a614b 100644 --- a/gdb/ldmud.py +++ b/gdb/ldmud.py @@ -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): diff --git a/mudlib/sys/functionlist.h b/mudlib/sys/functionlist.h index 61dcd649..db805059 100644 --- a/mudlib/sys/functionlist.h +++ b/mudlib/sys/functionlist.h @@ -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() */ diff --git a/mudlib/sys/lpctypes.h b/mudlib/sys/lpctypes.h index 927faaa7..2824fada 100644 --- a/mudlib/sys/lpctypes.h +++ b/mudlib/sys/lpctypes.h @@ -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 */ diff --git a/src/array.c b/src/array.c index ecf4675b..06eee3e1 100644 --- a/src/array.c +++ b/src/array.c @@ -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() */ diff --git a/src/dumpstat.c b/src/dumpstat.c index ea6abe20..cd749044 100644 --- a/src/dumpstat.c +++ b/src/dumpstat.c @@ -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: diff --git a/src/efuns.c b/src/efuns.c index a23c0849..87446e0c 100644 --- a/src/efuns.c +++ b/src/efuns.c @@ -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 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 against 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 diff --git a/src/efuns.h b/src/efuns.h index e586598f..de2b3360 100644 --- a/src/efuns.h +++ b/src/efuns.h @@ -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); diff --git a/src/exec.h b/src/exec.h index 8ffcbaf3..7b025ea2 100644 --- a/src/exec.h +++ b/src/exec.h @@ -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 , 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 : + * - 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; diff --git a/src/func_spec b/src/func_spec index d6be8360..246ffbd0 100644 --- a/src/func_spec +++ b/src/func_spec @@ -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); diff --git a/src/gcollect.c b/src/gcollect.c index 834d2ccf..43c44b8b 100644 --- a/src/gcollect.c +++ b/src/gcollect.c @@ -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) { diff --git a/src/i-svalue_hash.h b/src/i-svalue_hash.h index d4339941..79825971 100644 --- a/src/i-svalue_hash.h +++ b/src/i-svalue_hash.h @@ -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 diff --git a/src/interpret.c b/src/interpret.c index d1ccd3a3..a2f9f50a 100644 --- a/src/interpret.c +++ b/src/interpret.c @@ -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 --- */ { /* Check the top value off the stack against the type - * at prog->argument_types[]. Raise an error if - * it doesn't match. Do nothing otherwise. - * contains a value of enum type_check_operation to - * give a specific error message. + * at prog->types[]. Raise an error if it doesn't match. + * Do nothing otherwise. 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 --- */ + { + /* Push the type at prog->types[] 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() diff --git a/src/lex.c b/src/lex.c index 2a998636..8e9f42bc 100644 --- a/src/lex.c +++ b/src/lex.c @@ -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) diff --git a/src/make_func.y b/src/make_func.y index 1b750873..46d858cd 100644 --- a/src/make_func.y +++ b/src/make_func.y @@ -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 VOID MIXED UNKNOWN NUL STRUCT -%type INT STRING BYTES OBJECT MAPPING FLOAT CLOSURE SYMBOL QUOTED_ARRAY LWOBJECT COROUTINE +%type INT STRING BYTES OBJECT MAPPING FLOAT CLOSURE SYMBOL QUOTED_ARRAY LWOBJECT COROUTINE LPCTYPE %type 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; diff --git a/src/object.c b/src/object.c index d5a5246f..02c1d0be 100644 --- a/src/object.c +++ b/src/object.c @@ -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': diff --git a/src/pkg-python.c b/src/pkg-python.c index cecb9a7c..1c889485 100644 --- a/src/pkg-python.c +++ b/src/pkg-python.c @@ -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; diff --git a/src/prolang.y b/src/prolang.y index 3630ee44..620b7d6d 100644 --- a/src/prolang.y +++ b/src/prolang.y @@ -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 . * 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 . */ +#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 . + */ + +#define ARGUMENT_TYPE_INDEX(n) GET_BLOCK(A_ARGUMENT_TYPE_INDEX)[n] + /* Get the index into A_TYPES at position . + */ #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 | and |. +/* Operator type table for the binary or, + * allowing |, | and | */ 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 | and |. + */ +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 ( with entries) - * against the function definition with argument type list. + * against the function definition with argument type index + * list into . If is NULL, then a direct lookup into + * 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 '(' + { + $
$ = CURRENT_PROGRAM_SIZE; + compiling_decltype++; + } + expr0 ')' + { + compiling_decltype--; + + $$.type = get_fulltype_flags(lpctype_lpctype, TYPE_MOD_LITERAL); + $$.start = CURRENT_PROGRAM_SIZE = $
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 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 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 . */ @@ -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. diff --git a/src/simul_efun.c b/src/simul_efun.c index ffc20f53..e7add771 100644 --- a/src/simul_efun.c +++ b/src/simul_efun.c @@ -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 . * must be a valid simul-efun identifier (i.e. there * must be a simul-efun object with such a function). + * If 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() */ diff --git a/src/simul_efun.h b/src/simul_efun.h index c63276d6..f9ea4e41 100644 --- a/src/simul_efun.h +++ b/src/simul_efun.h @@ -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 diff --git a/src/sprintf.c b/src/sprintf.c index a861e699..48faa789 100644 --- a/src/sprintf.c +++ b/src/sprintf.c @@ -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: { diff --git a/src/strfuns.c b/src/strfuns.c index 4a7bc3eb..b2866aff 100644 --- a/src/strfuns.c +++ b/src/strfuns.c @@ -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 (of size ) contains characters that would + * need escaping. If 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 (of size ) and puts the result + * into (of size )). 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 (of size ) into (of size ) 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() */ /*====================================================================*/ diff --git a/src/strfuns.h b/src/strfuns.h index ca1c6929..f3b6a1cd 100644 --- a/src/strfuns.h +++ b/src/strfuns.h @@ -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); diff --git a/src/svalue.h b/src/svalue.h index c0761359..260e6169 100644 --- a/src/svalue.h +++ b/src/svalue.h @@ -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 . + */ +{ + 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 into , 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) ) diff --git a/src/swap.c b/src/swap.c index 927f632d..d247d418 100644 --- a/src/swap.c +++ b/src/swap.c @@ -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: diff --git a/src/types.c b/src/types.c index e4c8e116..0a07535f 100644 --- a/src/types.c +++ b/src/types.c @@ -5,17 +5,22 @@ */ #include +#include #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 . + * 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 . + * 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; } diff --git a/src/types.h b/src/types.h index 8417db7f..9ebcde4d 100644 --- a/src/types.h +++ b/src/types.h @@ -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 into , which is considered empty, + * and increment the refcount of . + */ +{ + *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); diff --git a/test/inc/deep_eq.inc b/test/inc/deep_eq.inc index bf50c882..87746e64 100644 --- a/test/inc/deep_eq.inc +++ b/test/inc/deep_eq.inc @@ -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: diff --git a/test/t-efuns.c b/test/t-efuns.c index ed763a65..18d748d2 100644 --- a/test/t-efuns.c +++ b/test/t-efuns.c @@ -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}),[*]) == 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"}),[*]) == 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 + ")", ( + 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;", ( 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([*]), ({ [*] })) :) }), + ({ "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], [*] ])) :) }), + + ({ "to_lpctype 1", 0, (: to_lpctype("int") == [int] :) }), + ({ "to_lpctype 2", 0, (: to_lpctype("") == [int] :) }), + ({ "to_lpctype 3", 0, (: to_lpctype("<>") == [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("*") == [*] :) }), + ({ "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(" 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([*])) == [*] :) }), + ({ "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**], + [*], + [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, diff --git a/test/t-operators.c b/test/t-operators.c index c8db0436..570d82b5 100644 --- a/test/t-operators.c +++ b/test/t-operators.c @@ -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()