diff --git a/libs/yscript/javascript.cpp b/libs/yscript/javascript.cpp index 15d38156..e55544d3 100644 --- a/libs/yscript/javascript.cpp +++ b/libs/yscript/javascript.cpp @@ -2570,7 +2570,7 @@ bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, GenObject* c bool ret = false; if (obj && (!obj->frozen() || !obj->hasField(stack,name,context)) && obj->toString() != YSTRING("()")) { - obj->params().clearParam(name); + obj->clearField(name); ret = true; } DDebug(DebugAll,"Deleted '%s' : %s",name.c_str(),String::boolText(ret)); diff --git a/libs/yscript/jsobjects.cpp b/libs/yscript/jsobjects.cpp index 719f4999..1470f3e0 100644 --- a/libs/yscript/jsobjects.cpp +++ b/libs/yscript/jsobjects.cpp @@ -271,7 +271,7 @@ JsObject* JsObject::buildCallContext(Mutex* mtx, JsObject* thisObj) void JsObject::fillFieldNames(ObjList& names) { - ScriptContext::fillFieldNames(names,params(),"__"); + ScriptContext::fillFieldNames(names,params(),false,"__"); const NamedList* native = nativeParams(); if (native) ScriptContext::fillFieldNames(names,*native); diff --git a/libs/yscript/script.cpp b/libs/yscript/script.cpp index dd48d23f..f7e1b575 100644 --- a/libs/yscript/script.cpp +++ b/libs/yscript/script.cpp @@ -184,7 +184,8 @@ bool ScriptContext::copyFields(ObjList& stack, const ScriptContext& original, Ge void ScriptContext::fillFieldNames(ObjList& names) { - fillFieldNames(names,params()); + bool checkDupl = !(YOBJECT(JsObject,this)); + fillFieldNames(names,params(),checkDupl); const NamedList* native = nativeParams(); if (native) fillFieldNames(names,*native); @@ -195,7 +196,7 @@ void ScriptContext::fillFieldNames(ObjList& names) #endif } -void ScriptContext::fillFieldNames(ObjList& names, const NamedList& list, const char* skip) +void ScriptContext::fillFieldNames(ObjList& names, const NamedList& list, bool checkDupl, const char* skip) { ObjList* tail = &names; for (const ObjList* l = list.paramList()->skipNull(); l; l = l->skipNext()) { @@ -204,12 +205,25 @@ void ScriptContext::fillFieldNames(ObjList& names, const NamedList& list, const continue; if (skip && s->name().startsWith(skip)) continue; - if (names.find(s->name())) + if (checkDupl && names.find(s->name())) continue; tail = tail->append(new String(s->name())); } } +void ScriptContext::fillFieldNames(ObjList& names, const HashList& list) +{ + ObjList* tail = &names; + for (unsigned int i = 0; i < list.length(); i++) { + ObjList* o = list.getList(i); + for (o = o ? o->skipNull() : 0;o;o = o->skipNext()) { + GenObject* obj = o->get(); + if (obj->toString().null()) + continue; + tail = tail->append(new String(obj->toString())); + } + } +} #define MAKE_NAME(x) { #x, ScriptRun::x } static const TokenDict s_states[] = { diff --git a/libs/yscript/yatescript.h b/libs/yscript/yatescript.h index e2b4073d..6069950a 100644 --- a/libs/yscript/yatescript.h +++ b/libs/yscript/yatescript.h @@ -1469,9 +1469,17 @@ public: * Fill a list with the unique names of all fields * @param names List to which key names must be added * @param list List of parameters whose names to be added + * @param checkDupl True to ignore duplicates from the given list * @param skip Parameters starting with this prefix will not be added */ - static void fillFieldNames(ObjList& names, const NamedList& list, const char* skip = 0); + static void fillFieldNames(ObjList& names, const NamedList& list, bool checkDupl = true, const char* skip = 0); + + /** + * Fill a list with the unique names from a Hash list + * @param names List to which key names must be added + * @param list Hash list whose names are to be added + */ + static void fillFieldNames(ObjList& names, const HashList& list); /** * Try to evaluate a single function in the context @@ -2050,6 +2058,13 @@ public: */ virtual ExpOperation* popValue(ObjList& stack, GenObject* context = 0); + /** + * Delete a field of the object + * @param name Name of field to remove + */ + virtual void clearField(const String& name) + { params().clearParam(name); } + /** * Retrieve the object frozen status (cannot modify attributes or methods) * @return True if the object is frozen diff --git a/modules/javascript.cpp b/modules/javascript.cpp index 80620347..851024ec 100644 --- a/modules/javascript.cpp +++ b/modules/javascript.cpp @@ -255,6 +255,37 @@ private: bool m_exit; }; +class JsHashList : public JsObject +{ +public: + inline JsHashList(Mutex* mtx) + : JsObject("HashList",mtx,true), + m_list() + { + XDebug(DebugAll,"JsHashList::JsHashList() [%p]",this); + } + inline JsHashList(unsigned int size, Mutex* mtx) + : JsObject(mtx,"[object HashList]",false), + m_list(size) + { + XDebug(DebugAll,"JsHashList::JsHashList(%u) [%p]",size,this); + } + virtual ~JsHashList() + { + XDebug(DebugAll,"~JsHashList() [%p]",this); + m_list.clear(); + } + virtual void* getObject(const String& name) const; + virtual JsObject* runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context); + virtual void fillFieldNames(ObjList& names); + virtual bool runField(ObjList& stack, const ExpOperation& oper, GenObject* context); + virtual bool runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context); + virtual void clearField(const String& name) + { m_list.remove(name); } +private: + HashList m_list; +}; + #define MKDEBUG(lvl) params().addParam(new ExpOperation((int64_t)Debug ## lvl,"Debug" # lvl)) #define MKTIME(typ) params().addParam(new ExpOperation((int64_t)SysUsage:: typ ## Time,# typ "Time")) class JsEngine : public JsObject, public DebugEnabler @@ -322,6 +353,7 @@ public: params().addParam(new ExpFunction("btoh")); params().addParam(new ExpFunction("htob")); addConstructor(params(), "Semaphore", new JsSemaphore(mtx)); + addConstructor(params(),"HashList",new JsHashList(mtx)); } static void initialize(ScriptContext* context, const char* name = 0); inline void resetWorker() @@ -3663,6 +3695,100 @@ void JsXML::initialize(ScriptContext* context) addConstructor(params,"XML",new JsXML(mtx)); } +void* JsHashList::getObject(const String& name) const +{ + void* obj = (name == YATOM("JsHashList")) ? const_cast(this) : JsObject::getObject(name); + if (!obj) + obj = m_list.getObject(name); + return obj; +} + +JsObject* JsHashList::runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context) +{ + XDebug(&__plugin,DebugAll,"JsHashList::runConstructor '%s'(" FMT64 ")",oper.name().c_str(),oper.number()); + ObjList args; + unsigned int cnt = 17; // default value for HashList + switch (extractArgs(stack,oper,context,args)) { + case 1: + { + ExpOperation* op = static_cast(args[0]); + if (!op || !op->isInteger() || op->toNumber() <= 0) + return 0; + cnt = op->toNumber(); + break; + } + case 0: + break; + default: + return 0; + } + + JsHashList* obj = new JsHashList(cnt,mutex()); + if (!ref()) { + TelEngine::destruct(obj); + return 0; + } + obj->params().addParam(new ExpWrapper(this,protoName())); + return obj; +} + +void JsHashList::fillFieldNames(ObjList& names) +{ + JsObject::fillFieldNames(names); + ScriptContext::fillFieldNames(names,m_list); +#ifdef XDEBUG + String tmp; + tmp.append(names,","); + Debug(DebugInfo,"JsHashList::fillFieldNames: %s",tmp.c_str()); +#endif +} + +bool JsHashList::runField(ObjList& stack, const ExpOperation& oper, GenObject* context) +{ + XDebug(DebugAll,"JsHashList::runField() '%s' in '%s' [%p]", + oper.name().c_str(),toString().c_str(),this); + ExpOperation* obj = static_cast(m_list[oper.name()]); + if (obj) { + ExpWrapper* wrp = YOBJECT(ExpWrapper,obj); + if (wrp) + ExpEvaluator::pushOne(stack,wrp->clone(oper.name())); + else + ExpEvaluator::pushOne(stack,new ExpOperation(*obj,oper.name())); + return true; + } + return JsObject::runField(stack,oper,context); +} + +bool JsHashList::runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context) +{ + XDebug(DebugAll,"JsHashList::runAssign() '%s'='%s' (%s) in '%s' [%p]", + oper.name().c_str(),oper.c_str(),oper.typeOf(),toString().c_str(),this); + if (frozen()) { + Debug(DebugWarn,"Object '%s' is frozen",toString().c_str()); + return false; + } + ObjList* obj = m_list.find(oper.name()); + ExpOperation* cln = 0; + ExpFunction* ef = YOBJECT(ExpFunction,&oper); + if (ef) + cln = ef->ExpOperation::clone(); + else { + ExpWrapper* w = YOBJECT(ExpWrapper,&oper); + if (w) { + JsFunction* jsf = YOBJECT(JsFunction,w->object()); + if (jsf) + jsf->firstName(oper.name()); + cln = w->clone(oper.name()); + } + else + cln = oper.clone(); + } + if (!obj) + m_list.append(cln); + else + obj->set(cln); + return true; +} bool JsJSON::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context) {