AddCmd(): keine exportierbare Closure auf protected lfuns
AddCmd() erzeugt eine Closure fuer String-Methoden, wenn moeglich.
Bislang dann, wenn AddCmd entweder intern gerufen wurde oder
function_exists() die lfun findet. Dabei wurden aber auch protected
lfuns gefunden, weswegen man mit AddCmd beliebige dieser protected
lfuns rufen konnte.
Daher werden:
* die erzeugten Closures nur nocht intern gecached
* bei externem Auruf von AddCmd protected/static Methoden abgelehnt.
QueryProp/SetProp-Methoden sind entsprechend ueberarbeitet.
Change-Id: Icc34f9e648a392177cf487943b7df493194fc0d6
diff --git a/doc/props/P_COMMANDS b/doc/props/P_COMMANDS
index 527d1a1..d0ff99b 100644
--- a/doc/props/P_COMMANDS
+++ b/doc/props/P_COMMANDS
@@ -18,30 +18,41 @@
({flag1,...});
({regel1,...});
({id1, ...}),
+ ({closure auf fun1, ...}),
... ])
Die Eintraege entsprechen den Parametern des AddCmd()-Aufrufs, sind
aber in anderer Form. Als Beispiel:
AddCmd(verb,fun1,1);
- AddCmd(verb+syn1a|syn1b&syn2a|syn2b|syn2c,fun2,
- error1_notify|error2_notify^error2_write);
+ AddCmd(verb+syn1a|syn1b&syn2a|syn2b|syn2c, fun2,
+ error1_notify|error2_notify^error2_write);
-->
- ([verb:({fun1,fun2}); // funs
- ({1,({error1_notify, error2_write^error2_say, 1})}); // flags
- ({0,({({syn1a,syn1b}),({syn2a,syn2b,syn2c})})}); // rules
- 0]) // IDs
+ ([verb:
+ ({fun1,fun2}); // funs
+ ({1,({error1_notify, error2_write^error2_say, 1})}); // flags
+ ({0,({({syn1a,syn1b}),({syn2a,syn2b,syn2c})})}); // rules
+ 0; // IDs
+ ({closure auf fun1, closure auf fun2}) ]) // Cache
- Aufgeschluesselt sehen die einzelnen Arrays folgendermassen aus:
-
+ Funs: ({<fun1> ,...
+ 'fun' kann sein: Closure
+ String: Methodenname - wird etwas geprueft
+ Objekt: wenn keine Methode, this_object() fuer
+ intern, previous_object() fuer extern
+ 0 (erloschenes externes Objekt)
Rules: ({<Regelsatz fuer fun1>, ({<1. Synonymgruppe>,
- <2. Synonymgruppe, ...}), ...})
+ <2. Synonymgruppe, ...}), ...})
Flags: ({<Flag fuer fun1>, ({<Fehlermeldung 1. Synonymgruppe>, ... ,
- [, Index fuer write anstatt notify_fail]}),
- ... })
+ [, Index fuer write anstatt notify_fail]}),
+ ... })
IDs: 0 oder ({<ID fuer fun1>}) oder ({0, <ID fuer fun2>}) ...
+ Cache: ({<closure fuer fun1>, ...
+
+BEMERKUNGEN:
+ Cache-Closures sind bei Export immer genullt
SIEHE AUCH:
/std/thing/commands.c, AddCmd(), RemoveCmd()
-08.Dez.2003 Gloinson
+16. Dez 2016 Gloinson
\ No newline at end of file
diff --git a/std/thing/commands.c b/std/thing/commands.c
index 8134f4b..a1aa079 100644
--- a/std/thing/commands.c
+++ b/std/thing/commands.c
@@ -11,22 +11,29 @@
//
// AddCmd(verb,fun1,1);
// AddCmd(verb+syn1a|syn1b&syn2a|syn2b|syn2c,fun2,
-// error1_notify|error2_notify^error2_write);
+// error1_notify|error2_notify^error2_write);
// -->
-// ([verb:({fun1,fun2}); // funs
-// ({1,({error1_notify, error2_write^error2_say, 1})}); // flags
-// ({0,({({syn1a,syn1b}),({syn2a,syn2b,syn2c})})}); // rules
-// 0]) // IDs
+// ([verb:
+// ({fun1,fun2}); // funs
+// ({1,({error1_notify, error2_write^error2_say, 1})}); // flags
+// ({0,({({syn1a,syn1b}),({syn2a,syn2b,syn2c})})}); // rules
+// 0; // IDs
+// ({closure auf fun1, closure auf fun2}) ]) // Cache
//
+// Funs: ({<fun1> ,...
+// 'fun' kann sein: Closure
+// String: Methodenname - wird etwas geprueft
+// Objekt: wenn keine Methode, this_object() fuer
+// intern, previous_object() fuer extern
+// 0 (erloschenes externes Objekt)
// Rules: ({<Regelsatz fuer fun1>, ({<1. Synonymgruppe>,
-// <2. Synonymgruppe, ...}), ...})
+// <2. Synonymgruppe, ...}), ...})
// Flags: ({<Flag fuer fun1>, ({<Fehlermeldung 1. Synonymgruppe>, ... ,
-// [, Index fuer write anstatt notify_fail]}),
-// ... })
+// [, Index fuer write anstatt notify_fail]}),
+// ... })
// IDs: 0 oder ({<ID fuer fun1>}) oder ({0, <ID fuer fun2>}) ...
-//
-// IDEA: save no 0s in rules/flags if possible (as in IDs)
-// (adressing especially old-style-AddCmd-MUDs)
+// Cache: ({<closure fuer fun1>, ...
+// Cache-Closures sind bei Export (QueryProp) immer genullt
#pragma strict_types
#pragma save_types
#pragma range_check
@@ -38,6 +45,7 @@
#include <exploration.h>
#include <defines.h>
#include <living/comm.h>
+#include <functionlist.h>
#define NEED_PROTOTYPES
#include <thing/properties.h>
@@ -45,6 +53,14 @@
#include <thing/commands.h>
#undef NEED_PROTOTYPES
+#define CMDS_WIDTH 5
+// Mapping-Indizes
+#define CMDIDX_FUN 0
+#define CMDIDX_FLAG 1
+#define CMDIDX_RULE 2
+#define CMDIDX_ID 3
+#define CMDIDX_CACHE 4
+
#ifdef DBG
#undef DBG
#endif
@@ -129,9 +145,39 @@
set_next_reset(-1);
}
+private closure _check_stringmethod(mixed func, int ex, string resp) {
+ closure cl;
+ if(!function_exists(func))
+ raise_error(sprintf(
+ resp+"string-Methode '%s' fehlt oder ist private.\n", func));
+
+ // extern erstellte auf Sichtbarkeit pruefen und abbrechen/davor warnen
+ if(ex) {
+ mixed *fl = functionlist(this_object(), RETURN_FUNCTION_NAME|
+ RETURN_FUNCTION_FLAGS);
+ int index = member(fl, func)+1;
+ if(index<=0 || sizeof(fl)<=index)
+ raise_error(sprintf(
+ resp+"string-Methode '%s' nicht in functionlist.\n", func));
+ else if(fl[index]&TYPE_MOD_PROTECTED || fl[index]&TYPE_MOD_STATIC)
+ raise_error(sprintf(
+ resp+"string-Methode '%s' ist protected/static. Extern "
+ "definiertes Kommando darf so eine Methode nicht aufrufen!\n",
+ func));
+ }
+
+ // ansonsten Methode erstmal cachen
+ cl = symbol_function(func, this_object());
+ if(!cl)
+ raise_error(sprintf(
+ resp+"string-Methode '%s' nicht in Closure wandelbar?\n", func));
+
+ return cl;
+}
+
varargs void AddCmd(mixed cmd, mixed func, mixed flag, mixed cmdid) {
int i,j;
- closure cl;
+ closure cachedcl;
mixed *rule;
// potentielle AddCmd mit Regel?
@@ -196,37 +242,41 @@
if(!pointerp(cmd))
raise_error("AddCmd: missing string/pointer-parameter for command.\n");
- // Closure aus einem String erzeugen, wenn moeglich und sicher
- // (function_exists() filtert unnoetigerweise auch reine "static" funs,
- // die genaue Pruefung ueber functionlist() kostet jedoch zuviel)
- if(stringp(func) &&
- (!extern_call() || function_exists(func,this_object())) &&
- closurep(cl=symbol_function(func,this_object())))
- func=cl;
+ // String-Methode auf Sichtbarkeit pruefen, Cache gleich anpassen
+ switch(typeof(func)) {
+ case T_STRING:
+ cachedcl=_check_stringmethod(func, extern_call(), "AddCmd: ");
+ break;
+ case T_CLOSURE:
+ cachedcl=func;
+ break;
+ default:
+ if(extern_call()) func=previous_object();
+ else func=this_object();
+ break;
+ }
- // jedes einzelne Verb mit seinen Regeln und Funktionen eintragen
+ // jedes einzelne Verb mit seinen Regeln und Funktionen eintragen
i=sizeof(cmd);
- if(!added_cmds) added_cmds=m_allocate(i,4);
+ if(!added_cmds) added_cmds=m_allocate(i, CMDS_WIDTH);
while(i--) {
string str;
str=cmd[i];
- if(!func)
- if(extern_call()) func=previous_object();
- else func=this_object();
if(!member(added_cmds,str))
- added_cmds+=([str:allocate(0);allocate(0);allocate(0);0]);
+ added_cmds+=([str:allocate(0);allocate(0);allocate(0);0;allocate(0)]);
// existierendes Verb ergaenzen
- added_cmds[str,0]+=({func});
- added_cmds[str,1]+=({flag});
- added_cmds[str,2]+=({rule});
+ added_cmds[str, CMDIDX_FUN]+=({func});
+ added_cmds[str, CMDIDX_FLAG]+=({flag});
+ added_cmds[str, CMDIDX_RULE]+=({rule});
+ added_cmds[str, CMDIDX_CACHE]+=({cachedcl});
// ggf. id in das ID-Mapping eintragen
if(cmdid) {
mixed *tmp;
- j=sizeof((string*)added_cmds[str,0]);
- tmp=added_cmds[str,3]||allocate(j);
+ j=sizeof((string*)added_cmds[str, CMDIDX_FUN]);
+ tmp=added_cmds[str, CMDIDX_ID]||allocate(j);
if(sizeof(tmp)<j) tmp+=allocate(j-sizeof(tmp));
tmp[<1]=cmdid;
- added_cmds[str,3]=tmp;
+ added_cmds[str, CMDIDX_ID]=tmp;
}
}
}
@@ -254,7 +304,7 @@
added_cmds=(mapping)0;
else {
int i, j;
- mixed *rule, *flag, *fun, *delrule, *ids;
+ mixed *rule, *flag, *fun, *delrule, *ids, *cachecl;
if(stringp(cmd)) {
// Regeln entdeckt - Zerlegen (wie AddCmd)
@@ -275,7 +325,7 @@
while(i--) {
// keine Regeln da und Regeln loeschen erlaubt: alles loeschen
if(!delrule && !del_norule && !onlyid) m_delete(added_cmds,cmd[i]);
- else if(m_contains(&fun, &flag, &rule, &ids, added_cmds, cmd[i])) {
+ else if(m_contains(&fun, &flag, &rule, &ids, &cachecl, added_cmds, cmd[i])) {
j=sizeof(fun);
while(j--) {
int k;
@@ -294,9 +344,10 @@
}
// alles korrekt: Löschen!
// (Arraybereich durch leeres Array loeschen)
- flag[j..j] = allocate(0);
- fun[j..j] = allocate(0);
- rule[j..j] = allocate(0);
+ flag[j..j] = allocate(0);
+ fun[j..j] = allocate(0);
+ rule[j..j] = allocate(0);
+ cachecl[j..j] = allocate(0);
if(ids) {
ids[j..j] = allocate(0);
if(!sizeof(ids-allocate(1))) ids=(mixed*)0;
@@ -307,10 +358,11 @@
}
// Funktions/Regelliste update oder ggf. Kommando voellig loeschen
if(sizeof(rule)) {
- added_cmds[cmd[i],0]=fun;
- added_cmds[cmd[i],1]=flag;
- added_cmds[cmd[i],2]=rule;
- added_cmds[cmd[i],3]=ids;
+ added_cmds[cmd[i], CMDIDX_FUN]=fun;
+ added_cmds[cmd[i], CMDIDX_FLAG]=flag;
+ added_cmds[cmd[i], CMDIDX_RULE]=rule;
+ added_cmds[cmd[i], CMDIDX_ID]=ids;
+ added_cmds[cmd[i], CMDIDX_CACHE]=cachecl;
} else m_delete(added_cmds,cmd[i]);
}
if(!sizeof(added_cmds)) added_cmds=(mapping)0;
@@ -320,11 +372,20 @@
// Ausfuehren samt geparstem Inputstring und getriggerten Parserergebnissen
static int _execute(mixed fun, string str, mixed *parsed) {
- if(closurep(fun))
- return ((int)funcall(fun,str,&parsed));
- if(stringp(fun))
- return ((int)call_other(this_object(),fun,str,&parsed));
- return 0;
+ switch(typeof(fun)) {
+ case T_CLOSURE:
+ return ((int)funcall(fun,str,&parsed));
+ case T_STRING:
+ int ret;
+ if(!call_resolved(&ret, this_object(), fun, str, &parsed))
+ raise_error(sprintf(
+ "AddCmd: String-Methode '%s' in Kommando %#O scheint zu fehlen "
+ "oder nicht zugreifbar zu sein.\n", fun, parsed));
+ return ret;
+ default:
+ break;
+ }
+ return 0;
}
#define CHECK_PRESENT 1
@@ -339,8 +400,8 @@
// Regeln fuer ein (nun unwichtiges) Verb triggern
static int _process_command(string str, string *noparsestr,
- mixed fun, mixed flag, mixed rule, mixed id)
-{
+ mixed fun, mixed flag, mixed rule,
+ mixed id, mixed cachedcl) {
mixed *parsed, *objmatches;
// eine Regel ... auswerten ...
@@ -505,7 +566,7 @@
parsed[objmatches[objsize]]=objmatches[objsize+1];
// erfolgreich Methode gefunden/eine Regel bis zum Ende durchgeparst:
- return(_execute(&fun,&str,&parsed));
+ return(_execute(cachedcl||fun, str, &parsed));
}
// Auswertung - add_action-Fun
@@ -515,10 +576,10 @@
mixed *flag;
// Verb existiert, Kommandos auch, Eintrag fuer Verb gefunden
if(mappingp(added_cmds) && query_verb()) {
- mixed *fun, *rule, *ids;
+ mixed *fun, *rule, *ids, *cachecl;
// ist das Verb ein Key im Kommandomapping?
- if(m_contains(&fun, &flag, &rule, &ids, added_cmds, query_verb())) {
+ if(m_contains(&fun, &flag, &rule, &ids, &cachecl, added_cmds, query_verb())) {
string *noparsestr;
nindex=sizeof(fun);
noparsestr=explode((str||"")," ")-({""});
@@ -528,8 +589,7 @@
{
mixed cmd_id = (pointerp(ids) && sizeof(ids)>nindex) ? ids[nindex] : 0;
if(_process_command(&str, noparsestr, fun[nindex], flag[nindex],
- rule[nindex], cmd_id))
- {
+ rule[nindex], cmd_id, cachecl[nindex])) {
GiveEP(EP_CMD, query_verb());
return 1;
}
@@ -542,20 +602,21 @@
nindex=sizeof(keys);
while(nindex--)
if(!strstr(query_verb(),keys[nindex]) &&
- member((flag=added_cmds[keys[nindex],1]),1)>=0) {
+ member((flag=added_cmds[keys[nindex], CMDIDX_FLAG]),1)>=0) {
int i,ret;
i=sizeof(flag);
// Reihenfolge nicht aendern !
while(i--)
if(flag[i]==1) {
- mixed *exec = added_cmds[keys[nindex],0];
- if(!pointerp(exec) || sizeof(exec)<=i)
+ mixed *fuzzyfuns = added_cmds[keys[nindex], CMDIDX_FUN];
+ mixed *fuzzycache = added_cmds[keys[nindex], CMDIDX_CACHE];
+ if(!pointerp(fuzzyfuns) || sizeof(fuzzyfuns)<=i)
catch(raise_error(
"AddCmd-Auswertung: CatchAll-Kommando \""+keys[nindex]+"\""
" wurde waehrend der Ausfuehrung veraendert oder geloescht. "
"Klartext: Das ist schlecht. Sucht ein RemoveCmd? ");
publish);
- else if(_execute(exec[i],str,0)) {
+ else if(_execute(fuzzycache[i]||fuzzyfuns[i], str, 0)) {
GiveEP(EP_CMD, query_verb());
return 1;
}
@@ -570,39 +631,51 @@
}
-static void _check_copy_commands(string ind, mixed *fun,
- mixed *flag, mixed *rule) {
- if(pointerp(fun)) added_cmds[ind,0]=fun+allocate(0);
- else added_cmds[ind,0]=({fun});
- if(pointerp(flag)) added_cmds[ind,1]=flag+allocate(0);
- else if(flag) added_cmds[ind,1]=({flag});
- else added_cmds[ind,1]=allocate(sizeof(added_cmds[ind]));
- if(pointerp(rule)) added_cmds[ind,2]=rule+allocate(0);
- else added_cmds[ind,2]=allocate(sizeof(added_cmds[ind]));
-
- if(sizeof(added_cmds[ind])!=sizeof(added_cmds[ind,1]) ||
- sizeof(added_cmds[ind])!=sizeof(added_cmds[ind,2])) {
- added_cmds=(mapping)0;
- raise_error("SetProp(P_COMMANDS): corrupt commands-mapping.\n");
- }
+private void _check_importentry(string ind, mixed *fun, mixed *flag,
+ mixed *rule, int|mixed id, mixed *cachedcl) {
+ // Check der Stringmethoden
+ cachedcl = map(fun, function int|closure(string clfun) {
+ closure ret;
+ catch(ret=_check_stringmethod(
+ clfun, 1,
+ "Warnung SetProp(P_COMMANDS): ");
+ publish);
+ return ret;
+ });
+ if(sizeof(fun)!=sizeof(flag) || sizeof(fun)!=sizeof(rule))
+ raise_error("SetProp(P_COMMANDS): corrupt commands-mapping.\n");
}
-static mapping _set_commands(mapping commands) {
- if(!commands) added_cmds=(mapping)0;
- else if(mappingp(commands)) {
- added_cmds=m_allocate(sizeof(commands),4);
- walk_mapping(commands,#'_check_copy_commands);
- }
- if (mappingp(added_cmds))
- return(deep_copy(added_cmds));
- else
- return (mapping)0;
+private void _cleanup_exportcl(string ind, mixed *fun, mixed *flag,
+ mixed *rule, int|mixed id,
+ mixed *cachedcl, mixed *fl) {
+ cachedcl = allocate(sizeof(fun));
+ foreach(string func: filter(fun, #'stringp))
+ catch(_check_stringmethod(func, 1, "Warnung QueryProp(P_COMMANDS): ");
+ publish);
}
static mapping _query_commands() {
- if (mappingp(added_cmds))
- return(deep_copy(added_cmds));
- else
- return (mapping)0;
+ mapping ret;
+ if(mappingp(added_cmds)) {
+ ret = deep_copy(added_cmds);
+ // Hier kann man nicht mehr herausfinden, ob es ein extern_call() ist:
+ // Closures im Cache werden immer geloescht.
+ walk_mapping(ret, #'_cleanup_exportcl,
+ functionlist(this_object(), RETURN_FUNCTION_NAME|
+ RETURN_FUNCTION_FLAGS));
+ }
+ return ret;
}
+static mapping _set_commands(mapping commands) {
+ if(!commands) added_cmds=(mapping)0;
+ else if(mappingp(commands)) {
+ if(widthof(commands) != CMDS_WIDTH)
+ raise_error("SetProp(P_COMMANDS): corrupt commands-mapping.\n");
+ mapping tmp = deep_copy(commands);
+ walk_mapping(tmp, #'_check_importentry);
+ commands = tmp;
+ }
+ return _query_commands();
+}
diff --git a/sys/thing/commands.h b/sys/thing/commands.h
index 8f48587..76e44e7 100644
--- a/sys/thing/commands.h
+++ b/sys/thing/commands.h
@@ -33,8 +33,8 @@
// internal
static int _execute(mixed fun, string str, mixed *parsed);
-static int _process_command(string str, string *noparsestr,mixed fun,
- mixed flag, mixed rule, mixed id);
+static int _process_command(string str, string *noparsestr, mixed fun,
+ mixed flag, mixed rule, mixed id, mixed cachedcl);
public int _cl(string str);
#endif // __THING_COMMANDS_H_PROTO__