2012-02-10 14:53:55 +00:00
|
|
|
/**
|
|
|
|
* jsobject.cpp
|
|
|
|
* Yet Another (Java)script library
|
|
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
|
|
*
|
|
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
2014-02-05 11:42:17 +00:00
|
|
|
* Copyright (C) 2011-2014 Null Team
|
2012-02-10 14:53:55 +00:00
|
|
|
*
|
2013-08-06 13:38:10 +00:00
|
|
|
* This software is distributed under multiple licenses;
|
|
|
|
* see the COPYING file in the main directory for licensing
|
|
|
|
* information for this specific distribution.
|
|
|
|
*
|
|
|
|
* This use of this software may be subject to additional restrictions.
|
|
|
|
* See the LEGAL file in the main directory for details.
|
2012-02-10 14:53:55 +00:00
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2013-08-06 13:38:10 +00:00
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
2012-02-10 14:53:55 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "yatescript.h"
|
2013-02-12 10:16:12 +00:00
|
|
|
#include <string.h>
|
2012-02-10 14:53:55 +00:00
|
|
|
|
|
|
|
using namespace TelEngine;
|
|
|
|
|
|
|
|
namespace { // anonymous
|
|
|
|
|
2012-02-17 16:19:17 +00:00
|
|
|
// Object object
|
|
|
|
class JsObjectObj : public JsObject
|
2012-02-10 14:53:55 +00:00
|
|
|
{
|
2012-02-17 16:19:17 +00:00
|
|
|
YCLASS(JsObjectObj,JsObject)
|
2012-02-10 14:53:55 +00:00
|
|
|
public:
|
2012-02-17 16:19:17 +00:00
|
|
|
inline JsObjectObj(Mutex* mtx)
|
|
|
|
: JsObject("Object",mtx,true)
|
|
|
|
{
|
|
|
|
}
|
2015-09-16 17:33:28 +00:00
|
|
|
virtual void initConstructor(JsFunction* construct)
|
|
|
|
{
|
|
|
|
construct->params().addParam(new ExpFunction("keys"));
|
|
|
|
}
|
2012-02-17 16:19:17 +00:00
|
|
|
protected:
|
|
|
|
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
|
2012-02-10 14:53:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Date object
|
|
|
|
class JsDate : public JsObject
|
|
|
|
{
|
|
|
|
YCLASS(JsDate,JsObject)
|
|
|
|
public:
|
2012-02-17 16:19:17 +00:00
|
|
|
inline JsDate(Mutex* mtx)
|
2013-04-17 15:42:40 +00:00
|
|
|
: JsObject("Date",mtx,true),
|
|
|
|
m_time(0), m_msec(0), m_offs(0)
|
2012-02-10 14:53:55 +00:00
|
|
|
{
|
2012-02-17 16:19:17 +00:00
|
|
|
params().addParam(new ExpFunction("getDate"));
|
|
|
|
params().addParam(new ExpFunction("getDay"));
|
|
|
|
params().addParam(new ExpFunction("getFullYear"));
|
|
|
|
params().addParam(new ExpFunction("getHours"));
|
|
|
|
params().addParam(new ExpFunction("getMilliseconds"));
|
|
|
|
params().addParam(new ExpFunction("getMinutes"));
|
|
|
|
params().addParam(new ExpFunction("getMonth"));
|
|
|
|
params().addParam(new ExpFunction("getSeconds"));
|
|
|
|
params().addParam(new ExpFunction("getTime"));
|
2013-04-17 15:42:40 +00:00
|
|
|
params().addParam(new ExpFunction("getTimezoneOffset"));
|
2012-05-11 16:15:20 +00:00
|
|
|
|
|
|
|
params().addParam(new ExpFunction("getUTCDate"));
|
|
|
|
params().addParam(new ExpFunction("getUTCDay"));
|
|
|
|
params().addParam(new ExpFunction("getUTCFullYear"));
|
|
|
|
params().addParam(new ExpFunction("getUTCHours"));
|
|
|
|
params().addParam(new ExpFunction("getUTCMilliseconds"));
|
|
|
|
params().addParam(new ExpFunction("getUTCMinutes"));
|
|
|
|
params().addParam(new ExpFunction("getUTCMonth"));
|
|
|
|
params().addParam(new ExpFunction("getUTCSeconds"));
|
2019-11-08 11:24:35 +00:00
|
|
|
|
|
|
|
params().addParam(new ExpFunction("toJSON"));
|
2012-02-10 14:53:55 +00:00
|
|
|
}
|
2012-11-20 17:14:51 +00:00
|
|
|
virtual void initConstructor(JsFunction* construct)
|
|
|
|
{
|
|
|
|
construct->params().addParam(new ExpFunction("now"));
|
2017-06-07 14:17:15 +00:00
|
|
|
construct->params().addParam(new ExpFunction("UTC"));
|
2012-11-20 17:14:51 +00:00
|
|
|
}
|
2013-04-17 15:42:40 +00:00
|
|
|
virtual JsObject* runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context);
|
2019-11-08 11:24:35 +00:00
|
|
|
virtual const String& toString() const {
|
|
|
|
if (!m_str)
|
|
|
|
Time::appendTo(m_str,(uint64_t)m_time * 1000000 + (uint64_t)m_msec * 1000,1);
|
|
|
|
return m_str;
|
|
|
|
}
|
|
|
|
|
2012-02-17 16:19:17 +00:00
|
|
|
protected:
|
2013-04-17 15:42:40 +00:00
|
|
|
inline JsDate(Mutex* mtx, u_int64_t msecs, bool local = false)
|
|
|
|
: JsObject("Date",mtx),
|
2020-04-01 07:38:14 +00:00
|
|
|
m_time((unsigned int)(msecs / 1000)), m_msec((unsigned int)(msecs % 1000)), m_offs(Time::timeZone(m_time))
|
2013-04-17 15:42:40 +00:00
|
|
|
{ if (local) m_time -= m_offs; }
|
|
|
|
inline JsDate(Mutex* mtx, const char* name, unsigned int time, unsigned int msec, unsigned int offs)
|
|
|
|
: JsObject(mtx,name),
|
|
|
|
m_time(time), m_msec(msec), m_offs(offs)
|
2012-05-14 19:51:36 +00:00
|
|
|
{ }
|
|
|
|
virtual JsObject* clone(const char* name) const
|
2013-04-17 15:42:40 +00:00
|
|
|
{ return new JsDate(mutex(),name,m_time,m_msec,m_offs); }
|
2012-02-17 16:19:17 +00:00
|
|
|
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
|
2013-04-17 15:42:40 +00:00
|
|
|
private:
|
|
|
|
unsigned int m_time;
|
|
|
|
unsigned int m_msec;
|
|
|
|
int m_offs;
|
2019-11-08 11:24:35 +00:00
|
|
|
mutable String m_str;
|
2012-02-10 14:53:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Math class - not really an object, all methods are static
|
|
|
|
class JsMath : public JsObject
|
|
|
|
{
|
|
|
|
YCLASS(JsMath,JsObject)
|
|
|
|
public:
|
2012-02-17 16:19:17 +00:00
|
|
|
inline JsMath(Mutex* mtx)
|
|
|
|
: JsObject("Math",mtx,true)
|
2012-02-10 14:53:55 +00:00
|
|
|
{
|
2012-02-17 16:19:17 +00:00
|
|
|
params().addParam(new ExpFunction("abs"));
|
|
|
|
params().addParam(new ExpFunction("max"));
|
|
|
|
params().addParam(new ExpFunction("min"));
|
2013-02-12 10:16:12 +00:00
|
|
|
params().addParam(new ExpFunction("random"));
|
2012-02-10 14:53:55 +00:00
|
|
|
}
|
2012-02-17 16:19:17 +00:00
|
|
|
protected:
|
|
|
|
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
|
2012-02-10 14:53:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
}; // anonymous namespace
|
|
|
|
|
|
|
|
|
2012-05-30 08:32:47 +00:00
|
|
|
// Helper function that does the actual object printing
|
2020-10-14 10:38:31 +00:00
|
|
|
static void dumpRecursiveObj(const GenObject* obj, String& buf, unsigned int depth, ObjList& seen,
|
|
|
|
unsigned int flags)
|
2012-05-30 08:32:47 +00:00
|
|
|
{
|
|
|
|
if (!obj)
|
|
|
|
return;
|
2020-10-14 10:38:31 +00:00
|
|
|
if (depth > 1 && !(flags & JsObject::DumpRecursive))
|
|
|
|
return;
|
|
|
|
// Check if we have something to dump
|
|
|
|
unsigned int dump = flags & (JsObject::DumpFunc | JsObject::DumpProp);
|
|
|
|
if (!dump)
|
|
|
|
return;
|
2012-05-30 08:32:47 +00:00
|
|
|
String str(' ',2 * depth);
|
|
|
|
if (seen.find(obj)) {
|
|
|
|
str << "(recursivity encountered)";
|
|
|
|
buf.append(str,"\r\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const NamedString* nstr = YOBJECT(NamedString,obj);
|
2020-10-14 10:38:31 +00:00
|
|
|
// Check for prototype dump (always dump the first level if original object is a prototype)
|
|
|
|
bool isProto = nstr && nstr->name() == JsObject::protoName();
|
|
|
|
if (depth && isProto && !(flags & JsObject::DumpProto))
|
|
|
|
return;
|
2012-05-30 08:32:47 +00:00
|
|
|
const NamedPointer* nptr = YOBJECT(NamedPointer,nstr);
|
|
|
|
const char* type = nstr ? (nptr ? "NamedPointer" : "NamedString") : "???";
|
2014-06-25 16:04:00 +00:00
|
|
|
const char* subType = 0;
|
2012-05-30 08:32:47 +00:00
|
|
|
const ScriptContext* scr = YOBJECT(ScriptContext,obj);
|
|
|
|
const ExpWrapper* wrap = 0;
|
2012-05-30 17:48:13 +00:00
|
|
|
bool objRecursed = false;
|
2020-10-14 10:38:31 +00:00
|
|
|
bool isFunc = false;
|
2012-05-30 08:32:47 +00:00
|
|
|
if (scr) {
|
2012-05-30 17:48:13 +00:00
|
|
|
const JsObject* jso = YOBJECT(JsObject,scr);
|
|
|
|
if (jso) {
|
|
|
|
objRecursed = (seen.find(jso) != 0);
|
|
|
|
if ((jso != obj) && !objRecursed)
|
|
|
|
seen.append(jso)->setDelete(false);
|
2012-05-30 08:32:47 +00:00
|
|
|
if (YOBJECT(JsArray,scr))
|
|
|
|
type = "JsArray";
|
2020-10-14 10:38:31 +00:00
|
|
|
else if (YOBJECT(JsFunction,scr)) {
|
2012-05-30 08:32:47 +00:00
|
|
|
type = "JsFunction";
|
2020-10-14 10:38:31 +00:00
|
|
|
isFunc = true;
|
|
|
|
}
|
2012-05-30 17:48:13 +00:00
|
|
|
else if (YOBJECT(JsRegExp,scr))
|
|
|
|
type = "JsRegExp";
|
2019-11-08 11:24:35 +00:00
|
|
|
else if (YOBJECT(JsDate,scr))
|
|
|
|
type = "JsDate";
|
2012-05-30 08:32:47 +00:00
|
|
|
else
|
|
|
|
type = "JsObject";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
type = "ScriptContext";
|
|
|
|
}
|
2012-05-30 17:48:13 +00:00
|
|
|
seen.append(obj)->setDelete(false);
|
2012-05-30 08:32:47 +00:00
|
|
|
const ExpOperation* exp = YOBJECT(ExpOperation,nstr);
|
2012-05-30 17:48:13 +00:00
|
|
|
if (exp && !scr) {
|
2012-05-30 08:32:47 +00:00
|
|
|
if ((wrap = YOBJECT(ExpWrapper,exp)))
|
|
|
|
type = wrap->object() ? "ExpWrapper" : "Undefined";
|
2020-10-14 10:38:31 +00:00
|
|
|
else if (YOBJECT(ExpFunction,exp)) {
|
2012-05-30 08:32:47 +00:00
|
|
|
type = "ExpFunction";
|
2020-10-14 10:38:31 +00:00
|
|
|
isFunc = true;
|
|
|
|
}
|
2014-06-25 16:04:00 +00:00
|
|
|
else {
|
2012-05-30 08:32:47 +00:00
|
|
|
type = "ExpOperation";
|
2014-06-25 16:04:00 +00:00
|
|
|
subType = exp->typeOf();
|
|
|
|
}
|
2012-05-30 08:32:47 +00:00
|
|
|
}
|
2020-10-14 10:38:31 +00:00
|
|
|
// Check for func/prop dump (don't do it if we are printing a prototype)
|
|
|
|
if (depth && !isProto && ((isFunc && (0 == (flags & JsObject::DumpFunc))) ||
|
|
|
|
(!isFunc && (0 == (flags & JsObject::DumpProp)))))
|
|
|
|
return;
|
|
|
|
bool dumpType = flags & JsObject::DumpType;
|
|
|
|
if (nstr) {
|
|
|
|
str << "'" << nstr->name() << "'";
|
|
|
|
// Nicely dump property value if dumping props only and type is not shown
|
|
|
|
if ((dump == JsObject::DumpProp) && !isProto && !dumpType) {
|
|
|
|
if (scr) {
|
|
|
|
if (exp && JsParser::isNull(*exp))
|
|
|
|
str << " = null";
|
|
|
|
else if (YOBJECT(JsRegExp,scr))
|
|
|
|
str << " = /" << *nstr << "/";
|
|
|
|
else if (flags & JsObject::DumpPropObjType) {
|
|
|
|
if (YOBJECT(JsObject,scr))
|
|
|
|
str << " = " << *nstr;
|
|
|
|
else
|
|
|
|
str << " = [ScriptContext]";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (exp) {
|
|
|
|
if (JsParser::isUndefined(*exp))
|
|
|
|
str << " = undefined";
|
|
|
|
else if (exp->isInteger()) {
|
|
|
|
if (exp->isBoolean())
|
|
|
|
str << " = " << exp->valBoolean();
|
|
|
|
else
|
|
|
|
str << " = " << exp->number();
|
|
|
|
}
|
|
|
|
else if (exp->isNumber()) // NaN
|
|
|
|
str << " = " << *nstr;
|
|
|
|
else // string
|
|
|
|
str << " = '" << *nstr << "'";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
str << " = '" << *nstr << "'";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
str << " = '" << *nstr << "'";
|
|
|
|
}
|
2012-05-30 08:32:47 +00:00
|
|
|
else
|
|
|
|
str << "'" << obj->toString() << "'";
|
2020-10-14 10:38:31 +00:00
|
|
|
if (dumpType)
|
|
|
|
str << " (" << type << (subType ? ", " : "") << subType << ")";
|
2012-05-30 17:48:13 +00:00
|
|
|
if (objRecursed)
|
|
|
|
str << " (already seen)";
|
2012-05-30 08:32:47 +00:00
|
|
|
buf.append(str,"\r\n");
|
2012-05-30 17:48:13 +00:00
|
|
|
if (objRecursed)
|
|
|
|
return;
|
2012-05-30 08:32:47 +00:00
|
|
|
if (scr) {
|
|
|
|
NamedIterator iter(scr->params());
|
|
|
|
while (const NamedString* p = iter.get())
|
2020-10-14 10:38:31 +00:00
|
|
|
dumpRecursiveObj(p,buf,depth + 1,seen,flags);
|
2012-06-19 18:55:45 +00:00
|
|
|
if (scr->nativeParams()) {
|
|
|
|
iter = *scr->nativeParams();
|
|
|
|
while (const NamedString* p = iter.get())
|
2020-10-14 10:38:31 +00:00
|
|
|
dumpRecursiveObj(p,buf,depth + 1,seen,flags);
|
2012-06-19 18:55:45 +00:00
|
|
|
}
|
2012-05-30 08:32:47 +00:00
|
|
|
}
|
|
|
|
else if (wrap)
|
2020-10-14 10:38:31 +00:00
|
|
|
dumpRecursiveObj(wrap->object(),buf,depth + 1,seen,flags);
|
2012-05-30 08:32:47 +00:00
|
|
|
else if (nptr)
|
2020-10-14 10:38:31 +00:00
|
|
|
dumpRecursiveObj(nptr->userData(),buf,depth + 1,seen,flags);
|
2012-05-30 08:32:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-18 17:17:43 +00:00
|
|
|
const String JsObject::s_protoName("__proto__");
|
|
|
|
|
2012-02-17 16:19:17 +00:00
|
|
|
JsObject::JsObject(const char* name, Mutex* mtx, bool frozen)
|
2012-05-30 13:18:34 +00:00
|
|
|
: ScriptContext(String("[object ") + name + "]"),
|
2012-02-17 16:19:17 +00:00
|
|
|
m_frozen(frozen), m_mutex(mtx)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"JsObject::JsObject('%s',%p,%s) [%p]",
|
|
|
|
name,mtx,String::boolText(frozen),this);
|
|
|
|
params().addParam(new ExpFunction("freeze"));
|
|
|
|
params().addParam(new ExpFunction("isFrozen"));
|
|
|
|
params().addParam(new ExpFunction("toString"));
|
2012-05-02 15:11:48 +00:00
|
|
|
params().addParam(new ExpFunction("hasOwnProperty"));
|
2012-02-17 16:19:17 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 19:51:36 +00:00
|
|
|
JsObject::JsObject(Mutex* mtx, const char* name, bool frozen)
|
|
|
|
: ScriptContext(name),
|
|
|
|
m_frozen(frozen), m_mutex(mtx)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"JsObject::JsObject(%p,'%s',%s) [%p]",
|
|
|
|
mtx,name,String::boolText(frozen),this);
|
|
|
|
}
|
|
|
|
|
2014-07-22 11:02:44 +00:00
|
|
|
JsObject::JsObject(GenObject* context, Mutex* mtx, bool frozen)
|
|
|
|
: ScriptContext("[object Object]"),
|
|
|
|
m_frozen(frozen), m_mutex(mtx)
|
|
|
|
{
|
|
|
|
setPrototype(context,YSTRING("Object"));
|
|
|
|
}
|
|
|
|
|
2012-02-17 16:19:17 +00:00
|
|
|
JsObject::~JsObject()
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"JsObject::~JsObject '%s' [%p]",toString().c_str(),this);
|
|
|
|
}
|
|
|
|
|
2012-06-26 16:40:38 +00:00
|
|
|
JsObject* JsObject::copy(Mutex* mtx) const
|
|
|
|
{
|
|
|
|
JsObject* jso = new JsObject(mtx,toString(),frozen());
|
|
|
|
deepCopyParams(jso->params(),params(),mtx);
|
|
|
|
return jso;
|
|
|
|
}
|
|
|
|
|
2020-10-14 10:38:31 +00:00
|
|
|
void JsObject::dumpRecursive(const GenObject* obj, String& buf, unsigned int flags)
|
2012-05-30 08:32:47 +00:00
|
|
|
{
|
|
|
|
ObjList seen;
|
2020-10-14 10:38:31 +00:00
|
|
|
dumpRecursiveObj(obj,buf,0,seen,flags);
|
2012-05-30 08:32:47 +00:00
|
|
|
}
|
|
|
|
|
2020-10-14 10:38:31 +00:00
|
|
|
void JsObject::printRecursive(const GenObject* obj, unsigned int flags)
|
2012-05-30 08:32:47 +00:00
|
|
|
{
|
|
|
|
String buf;
|
2020-10-14 10:38:31 +00:00
|
|
|
dumpRecursive(obj,buf,flags);
|
2012-05-30 08:32:47 +00:00
|
|
|
Output("%s",buf.c_str());
|
|
|
|
}
|
|
|
|
|
2018-06-15 13:39:48 +00:00
|
|
|
String JsObject::strEscape(const char* str)
|
|
|
|
{
|
|
|
|
String s("\"");
|
|
|
|
char c;
|
|
|
|
while (str && (c = *str++)) {
|
|
|
|
switch (c) {
|
|
|
|
case '\"':
|
|
|
|
case '\\':
|
|
|
|
s += "\\";
|
|
|
|
break;
|
|
|
|
case '\b':
|
|
|
|
s += "\\b";
|
|
|
|
continue;
|
|
|
|
case '\f':
|
|
|
|
s += "\\f";
|
|
|
|
continue;
|
|
|
|
case '\n':
|
|
|
|
s += "\\n";
|
|
|
|
continue;
|
|
|
|
case '\r':
|
|
|
|
s += "\\r";
|
|
|
|
continue;
|
|
|
|
case '\t':
|
|
|
|
s += "\\t";
|
|
|
|
continue;
|
|
|
|
case '\v':
|
|
|
|
s += "\\v";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
s += c;
|
|
|
|
}
|
|
|
|
s += "\"";
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
ExpOperation* JsObject::toJSON(const ExpOperation* oper, int spaces)
|
|
|
|
{
|
|
|
|
if (!oper || YOBJECT(JsFunction,oper) || YOBJECT(ExpFunction,oper) || JsParser::isUndefined(*oper))
|
|
|
|
return 0;
|
|
|
|
if (spaces < 0)
|
|
|
|
spaces = 0;
|
|
|
|
else if (spaces > 10)
|
|
|
|
spaces = 10;
|
|
|
|
ExpOperation* ret = new ExpOperation("","JSON");
|
|
|
|
toJSON(oper,*ret,spaces);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void JsObject::toJSON(const NamedString* ns, String& buf, int spaces, int indent)
|
|
|
|
{
|
|
|
|
const ExpOperation* oper = YOBJECT(ExpOperation,ns);
|
|
|
|
if (!oper) {
|
|
|
|
if (ns)
|
|
|
|
buf << strEscape(*ns);
|
|
|
|
else
|
|
|
|
buf << "null";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (JsParser::isNull(*oper) || JsParser::isUndefined(*oper) || YOBJECT(JsFunction,oper)
|
|
|
|
|| YOBJECT(ExpFunction,oper)) {
|
|
|
|
buf << "null";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const char* nl = spaces ? "\r\n" : "";
|
|
|
|
JsObject* jso = YOBJECT(JsObject,oper);
|
|
|
|
JsArray* jsa = YOBJECT(JsArray,jso);
|
|
|
|
if (jsa) {
|
|
|
|
if (jsa->length() <= 0) {
|
|
|
|
buf << "[]";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
String li(' ',indent);
|
|
|
|
String ci(' ',indent + spaces);
|
|
|
|
buf << "[" << nl;
|
|
|
|
for (int32_t i = 0; ; ) {
|
|
|
|
buf << ci;
|
|
|
|
const NamedString* p = jsa->params().getParam(String(i));
|
|
|
|
if (p)
|
|
|
|
toJSON(p,buf,spaces,indent + spaces);
|
|
|
|
else
|
|
|
|
buf << "null";
|
|
|
|
if (++i < jsa->length())
|
|
|
|
buf << "," << nl;
|
|
|
|
else {
|
|
|
|
buf << nl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buf << li << "]";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (jso) {
|
2019-11-08 11:24:35 +00:00
|
|
|
if (YOBJECT(JsDate,jso)) {
|
|
|
|
buf << strEscape(jso->toString());
|
|
|
|
return;
|
|
|
|
}
|
2018-06-15 13:39:48 +00:00
|
|
|
switch (jso->params().count()) {
|
|
|
|
case 1:
|
|
|
|
if (!jso->params().getParam(protoName()))
|
|
|
|
break;
|
|
|
|
// fall through
|
|
|
|
case 0:
|
|
|
|
buf << "{}";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ObjList* l = jso->params().paramList()->skipNull();
|
|
|
|
String li(' ',indent);
|
|
|
|
String ci(' ',indent + spaces);
|
|
|
|
const char* sep = spaces ? ": " : ":";
|
|
|
|
buf << "{" << nl;
|
|
|
|
while (l) {
|
|
|
|
const NamedString* p = static_cast<const NamedString*>(l->get());
|
|
|
|
l = l->skipNext();
|
|
|
|
if (p->name() == protoName() || YOBJECT(JsFunction,p) || YOBJECT(ExpFunction,p))
|
|
|
|
continue;
|
|
|
|
const ExpOperation* op = YOBJECT(ExpOperation,p);
|
|
|
|
if (op && JsParser::isUndefined(*op))
|
|
|
|
continue;
|
|
|
|
buf << ci << strEscape(p->name()) << sep;
|
|
|
|
toJSON(p,buf,spaces,indent + spaces);
|
|
|
|
for (; l; l = l->skipNext()) {
|
|
|
|
p = static_cast<const NamedString*>(l->get());
|
|
|
|
op = YOBJECT(ExpOperation,p);
|
|
|
|
if (!(p->name() == protoName() || YOBJECT(JsFunction,p) || YOBJECT(ExpFunction,p)
|
|
|
|
|| (op && JsParser::isUndefined(*op))))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (l)
|
|
|
|
buf << ",";
|
|
|
|
buf << nl;
|
|
|
|
}
|
|
|
|
buf << li << "}";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (oper->isBoolean())
|
|
|
|
buf << String::boolText(oper->valBoolean());
|
|
|
|
else if (oper->isNumber()) {
|
|
|
|
if (oper->isInteger())
|
|
|
|
buf << oper->number();
|
|
|
|
else
|
|
|
|
buf << "null";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
buf << strEscape(*oper);
|
|
|
|
}
|
|
|
|
|
2014-03-13 13:10:56 +00:00
|
|
|
void JsObject::setPrototype(GenObject* context, const String& objName)
|
|
|
|
{
|
|
|
|
ScriptContext* ctxt = YOBJECT(ScriptContext,context);
|
|
|
|
if (!ctxt) {
|
|
|
|
ScriptRun* sr = static_cast<ScriptRun*>(context);
|
|
|
|
if (!(sr && (ctxt = YOBJECT(ScriptContext,sr->context()))))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
JsObject* objCtr = YOBJECT(JsObject,ctxt->params().getParam(objName));
|
|
|
|
if (objCtr) {
|
|
|
|
JsObject* proto = YOBJECT(JsObject,objCtr->params().getParam(YSTRING("prototype")));
|
|
|
|
if (proto && proto->ref())
|
|
|
|
params().addParam(new ExpWrapper(proto,protoName()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-26 13:31:30 +00:00
|
|
|
JsObject* JsObject::buildCallContext(Mutex* mtx, JsObject* thisObj)
|
2012-06-16 21:03:40 +00:00
|
|
|
{
|
|
|
|
JsObject* ctxt = new JsObject(mtx,"()");
|
2012-06-26 16:40:38 +00:00
|
|
|
if (thisObj && thisObj->alive())
|
2012-06-16 21:03:40 +00:00
|
|
|
ctxt->params().addParam(new ExpWrapper(thisObj,"this"));
|
|
|
|
return ctxt;
|
|
|
|
}
|
|
|
|
|
2012-06-19 18:55:45 +00:00
|
|
|
void JsObject::fillFieldNames(ObjList& names)
|
|
|
|
{
|
2017-09-18 12:14:44 +00:00
|
|
|
ScriptContext::fillFieldNames(names,params(),false,"__");
|
2012-06-19 18:55:45 +00:00
|
|
|
const NamedList* native = nativeParams();
|
|
|
|
if (native)
|
|
|
|
ScriptContext::fillFieldNames(names,*native);
|
2012-06-25 15:41:47 +00:00
|
|
|
#ifdef XDEBUG
|
|
|
|
String tmp;
|
|
|
|
tmp.append(names,",");
|
|
|
|
Debug(DebugInfo,"JsObject::fillFieldNames: %s",tmp.c_str());
|
|
|
|
#endif
|
2012-06-19 18:55:45 +00:00
|
|
|
}
|
|
|
|
|
2012-06-18 17:17:43 +00:00
|
|
|
bool JsObject::hasField(ObjList& stack, const String& name, GenObject* context) const
|
|
|
|
{
|
|
|
|
if (ScriptContext::hasField(stack,name,context))
|
|
|
|
return true;
|
|
|
|
const ScriptContext* proto = YOBJECT(ScriptContext,params().getParam(protoName()));
|
2012-06-19 18:55:45 +00:00
|
|
|
if (proto && proto->hasField(stack,name,context))
|
|
|
|
return true;
|
|
|
|
NamedList* np = nativeParams();
|
|
|
|
return np && np->getParam(name);
|
2012-06-18 17:17:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NamedString* JsObject::getField(ObjList& stack, const String& name, GenObject* context) const
|
|
|
|
{
|
|
|
|
NamedString* fld = ScriptContext::getField(stack,name,context);
|
|
|
|
if (fld)
|
|
|
|
return fld;
|
|
|
|
const ScriptContext* proto = YOBJECT(ScriptContext,params().getParam(protoName()));
|
2012-06-19 18:55:45 +00:00
|
|
|
if (proto) {
|
|
|
|
fld = proto->getField(stack,name,context);
|
|
|
|
if (fld)
|
|
|
|
return fld;
|
|
|
|
}
|
|
|
|
NamedList* np = nativeParams();
|
|
|
|
if (np)
|
|
|
|
return np->getParam(name);
|
|
|
|
return 0;
|
2012-06-18 17:17:43 +00:00
|
|
|
}
|
|
|
|
|
2012-06-18 13:52:21 +00:00
|
|
|
JsObject* JsObject::runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
2012-06-18 17:17:43 +00:00
|
|
|
if (!ref())
|
|
|
|
return 0;
|
2012-10-30 09:00:43 +00:00
|
|
|
JsObject* obj = clone("[object " + oper.name() + "]");
|
2012-06-18 17:17:43 +00:00
|
|
|
obj->params().addParam(new ExpWrapper(this,protoName()));
|
2012-06-18 13:52:21 +00:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2012-02-17 16:19:17 +00:00
|
|
|
bool JsObject::runFunction(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
XDebug(DebugInfo,"JsObject::runFunction() '%s' in '%s' [%p]",
|
|
|
|
oper.name().c_str(),toString().c_str(),this);
|
2012-06-18 17:17:43 +00:00
|
|
|
NamedString* param = getField(stack,oper.name(),context);
|
2012-02-17 16:19:17 +00:00
|
|
|
if (!param)
|
|
|
|
return false;
|
|
|
|
ExpFunction* ef = YOBJECT(ExpFunction,param);
|
|
|
|
if (ef)
|
|
|
|
return runNative(stack,oper,context);
|
|
|
|
JsFunction* jf = YOBJECT(JsFunction,param);
|
2012-11-20 16:11:38 +00:00
|
|
|
if (jf) {
|
|
|
|
JsObject* objThis = 0;
|
|
|
|
if (toString() != YSTRING("()"))
|
|
|
|
objThis = this;
|
|
|
|
return jf->runDefined(stack,oper,context,objThis);
|
|
|
|
}
|
2012-02-17 16:19:17 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JsObject::runField(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"JsObject::runField() '%s' in '%s' [%p]",
|
|
|
|
oper.name().c_str(),toString().c_str(),this);
|
2012-06-18 17:17:43 +00:00
|
|
|
const String* param = getField(stack,oper.name(),context);
|
2012-02-17 16:19:17 +00:00
|
|
|
if (param) {
|
|
|
|
ExpFunction* ef = YOBJECT(ExpFunction,param);
|
|
|
|
if (ef)
|
2012-05-30 08:32:47 +00:00
|
|
|
ExpEvaluator::pushOne(stack,ef->ExpOperation::clone());
|
2012-02-17 16:19:17 +00:00
|
|
|
else {
|
2012-11-20 16:11:38 +00:00
|
|
|
ExpWrapper* w = YOBJECT(ExpWrapper,param);
|
|
|
|
if (w)
|
|
|
|
ExpEvaluator::pushOne(stack,w->clone(oper.name()));
|
2013-04-12 12:54:30 +00:00
|
|
|
else {
|
2014-07-11 16:24:15 +00:00
|
|
|
JsObject* jso = YOBJECT(JsObject,param);
|
|
|
|
if (jso && jso->ref())
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpWrapper(jso,oper.name()));
|
|
|
|
else {
|
|
|
|
ExpOperation* o = YOBJECT(ExpOperation,param);
|
|
|
|
ExpEvaluator::pushOne(stack,o ? new ExpOperation(*o,oper.name(),false) : new ExpOperation(*param,oper.name(),true));
|
|
|
|
}
|
2013-04-12 12:54:30 +00:00
|
|
|
}
|
2012-02-17 16:19:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpWrapper(0,oper.name()));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JsObject::runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
2013-04-12 12:54:30 +00:00
|
|
|
XDebug(DebugAll,"JsObject::runAssign() '%s'='%s' (%s) in '%s' [%p]",
|
|
|
|
oper.name().c_str(),oper.c_str(),oper.typeOf(),toString().c_str(),this);
|
2012-02-17 16:19:17 +00:00
|
|
|
if (frozen()) {
|
2012-06-22 14:06:16 +00:00
|
|
|
Debug(DebugWarn,"Object '%s' is frozen",toString().c_str());
|
2012-02-17 16:19:17 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ExpFunction* ef = YOBJECT(ExpFunction,&oper);
|
|
|
|
if (ef)
|
2012-05-30 08:32:47 +00:00
|
|
|
params().setParam(ef->ExpOperation::clone());
|
2012-02-17 16:19:17 +00:00
|
|
|
else {
|
|
|
|
ExpWrapper* w = YOBJECT(ExpWrapper,&oper);
|
2013-04-12 12:46:21 +00:00
|
|
|
if (w) {
|
|
|
|
JsFunction* jsf = YOBJECT(JsFunction,w->object());
|
|
|
|
if (jsf)
|
|
|
|
jsf->firstName(oper.name());
|
2013-02-12 09:48:29 +00:00
|
|
|
params().setParam(w->clone(oper.name()));
|
2013-04-12 12:46:21 +00:00
|
|
|
}
|
2012-02-17 16:19:17 +00:00
|
|
|
else
|
2013-04-12 12:54:30 +00:00
|
|
|
params().setParam(oper.clone());
|
2012-02-17 16:19:17 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JsObject::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"JsObject::runNative() '%s' in '%s' [%p]",
|
|
|
|
oper.name().c_str(),toString().c_str(),this);
|
|
|
|
if (oper.name() == YSTRING("freeze"))
|
|
|
|
freeze();
|
|
|
|
else if (oper.name() == YSTRING("isFrozen"))
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(frozen()));
|
|
|
|
else if (oper.name() == YSTRING("toString"))
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(params()));
|
2012-05-02 15:11:48 +00:00
|
|
|
else if (oper.name() == YSTRING("hasOwnProperty")) {
|
|
|
|
bool ok = true;
|
2013-08-07 11:24:10 +00:00
|
|
|
for (int i = (int)oper.number(); i; i--) {
|
2012-05-02 15:11:48 +00:00
|
|
|
ExpOperation* op = popValue(stack,context);
|
|
|
|
if (!op)
|
|
|
|
continue;
|
|
|
|
ok = ok && params().getParam(*op);
|
|
|
|
TelEngine::destruct(op);
|
|
|
|
}
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(ok));
|
|
|
|
}
|
2012-02-17 16:19:17 +00:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ExpOperation* JsObject::popValue(ObjList& stack, GenObject* context)
|
|
|
|
{
|
|
|
|
ExpOperation* oper = ExpEvaluator::popOne(stack);
|
|
|
|
if (!oper || (oper->opcode() != ExpEvaluator::OpcField))
|
|
|
|
return oper;
|
2012-05-30 17:48:13 +00:00
|
|
|
XDebug(DebugAll,"JsObject::popValue() field '%s' in '%s' [%p]",
|
|
|
|
oper->name().c_str(),toString().c_str(),this);
|
|
|
|
bool ok = runMatchingField(stack,*oper,context);
|
2012-02-17 16:19:17 +00:00
|
|
|
TelEngine::destruct(oper);
|
|
|
|
return ok ? ExpEvaluator::popOne(stack) : 0;
|
|
|
|
}
|
2012-02-10 14:53:55 +00:00
|
|
|
|
2012-05-15 14:43:15 +00:00
|
|
|
// Static method that adds an object to a parent
|
|
|
|
void JsObject::addObject(NamedList& params, const char* name, JsObject* obj)
|
|
|
|
{
|
|
|
|
params.addParam(new NamedPointer(name,obj,obj->toString()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Static method that adds a constructor to a parent
|
|
|
|
void JsObject::addConstructor(NamedList& params, const char* name, JsObject* obj)
|
|
|
|
{
|
|
|
|
JsFunction* ctr = new JsFunction(obj->mutex(),name);
|
|
|
|
ctr->params().addParam(new NamedPointer("prototype",obj,obj->toString()));
|
2012-06-18 13:52:21 +00:00
|
|
|
obj->initConstructor(ctr);
|
2012-05-15 14:43:15 +00:00
|
|
|
params.addParam(new NamedPointer(name,ctr,ctr->toString()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Static method that pops arguments off a stack to a list in proper order
|
2012-06-16 21:03:40 +00:00
|
|
|
int JsObject::extractArgs(JsObject* obj, ObjList& stack, const ExpOperation& oper,
|
|
|
|
GenObject* context, ObjList& arguments)
|
2012-05-15 14:43:15 +00:00
|
|
|
{
|
|
|
|
if (!obj || !oper.number())
|
|
|
|
return 0;
|
2013-08-07 11:24:10 +00:00
|
|
|
for (int i = (int)oper.number(); i; i--) {
|
2012-05-15 14:43:15 +00:00
|
|
|
ExpOperation* op = obj->popValue(stack,context);
|
2016-03-08 12:23:11 +00:00
|
|
|
JsFunction* jsf = YOBJECT(JsFunction,op);
|
|
|
|
if (jsf)
|
|
|
|
jsf->firstName(op->name());
|
2012-05-15 14:43:15 +00:00
|
|
|
arguments.insert(op);
|
|
|
|
}
|
2013-08-07 11:24:10 +00:00
|
|
|
return (int)oper.number();
|
2012-05-15 14:43:15 +00:00
|
|
|
}
|
|
|
|
|
2012-06-27 14:44:05 +00:00
|
|
|
// Static helper method that deep copies all parameters
|
|
|
|
void JsObject::deepCopyParams(NamedList& dst, const NamedList& src, Mutex* mtx)
|
|
|
|
{
|
|
|
|
NamedIterator iter(src);
|
|
|
|
while (const NamedString* p = iter.get()) {
|
|
|
|
ExpOperation* oper = YOBJECT(ExpOperation,p);
|
|
|
|
if (oper)
|
|
|
|
dst.addParam(oper->copy(mtx));
|
|
|
|
else
|
|
|
|
dst.addParam(p->name(),*p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-10 14:53:55 +00:00
|
|
|
// Initialize standard globals in the execution context
|
2012-02-17 16:19:17 +00:00
|
|
|
void JsObject::initialize(ScriptContext* context)
|
|
|
|
{
|
|
|
|
if (!context)
|
|
|
|
return;
|
|
|
|
Mutex* mtx = context->mutex();
|
|
|
|
Lock mylock(mtx);
|
|
|
|
NamedList& p = context->params();
|
2012-05-30 13:18:34 +00:00
|
|
|
static_cast<String&>(p) = "[object Global]";
|
2012-02-17 16:19:17 +00:00
|
|
|
if (!p.getParam(YSTRING("Object")))
|
2012-05-14 19:51:36 +00:00
|
|
|
addConstructor(p,"Object",new JsObjectObj(mtx));
|
2012-02-17 16:19:17 +00:00
|
|
|
if (!p.getParam(YSTRING("Function")))
|
2012-05-14 19:51:36 +00:00
|
|
|
addConstructor(p,"Function",new JsFunction(mtx));
|
2012-03-05 09:53:31 +00:00
|
|
|
if (!p.getParam(YSTRING("Array")))
|
2012-05-14 19:51:36 +00:00
|
|
|
addConstructor(p,"Array",new JsArray(mtx));
|
2012-05-28 16:10:41 +00:00
|
|
|
if (!p.getParam(YSTRING("RegExp")))
|
|
|
|
addConstructor(p,"RegExp",new JsRegExp(mtx));
|
2012-02-17 16:19:17 +00:00
|
|
|
if (!p.getParam(YSTRING("Date")))
|
2012-05-14 19:51:36 +00:00
|
|
|
addConstructor(p,"Date",new JsDate(mtx));
|
2012-02-17 16:19:17 +00:00
|
|
|
if (!p.getParam(YSTRING("Math")))
|
|
|
|
addObject(p,"Math",new JsMath(mtx));
|
|
|
|
}
|
|
|
|
|
2019-11-11 14:33:26 +00:00
|
|
|
bool JsObject::getIntField(const String& name, int64_t& val)
|
|
|
|
{
|
|
|
|
NamedString* ns = params().getParam(name);
|
|
|
|
ExpOperation* op = YOBJECT(ExpOperation,ns);
|
|
|
|
if (!(op && op->isInteger()))
|
|
|
|
return false;
|
|
|
|
val = op->number();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JsObject::getBoolField(const String& name, bool& val)
|
|
|
|
{
|
|
|
|
NamedString* ns = params().getParam(name);
|
|
|
|
ExpOperation* op = YOBJECT(ExpOperation,ns);
|
|
|
|
if (!(op && op->isBoolean()))
|
|
|
|
return false;
|
|
|
|
val = op->valBoolean();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JsObject::getStringField(const String& name, String& val)
|
|
|
|
{
|
|
|
|
NamedString* ns = params().getParam(name);
|
|
|
|
if (!(ns && *ns))
|
|
|
|
return false;
|
|
|
|
val = ns;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JsObject::getObjField(const String& name, JsObject*& obj)
|
|
|
|
{
|
|
|
|
if (!name)
|
|
|
|
return false;
|
|
|
|
String* n = params().getParam(name);
|
|
|
|
JsObject* jso = YOBJECT(JsObject,n);
|
|
|
|
if (jso && jso ->ref()) {
|
|
|
|
obj = jso;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2012-02-17 16:19:17 +00:00
|
|
|
|
|
|
|
bool JsObjectObj::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
if (oper.name() == YSTRING("constructor"))
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpWrapper(new JsObject("Object",mutex())));
|
2015-09-16 17:33:28 +00:00
|
|
|
else if (oper.name() == YSTRING("keys")) {
|
|
|
|
ExpOperation* op = 0;
|
|
|
|
GenObject* obj = 0;
|
|
|
|
if (oper.number() == 0) {
|
|
|
|
ScriptRun* run = YOBJECT(ScriptRun,context);
|
|
|
|
if (run)
|
|
|
|
obj = run->context();
|
|
|
|
else
|
|
|
|
obj = context;
|
|
|
|
}
|
|
|
|
else if (oper.number() == 1) {
|
|
|
|
op = popValue(stack,context);
|
|
|
|
if (!op)
|
|
|
|
return false;
|
|
|
|
obj = op;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
const NamedList* lst = YOBJECT(NamedList,obj);
|
|
|
|
if (lst) {
|
|
|
|
NamedIterator iter(*lst);
|
|
|
|
JsArray* jsa = new JsArray(context,mutex());
|
|
|
|
while (const NamedString* ns = iter.get())
|
|
|
|
if (ns->name() != protoName())
|
|
|
|
jsa->push(new ExpOperation(ns->name(),0,true));
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpWrapper(jsa,"keys"));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ExpEvaluator::pushOne(stack,JsParser::nullClone());
|
|
|
|
TelEngine::destruct(op);
|
|
|
|
}
|
2012-02-17 16:19:17 +00:00
|
|
|
else
|
|
|
|
return JsObject::runNative(stack,oper,context);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-05-02 15:11:48 +00:00
|
|
|
|
2012-05-24 17:15:34 +00:00
|
|
|
JsArray::JsArray(Mutex* mtx)
|
|
|
|
: JsObject("Array",mtx), m_length(0)
|
|
|
|
{
|
|
|
|
params().addParam(new ExpFunction("push"));
|
|
|
|
params().addParam(new ExpFunction("pop"));
|
|
|
|
params().addParam(new ExpFunction("concat"));
|
|
|
|
params().addParam(new ExpFunction("join"));
|
|
|
|
params().addParam(new ExpFunction("reverse"));
|
|
|
|
params().addParam(new ExpFunction("shift"));
|
|
|
|
params().addParam(new ExpFunction("unshift"));
|
|
|
|
params().addParam(new ExpFunction("slice"));
|
|
|
|
params().addParam(new ExpFunction("splice"));
|
|
|
|
params().addParam(new ExpFunction("sort"));
|
2018-05-10 12:55:45 +00:00
|
|
|
params().addParam(new ExpFunction("includes"));
|
2013-02-12 10:16:12 +00:00
|
|
|
params().addParam(new ExpFunction("indexOf"));
|
2015-10-01 15:15:09 +00:00
|
|
|
params().addParam(new ExpFunction("lastIndexOf"));
|
2012-05-24 17:15:34 +00:00
|
|
|
params().addParam("length","0");
|
|
|
|
}
|
|
|
|
|
2014-03-13 13:10:56 +00:00
|
|
|
JsArray::JsArray(GenObject* context, Mutex* mtx)
|
|
|
|
: JsObject(mtx,"[object Array]"), m_length(0)
|
|
|
|
{
|
|
|
|
setPrototype(context,YSTRING("Array"));
|
|
|
|
}
|
|
|
|
|
2012-06-26 16:40:38 +00:00
|
|
|
JsObject* JsArray::copy(Mutex* mtx) const
|
|
|
|
{
|
|
|
|
JsArray* jsa = new JsArray(mtx,toString(),frozen());
|
|
|
|
deepCopyParams(jsa->params(),params(),mtx);
|
|
|
|
jsa->setLength(length());
|
|
|
|
return jsa;
|
|
|
|
}
|
|
|
|
|
2012-05-24 17:15:34 +00:00
|
|
|
void JsArray::push(ExpOperation* item)
|
|
|
|
{
|
|
|
|
if (!item)
|
|
|
|
return;
|
|
|
|
unsigned int pos = m_length;
|
|
|
|
while (params().getParam(String(pos)))
|
|
|
|
pos++;
|
|
|
|
const_cast<String&>(item->name()) = pos;
|
|
|
|
params().addParam(item);
|
|
|
|
setLength(pos + 1);
|
|
|
|
}
|
|
|
|
|
2013-07-11 13:25:09 +00:00
|
|
|
bool JsArray::runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"JsArray::runAssign() '%s'='%s' (%s) in '%s' [%p]",
|
|
|
|
oper.name().c_str(),oper.c_str(),oper.typeOf(),toString().c_str(),this);
|
2013-07-16 13:47:17 +00:00
|
|
|
if (oper.name() == YSTRING("length")) {
|
|
|
|
int newLen = oper.toInteger(-1);
|
|
|
|
if (newLen < 0)
|
|
|
|
return false;
|
|
|
|
for (int i = newLen; i < length(); i++)
|
|
|
|
params().clearParam(String(i));
|
|
|
|
setLength(newLen);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (!JsObject::runAssign(stack,oper,context))
|
2013-07-11 13:25:09 +00:00
|
|
|
return false;
|
|
|
|
int idx = oper.toString().toInteger(-1) + 1;
|
|
|
|
if (idx && idx > m_length)
|
|
|
|
setLength(idx);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-07-16 13:47:17 +00:00
|
|
|
bool JsArray::runField(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"JsArray::runField() '%s' in '%s' [%p]",
|
|
|
|
oper.name().c_str(),toString().c_str(),this);
|
|
|
|
if (oper.name() == YSTRING("length")) {
|
|
|
|
// Reflects the number of elements in an array.
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)length()));
|
2013-07-16 13:47:17 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return JsObject::runField(stack,oper,context);
|
|
|
|
}
|
|
|
|
|
2014-07-18 11:07:19 +00:00
|
|
|
void JsArray::initConstructor(JsFunction* construct)
|
|
|
|
{
|
|
|
|
construct->params().addParam(new ExpFunction("isArray"));
|
|
|
|
}
|
|
|
|
|
2014-02-26 12:25:53 +00:00
|
|
|
JsObject* JsArray::runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
if (!ref())
|
|
|
|
return 0;
|
|
|
|
JsArray* obj = static_cast<JsArray*>(clone("[object " + oper.name() + "]"));
|
2017-09-07 15:29:32 +00:00
|
|
|
unsigned int len = (unsigned int)oper.number();
|
2014-02-26 12:25:53 +00:00
|
|
|
for (unsigned int i = len; i; i--) {
|
|
|
|
ExpOperation* op = obj->popValue(stack,context);
|
|
|
|
if ((len == 1) && op->isInteger() && (op->number() >= 0) && (op->number() <= 0xffffffff)) {
|
2017-09-07 15:29:32 +00:00
|
|
|
len = (unsigned int)op->number();
|
2014-02-26 12:25:53 +00:00
|
|
|
TelEngine::destruct(op);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
const_cast<String&>(op->name()) = i - 1;
|
|
|
|
obj->params().paramList()->insert(op);
|
|
|
|
}
|
|
|
|
obj->setLength(len);
|
|
|
|
obj->params().addParam(new ExpWrapper(this,protoName()));
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2012-05-02 15:11:48 +00:00
|
|
|
bool JsArray::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
2012-05-14 19:51:36 +00:00
|
|
|
XDebug(DebugAll,"JsArray::runNative() '%s' in '%s' [%p]",
|
|
|
|
oper.name().c_str(),toString().c_str(),this);
|
2014-07-18 11:07:19 +00:00
|
|
|
if (oper.name() == YSTRING("isArray")) {
|
|
|
|
// Static function that checks if the argument is an Array
|
|
|
|
ObjList args;
|
|
|
|
extractArgs(this,stack,oper,context,args);
|
2017-09-07 15:29:32 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(!!YOBJECT(JsArray,args[0])));
|
2014-07-18 11:07:19 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("push")) {
|
2012-05-11 16:15:20 +00:00
|
|
|
// Adds one or more elements to the end of an array and returns the new length of the array.
|
2012-06-25 15:41:47 +00:00
|
|
|
ObjList args;
|
|
|
|
if (!extractArgs(this,stack,oper,context,args))
|
2012-05-11 16:15:20 +00:00
|
|
|
return false;
|
2012-06-25 15:41:47 +00:00
|
|
|
while (ExpOperation* op = static_cast<ExpOperation*>(args.remove(false))) {
|
|
|
|
const_cast<String&>(op->name()) = (unsigned int)m_length++;
|
|
|
|
params().addParam(op);
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)length()));
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("pop")) {
|
|
|
|
// Removes the last element from an array and returns that element
|
2014-02-05 17:22:09 +00:00
|
|
|
if (oper.number())
|
|
|
|
return false;
|
2012-05-11 16:15:20 +00:00
|
|
|
NamedString* last = 0;
|
2014-02-05 17:22:09 +00:00
|
|
|
while ((m_length > 0) && !last)
|
2013-08-06 15:42:44 +00:00
|
|
|
last = params().getParam(String(--m_length));
|
2012-05-11 16:15:20 +00:00
|
|
|
if (!last)
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpWrapper(0,0));
|
|
|
|
else {
|
2014-02-05 17:22:09 +00:00
|
|
|
params().paramList()->remove(last,false);
|
|
|
|
ExpOperation* op = YOBJECT(ExpOperation,last);
|
|
|
|
if (!op) {
|
|
|
|
op = new ExpOperation(*last,0,true);
|
|
|
|
TelEngine::destruct(last);
|
|
|
|
}
|
|
|
|
ExpEvaluator::pushOne(stack,op);
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("concat")) {
|
|
|
|
// Returns a new array comprised of this array joined with other array(s) and/or value(s).
|
2014-02-05 11:42:17 +00:00
|
|
|
// var num1 = [1, 2, 3];
|
|
|
|
// var num2 = [4, 5, 6];
|
|
|
|
// var num3 = [7, 8, 9];
|
2012-05-11 16:15:20 +00:00
|
|
|
//
|
2014-02-05 11:42:17 +00:00
|
|
|
// creates array [1, 2, 3, 4, 5, 6, 7, 8, 9]; num1, num2, num3 are unchanged
|
|
|
|
// var nums = num1.concat(num2, num3);
|
2012-05-11 16:15:20 +00:00
|
|
|
|
2014-02-05 11:42:17 +00:00
|
|
|
// var alpha = ['a', 'b', 'c'];
|
|
|
|
// creates array ["a", "b", "c", 1, 2, 3], leaving alpha unchanged
|
2012-05-11 16:15:20 +00:00
|
|
|
// var alphaNumeric = alpha.concat(1, [2, 3]);
|
2014-02-05 16:49:01 +00:00
|
|
|
|
2014-02-06 10:02:01 +00:00
|
|
|
ObjList args;
|
|
|
|
extractArgs(this,stack,oper,context,args);
|
|
|
|
|
2014-03-14 10:09:15 +00:00
|
|
|
JsArray* array = new JsArray(context,mutex());
|
2014-02-06 10:02:01 +00:00
|
|
|
// copy this array - only numerically indexed elements!
|
|
|
|
for (int i = 0; i < m_length; i++) {
|
|
|
|
NamedString* ns = params().getParam(String(i));
|
|
|
|
ExpOperation* op = YOBJECT(ExpOperation,ns);
|
|
|
|
op = op ? op->clone() : new ExpOperation(*ns,ns->name(),true);
|
|
|
|
array->params().addParam(op);
|
|
|
|
}
|
2012-05-11 16:15:20 +00:00
|
|
|
array->setLength(length());
|
2014-02-06 10:02:01 +00:00
|
|
|
// add parameters - either basic types or elements of Array
|
|
|
|
while (ExpOperation* op = static_cast<ExpOperation*>(args.remove(false))) {
|
|
|
|
JsArray* ja = YOBJECT(JsArray,op);
|
2012-05-11 16:15:20 +00:00
|
|
|
if (ja) {
|
2014-02-06 10:02:01 +00:00
|
|
|
int len = ja->length();
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
NamedString* ns = ja->params().getParam(String(i));
|
2014-03-14 10:35:29 +00:00
|
|
|
ExpOperation* arg = YOBJECT(ExpOperation,ns);
|
|
|
|
arg = arg ? arg->clone() : new ExpOperation(*ns,0,true);
|
|
|
|
const_cast<String&>(arg->name()) = (unsigned int)array->m_length++;
|
|
|
|
array->params().addParam(arg);
|
2014-02-06 10:02:01 +00:00
|
|
|
}
|
2014-03-14 10:35:29 +00:00
|
|
|
TelEngine::destruct(op);
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else {
|
2014-02-06 10:02:01 +00:00
|
|
|
const_cast<String&>(op->name()) = (unsigned int)array->m_length++;
|
|
|
|
array->params().addParam(op);
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
}
|
2014-02-06 10:02:01 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpWrapper(array));
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("join")) {
|
|
|
|
// Joins all elements of an array into a string
|
|
|
|
// var a = new Array("Wind","Rain","Fire");
|
|
|
|
// var myVar1 = a.join(); // assigns "Wind,Rain,Fire" to myVar1
|
|
|
|
// var myVar2 = a.join(", "); // assigns "Wind, Rain, Fire" to myVar2
|
|
|
|
// var myVar3 = a.join(" + "); // assigns "Wind + Rain + Fire" to myVar3
|
|
|
|
String separator = ",";
|
|
|
|
if (oper.number()) {
|
|
|
|
ExpOperation* op = popValue(stack,context);
|
2013-01-21 11:03:06 +00:00
|
|
|
separator = *op;
|
|
|
|
TelEngine::destruct(op);
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
String result;
|
2013-08-07 09:49:52 +00:00
|
|
|
for (int32_t i = 0; i < length(); i++)
|
2013-08-06 15:42:44 +00:00
|
|
|
result.append(params()[String(i)],separator);
|
2012-05-11 16:15:20 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(result));
|
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("reverse")) {
|
|
|
|
// Reverses the order of the elements of an array -- the first becomes the last, and the last becomes the first.
|
|
|
|
// var myArray = ["one", "two", "three"];
|
|
|
|
// myArray.reverse(); => three, two, one
|
2014-02-06 10:02:01 +00:00
|
|
|
if (oper.number())
|
|
|
|
return false;
|
|
|
|
int i1 = 0;
|
|
|
|
int i2 = length() - 1;
|
|
|
|
for (; i1 < i2; i1++, i2--) {
|
|
|
|
String s1(i1);
|
|
|
|
String s2(i2);
|
|
|
|
NamedString* n1 = params().getParam(s1);
|
|
|
|
NamedString* n2 = params().getParam(s2);
|
|
|
|
if (n1)
|
|
|
|
const_cast<String&>(n1->name()) = s2;
|
|
|
|
if (n2)
|
|
|
|
const_cast<String&>(n2->name()) = s1;
|
|
|
|
}
|
|
|
|
ref();
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpWrapper(this));
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("shift")) {
|
|
|
|
// Removes the first element from an array and returns that element
|
|
|
|
// var myFish = ["angel", "clown", "mandarin", "surgeon"];
|
|
|
|
// println("myFish before: " + myFish);
|
|
|
|
// var shifted = myFish.shift();
|
|
|
|
// println("myFish after: " + myFish);
|
|
|
|
// println("Removed this element: " + shifted);
|
|
|
|
// This example displays the following:
|
|
|
|
|
|
|
|
// myFish before: angel,clown,mandarin,surgeon
|
|
|
|
// myFish after: clown,mandarin,surgeon
|
|
|
|
// Removed this element: angel
|
2014-02-05 16:49:01 +00:00
|
|
|
if (oper.number())
|
|
|
|
return false;
|
|
|
|
ObjList* l = params().paramList()->find("0");
|
|
|
|
if (l) {
|
|
|
|
NamedString* ns = static_cast<NamedString*>(l->get());
|
|
|
|
params().paramList()->remove(ns,false);
|
|
|
|
ExpOperation* op = YOBJECT(ExpOperation,ns);
|
|
|
|
if (!op) {
|
|
|
|
op = new ExpOperation(*ns,0,true);
|
|
|
|
TelEngine::destruct(ns);
|
|
|
|
}
|
|
|
|
ExpEvaluator::pushOne(stack,op);
|
2012-05-11 16:15:20 +00:00
|
|
|
// shift : value n+1 becomes value n
|
2014-02-05 16:49:01 +00:00
|
|
|
for (int32_t i = 0; ; i++) {
|
|
|
|
ns = static_cast<NamedString*>((*params().paramList())[String(i + 1)]);
|
|
|
|
if (!ns) {
|
|
|
|
setLength(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
const_cast<String&>(ns->name()) = i;
|
|
|
|
}
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
2014-02-05 16:49:01 +00:00
|
|
|
else
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpWrapper(0,0));
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("unshift")) {
|
|
|
|
// Adds one or more elements to the front of an array and returns the new length of the array
|
|
|
|
// myFish = ["angel", "clown"];
|
|
|
|
// println("myFish before: " + myFish);
|
|
|
|
// unshifted = myFish.unshift("drum", "lion");
|
|
|
|
// println("myFish after: " + myFish);
|
|
|
|
// println("New length: " + unshifted);
|
|
|
|
// This example displays the following:
|
|
|
|
// myFish before: ["angel", "clown"]
|
|
|
|
// myFish after: ["drum", "lion", "angel", "clown"]
|
|
|
|
// New length: 4
|
|
|
|
// shift array
|
2013-08-07 11:24:10 +00:00
|
|
|
int32_t shift = (int32_t)oper.number();
|
2015-08-31 08:06:14 +00:00
|
|
|
if (shift >= 1) {
|
|
|
|
for (int32_t i = length() + shift - 1; i >= shift; i--) {
|
|
|
|
NamedString* ns = static_cast<NamedString*>((*params().paramList())[String(i - shift)]);
|
|
|
|
if (ns) {
|
|
|
|
String index(i);
|
|
|
|
params().clearParam(index);
|
|
|
|
const_cast<String&>(ns->name()) = index;
|
|
|
|
}
|
2014-02-05 17:22:09 +00:00
|
|
|
}
|
2015-08-31 08:06:14 +00:00
|
|
|
for (int32_t i = shift - 1; i >= 0; i--) {
|
|
|
|
ExpOperation* op = popValue(stack,context);
|
|
|
|
if (!op)
|
|
|
|
continue;
|
|
|
|
const_cast<String&>(op->name()) = i;
|
|
|
|
params().paramList()->insert(op);
|
|
|
|
}
|
|
|
|
setLength(length() + shift);
|
2014-02-05 17:22:09 +00:00
|
|
|
}
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)length()));
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("slice"))
|
|
|
|
return runNativeSlice(stack,oper,context);
|
|
|
|
else if (oper.name() == YSTRING("splice"))
|
|
|
|
return runNativeSplice(stack,oper,context);
|
|
|
|
else if (oper.name() == YSTRING("sort")) {
|
|
|
|
return runNativeSort(stack,oper,context);
|
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("toString")) {
|
|
|
|
// Override the JsObject toString method
|
|
|
|
// var monthNames = ['Jan', 'Feb', 'Mar', 'Apr'];
|
|
|
|
// var myVar = monthNames.toString(); // assigns "Jan,Feb,Mar,Apr" to myVar.
|
|
|
|
String separator = ",";
|
|
|
|
String result;
|
2013-08-07 09:49:52 +00:00
|
|
|
for (int32_t i = 0; i < length(); i++)
|
2013-08-06 15:42:44 +00:00
|
|
|
result.append(params()[String(i)],separator);
|
2012-05-11 16:15:20 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(result));
|
2018-05-10 12:55:45 +00:00
|
|
|
} else if (oper.name() == YSTRING("includes") || oper.name() == YSTRING("indexOf")
|
|
|
|
|| oper.name() == YSTRING("lastIndexOf")) {
|
|
|
|
// arr.includes(searchElement[,startIndex = 0[,"fieldName"]])
|
2015-10-01 15:15:09 +00:00
|
|
|
// arr.indexOf(searchElement[,startIndex = 0[,"fieldName"]])
|
|
|
|
// arr.lastIndexOf(searchElement[,startIndex = arr.length-1[,"fieldName"]])
|
2013-02-12 10:16:12 +00:00
|
|
|
ObjList args;
|
|
|
|
if (!extractArgs(this,stack,oper,context,args)) {
|
|
|
|
Debug(DebugWarn,"Failed to extract arguments!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ExpOperation* op1 = static_cast<ExpOperation*>(args.remove(false));
|
|
|
|
if (!op1)
|
|
|
|
return false;
|
2015-10-01 15:15:09 +00:00
|
|
|
ExpWrapper* w1 = YOBJECT(ExpWrapper,op1);
|
|
|
|
ExpOperation* fld = 0;
|
|
|
|
int dir = 1;
|
2013-02-12 10:16:12 +00:00
|
|
|
int pos = 0;
|
2015-10-01 15:15:09 +00:00
|
|
|
if (oper.name().at(0) == 'l') {
|
|
|
|
dir = -1;
|
|
|
|
pos = length() - 1;
|
|
|
|
}
|
2013-02-12 10:16:12 +00:00
|
|
|
if (args.skipNull()) {
|
|
|
|
String* spos = static_cast<String*>(args.remove(false));
|
2015-10-01 15:15:09 +00:00
|
|
|
if (spos) {
|
|
|
|
pos = spos->toInteger(pos);
|
|
|
|
if (pos < 0)
|
|
|
|
pos += length();
|
|
|
|
if (dir > 0) {
|
|
|
|
if (pos < 0)
|
|
|
|
pos = 0;
|
|
|
|
}
|
|
|
|
else if (pos >= length())
|
|
|
|
pos = length() - 1;
|
|
|
|
}
|
2013-02-12 10:16:12 +00:00
|
|
|
TelEngine::destruct(spos);
|
2015-10-01 15:15:09 +00:00
|
|
|
fld = static_cast<ExpOperation*>(args.remove(false));
|
2013-02-12 10:16:12 +00:00
|
|
|
}
|
|
|
|
int index = -1;
|
2015-10-01 15:15:09 +00:00
|
|
|
for (int i = pos; ; i += dir) {
|
|
|
|
if (dir > 0) {
|
|
|
|
if (i >= length())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (i < 0)
|
|
|
|
break;
|
2013-02-12 10:16:12 +00:00
|
|
|
ExpOperation* op2 = static_cast<ExpOperation*>(params().getParam(String(i)));
|
2015-10-01 15:15:09 +00:00
|
|
|
if (op2 && !TelEngine::null(fld)) {
|
|
|
|
const ExpExtender* ext = YOBJECT(ExpExtender,op2);
|
|
|
|
if (!ext)
|
|
|
|
continue;
|
|
|
|
op2 = YOBJECT(ExpOperation,ext->getField(stack,*fld,context));
|
|
|
|
}
|
2013-02-12 10:16:12 +00:00
|
|
|
if (!op2 || op2->opcode() != op1->opcode())
|
|
|
|
continue;
|
|
|
|
ExpWrapper* w2 = YOBJECT(ExpWrapper,op2);
|
|
|
|
if (w1 || w2) {
|
|
|
|
if (w1 && w2 && w1->object() == w2->object()) {
|
|
|
|
index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if ((op1->number() == op2->number()) && (*op1 == *op2)) {
|
|
|
|
index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TelEngine::destruct(op1);
|
2015-10-01 15:15:09 +00:00
|
|
|
TelEngine::destruct(fld);
|
2018-05-10 12:55:45 +00:00
|
|
|
if (oper.name().length() == 8)
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(index >= 0));
|
|
|
|
else
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)index));
|
2013-02-12 10:16:12 +00:00
|
|
|
return true;
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
return JsObject::runNative(stack,oper,context);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JsArray::runNativeSlice(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
// Extracts a section of an array and returns a new array.
|
2014-02-05 11:42:17 +00:00
|
|
|
// var myHonda = { color: "red", wheels: 4, engine: { cylinders: 4, size: 2.2 } };
|
|
|
|
// var myCar = [myHonda, 2, "cherry condition", "purchased 1997"];
|
|
|
|
// var newCar = myCar.slice(0, 2);
|
2014-03-14 16:08:31 +00:00
|
|
|
int32_t begin = 0, end = length();
|
|
|
|
switch (oper.number()) {
|
|
|
|
case 2:
|
|
|
|
{ // get end of interval
|
|
|
|
ExpOperation* op = popValue(stack,context);
|
|
|
|
if (op && op->isInteger())
|
|
|
|
end = op->number();
|
|
|
|
TelEngine::destruct(op);
|
|
|
|
}
|
|
|
|
// intentional fallthrough
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
ExpOperation* op = popValue(stack,context);
|
|
|
|
if (op && op->isInteger())
|
|
|
|
begin = op->number();
|
|
|
|
TelEngine::destruct(op);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// maybe we should ignore the rest of the given parameters?
|
|
|
|
return false;
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
2014-03-14 16:08:31 +00:00
|
|
|
if (begin < 0) {
|
2012-05-11 16:15:20 +00:00
|
|
|
begin = length() + begin;
|
2014-03-14 16:08:31 +00:00
|
|
|
if (begin < 0)
|
|
|
|
begin = 0;
|
|
|
|
}
|
2012-05-11 16:15:20 +00:00
|
|
|
if (end < 0)
|
|
|
|
end = length() + end;
|
2014-03-14 16:08:31 +00:00
|
|
|
|
|
|
|
JsArray* array = new JsArray(context,mutex());
|
|
|
|
for (int32_t i = begin; i < end; i++) {
|
|
|
|
NamedString* ns = params().getParam(String(i));
|
|
|
|
if (!ns) {
|
|
|
|
// if missing, insert undefined element in array also
|
|
|
|
array->m_length++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ExpOperation* arg = YOBJECT(ExpOperation,ns);
|
|
|
|
arg = arg ? arg->clone() : new ExpOperation(*ns,0,true);
|
|
|
|
const_cast<String&>(arg->name()) = (unsigned int)array->m_length++;
|
|
|
|
array->params().addParam(arg);
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
2014-03-14 16:08:31 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpWrapper(array));
|
2012-05-11 16:15:20 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JsArray::runNativeSplice(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
2014-02-05 11:42:17 +00:00
|
|
|
// Changes the content of an array, adding new elements while removing old elements.
|
2012-05-11 16:15:20 +00:00
|
|
|
// Returns an array containing the removed elements
|
|
|
|
// array.splice(index , howMany[, element1[, ...[, elementN]]])
|
|
|
|
// array.splice(index ,[ howMany[, element1[, ...[, elementN]]]])
|
2014-03-14 16:08:31 +00:00
|
|
|
ObjList args;
|
|
|
|
int argc = extractArgs(this,stack,oper,context,args);
|
2012-05-11 16:15:20 +00:00
|
|
|
if (!argc)
|
|
|
|
return false;
|
|
|
|
// get start index
|
2014-03-14 16:08:31 +00:00
|
|
|
int32_t len = length();
|
|
|
|
ExpOperation* op = static_cast<ExpOperation*>(args.remove(false));
|
|
|
|
int32_t begin = (int)(op->number() > len ? len : op->number());
|
2012-05-11 16:15:20 +00:00
|
|
|
if (begin < 0)
|
2014-03-14 16:08:31 +00:00
|
|
|
begin = len + begin > 0 ? len + begin : 0;
|
|
|
|
TelEngine::destruct(op);
|
|
|
|
argc--;
|
|
|
|
// get count of objects to delete
|
|
|
|
int32_t delCount = len - begin;
|
|
|
|
if (argc) {
|
|
|
|
op = static_cast<ExpOperation*>(args.remove(false));
|
|
|
|
// howMany is negative, set it to 0
|
|
|
|
if (op->number() < 0)
|
|
|
|
delCount = 0;
|
|
|
|
// if howMany is greater than the length of remaining elements from start index, do not set it
|
|
|
|
else if (op->number() < delCount)
|
|
|
|
delCount = op->number();
|
|
|
|
TelEngine::destruct(op);
|
|
|
|
argc--;
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// remove elements
|
2014-03-14 10:09:15 +00:00
|
|
|
JsArray* removed = new JsArray(context,mutex());
|
2014-03-14 16:08:31 +00:00
|
|
|
for (int32_t i = begin; i < begin + delCount; i++) {
|
|
|
|
NamedString* ns = params().getParam(String(i));
|
|
|
|
if (!ns) {
|
|
|
|
// if missing, insert undefined element in array also
|
|
|
|
removed->m_length++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
params().paramList()->remove(ns,false);
|
|
|
|
ExpOperation* op = YOBJECT(ExpOperation,ns);
|
|
|
|
if (!op) {
|
|
|
|
op = new ExpOperation(*ns,0,true);
|
|
|
|
TelEngine::destruct(ns);
|
|
|
|
}
|
|
|
|
const_cast<String&>(op->name()) = (unsigned int)removed->m_length++;
|
|
|
|
removed->params().addParam(op);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t shiftIdx = argc - delCount;
|
|
|
|
// shift elements to make room for those that are to be inserted or move the ones that remained
|
|
|
|
// after delete
|
|
|
|
if (shiftIdx > 0) {
|
|
|
|
for (int32_t i = m_length - 1; i >= begin + delCount; i--) {
|
|
|
|
NamedString* ns = static_cast<NamedString*>((*params().paramList())[String(i)]);
|
|
|
|
if (ns)
|
|
|
|
const_cast<String&>(ns->name()) = i + shiftIdx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (shiftIdx < 0) {
|
|
|
|
for (int32_t i = begin + delCount; i < m_length; i++) {
|
|
|
|
NamedString* ns = static_cast<NamedString*>((*params().paramList())[String(i)]);
|
|
|
|
if (ns)
|
|
|
|
const_cast<String&>(ns->name()) = i + shiftIdx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setLength(length() + shiftIdx);
|
|
|
|
// insert the new elements
|
|
|
|
for (int i = 0; i < argc; i++) {
|
|
|
|
ExpOperation* arg = static_cast<ExpOperation*>(args.remove(false));
|
|
|
|
const_cast<String&>(arg->name()) = (unsigned int)(begin + i);
|
|
|
|
params().addParam(arg);
|
|
|
|
}
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpWrapper(removed));
|
2012-05-11 16:15:20 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-02-12 10:16:12 +00:00
|
|
|
class JsComparator
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
JsComparator(const char* funcName, ScriptRun* runner)
|
2014-05-16 10:40:56 +00:00
|
|
|
: m_name(funcName), m_runner(runner), m_failed(false)
|
2013-02-12 10:16:12 +00:00
|
|
|
{ }
|
|
|
|
const char* m_name;
|
|
|
|
ScriptRun* m_runner;
|
2014-05-16 10:40:56 +00:00
|
|
|
bool m_failed;
|
2013-02-12 10:16:12 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
int compare(GenObject* op1, GenObject* op2, void* data)
|
|
|
|
{
|
|
|
|
JsComparator* cmp = static_cast<JsComparator*>(data);
|
2014-05-16 10:40:56 +00:00
|
|
|
if (cmp && cmp->m_failed)
|
|
|
|
return 0;
|
2013-02-12 10:16:12 +00:00
|
|
|
if (!(cmp && cmp->m_runner))
|
|
|
|
return ::strcmp(*(static_cast<String*>(op1)),*(static_cast<String*>(op2)));
|
|
|
|
ScriptRun* runner = cmp->m_runner->code()->createRunner(cmp->m_runner->context());
|
|
|
|
if (!runner)
|
|
|
|
return 0;
|
|
|
|
ObjList stack;
|
|
|
|
stack.append((static_cast<ExpOperation*>(op1))->clone());
|
|
|
|
stack.append((static_cast<ExpOperation*>(op2))->clone());
|
|
|
|
ScriptRun::Status rval = runner->call(cmp->m_name,stack);
|
|
|
|
int ret = 0;
|
|
|
|
if (ScriptRun::Succeeded == rval) {
|
2014-05-16 10:40:56 +00:00
|
|
|
ExpOperation* sret = static_cast<ExpOperation*>(ExpEvaluator::popOne(runner->stack()));
|
|
|
|
if (sret) {
|
|
|
|
ret = sret->toInteger();
|
|
|
|
TelEngine::destruct(sret);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cmp->m_failed = true;
|
2013-02-12 10:16:12 +00:00
|
|
|
}
|
2014-05-16 10:40:56 +00:00
|
|
|
else
|
|
|
|
cmp->m_failed = true;
|
2013-02-12 10:16:12 +00:00
|
|
|
TelEngine::destruct(runner);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-05-11 16:15:20 +00:00
|
|
|
bool JsArray::runNativeSort(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
2013-02-12 10:16:12 +00:00
|
|
|
ObjList arguments;
|
|
|
|
ExpOperation* op = 0;
|
|
|
|
if (extractArgs(this,stack,oper,context,arguments))
|
|
|
|
op = static_cast<ExpOperation*>(arguments[0]);
|
|
|
|
ScriptRun* runner = YOBJECT(ScriptRun,context);
|
|
|
|
if (op && !runner)
|
|
|
|
return false;
|
2014-05-19 15:01:29 +00:00
|
|
|
ObjList sorted;
|
|
|
|
ObjList* last = &sorted;
|
|
|
|
// Copy the arguments in a ObjList for sorting
|
|
|
|
for (ObjList* o = params().paramList()->skipNull(); o; o = o->skipNext()) {
|
|
|
|
NamedString* str = static_cast<NamedString*>(o->get());
|
|
|
|
if (str->name().toInteger(-1) > -1)
|
|
|
|
(last = last->append(str))->setDelete(false);
|
|
|
|
}
|
2013-02-12 10:16:12 +00:00
|
|
|
JsComparator* comp = op ? new JsComparator(op->name() ,runner) : 0;
|
|
|
|
sorted.sort(&compare,comp);
|
2014-05-16 10:40:56 +00:00
|
|
|
bool ok = comp ? !comp->m_failed : true;
|
2013-02-12 10:16:12 +00:00
|
|
|
delete comp;
|
2014-05-16 10:40:56 +00:00
|
|
|
if (ok) {
|
2014-05-19 15:01:29 +00:00
|
|
|
for (ObjList* o = params().paramList()->skipNull(); o;) {
|
|
|
|
NamedString* str = static_cast<NamedString*>(o->get());
|
|
|
|
if (str && str->name().toInteger(-1) > -1)
|
|
|
|
o->remove(false);
|
|
|
|
else
|
|
|
|
o = o->skipNext();
|
|
|
|
}
|
2014-05-16 10:40:56 +00:00
|
|
|
int i = 0;
|
2014-05-19 15:01:29 +00:00
|
|
|
last = params().paramList()->last();
|
|
|
|
for (ObjList* o = sorted.skipNull();o; o = o->skipNull()) {
|
|
|
|
ExpOperation* slice = static_cast<ExpOperation*>(o->remove(false));
|
|
|
|
const_cast<String&>(slice->name()) = i++;
|
|
|
|
last = last->append(slice);
|
2014-05-16 10:40:56 +00:00
|
|
|
}
|
2013-02-12 10:16:12 +00:00
|
|
|
}
|
2014-05-16 10:40:56 +00:00
|
|
|
return ok;
|
2012-05-02 15:11:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-28 16:10:41 +00:00
|
|
|
JsRegExp::JsRegExp(Mutex* mtx)
|
|
|
|
: JsObject("RegExp",mtx)
|
|
|
|
{
|
|
|
|
params().addParam(new ExpFunction("test"));
|
2016-02-23 14:00:18 +00:00
|
|
|
params().addParam(new ExpFunction("valid"));
|
2012-05-28 16:10:41 +00:00
|
|
|
}
|
|
|
|
|
2012-05-30 08:32:47 +00:00
|
|
|
JsRegExp::JsRegExp(Mutex* mtx, const char* name, const char* rexp, bool insensitive, bool extended, bool frozen)
|
2012-05-28 16:10:41 +00:00
|
|
|
: JsObject(mtx,name,frozen),
|
|
|
|
m_regexp(rexp,extended,insensitive)
|
|
|
|
{
|
2017-05-08 08:59:46 +00:00
|
|
|
XDebug(DebugAll,"JsRegExp::JsRegExp('%s',%p,%s) [%p]",
|
|
|
|
name,mtx,String::boolText(frozen),this);
|
2012-05-28 16:10:41 +00:00
|
|
|
params().addParam("ignoreCase",String::boolText(insensitive));
|
|
|
|
params().addParam("basicPosix",String::boolText(!extended));
|
|
|
|
}
|
|
|
|
|
2014-07-22 11:02:44 +00:00
|
|
|
JsRegExp::JsRegExp(Mutex* mtx, const Regexp& rexp, bool frozen)
|
2017-05-08 08:59:46 +00:00
|
|
|
: JsObject(mtx,rexp.c_str()),
|
2014-07-22 11:02:44 +00:00
|
|
|
m_regexp(rexp)
|
|
|
|
{
|
2017-05-08 08:59:46 +00:00
|
|
|
XDebug(DebugAll,"JsRegExp::JsRegExp('%s',%p,%s) [%p]",
|
|
|
|
toString().c_str(),mtx,String::boolText(frozen),this);
|
|
|
|
}
|
|
|
|
|
|
|
|
JsObject* JsRegExp::copy(Mutex* mtx) const
|
|
|
|
{
|
|
|
|
JsRegExp* reg = new JsRegExp(mtx,m_regexp,frozen());
|
|
|
|
deepCopyParams(reg->params(),params(),mtx);
|
|
|
|
return reg;
|
2014-07-22 11:02:44 +00:00
|
|
|
}
|
|
|
|
|
2012-05-28 16:10:41 +00:00
|
|
|
bool JsRegExp::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"JsRegExp::runNative() '%s' in '%s' [%p]",
|
|
|
|
oper.name().c_str(),toString().c_str(),this);
|
|
|
|
if (oper.name() == YSTRING("test")) {
|
|
|
|
if (oper.number() != 1)
|
|
|
|
return false;
|
|
|
|
ExpOperation* op = popValue(stack,context);
|
|
|
|
bool ok = op && regexp().matches(*op);
|
|
|
|
TelEngine::destruct(op);
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(ok));
|
|
|
|
}
|
2016-02-23 14:00:18 +00:00
|
|
|
else if (oper.name() == YSTRING("valid")) {
|
|
|
|
if (oper.number())
|
|
|
|
return false;
|
2016-03-04 10:24:01 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(regexp().compile()));
|
2016-02-23 14:00:18 +00:00
|
|
|
}
|
2012-05-28 16:10:41 +00:00
|
|
|
else
|
|
|
|
return JsObject::runNative(stack,oper,context);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-03-04 14:48:48 +00:00
|
|
|
bool JsRegExp::runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"JsRegExp::runAssign() '%s'='%s' (%s) in '%s' [%p]",
|
|
|
|
oper.name().c_str(),oper.c_str(),oper.typeOf(),toString().c_str(),this);
|
|
|
|
if (!JsObject::runAssign(stack,oper,context))
|
|
|
|
return false;
|
|
|
|
if (oper.name() == YSTRING("ignoreCase"))
|
|
|
|
regexp().setFlags(regexp().isExtended(),oper.toBoolean());
|
|
|
|
else if (oper.name() == YSTRING("basicPosix"))
|
|
|
|
regexp().setFlags(!oper.toBoolean(),regexp().isCaseInsensitive());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-04-18 13:37:49 +00:00
|
|
|
JsObject* JsRegExp::runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
ObjList args;
|
|
|
|
switch (extractArgs(stack,oper,context,args)) {
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
ExpOperation* pattern = static_cast<ExpOperation*>(args[0]);
|
|
|
|
ExpOperation* flags = static_cast<ExpOperation*>(args[1]);
|
|
|
|
if (!pattern)
|
|
|
|
return 0;
|
|
|
|
bool insensitive = false;
|
|
|
|
bool extended = true;
|
2016-02-25 14:28:10 +00:00
|
|
|
if (flags && *flags) {
|
2014-04-18 13:37:49 +00:00
|
|
|
const char* f = *flags;
|
|
|
|
char c = *f++;
|
|
|
|
while (c) {
|
|
|
|
switch (c) {
|
|
|
|
case 'i':
|
|
|
|
c = *f++;
|
|
|
|
insensitive = true;
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
c = *f++;
|
|
|
|
extended = false;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
c = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!ref())
|
|
|
|
return 0;
|
|
|
|
JsRegExp* obj = new JsRegExp(mutex(),*pattern,*pattern,insensitive,extended);
|
|
|
|
obj->params().addParam(new ExpWrapper(this,protoName()));
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2012-05-28 16:10:41 +00:00
|
|
|
|
2012-02-17 16:19:17 +00:00
|
|
|
bool JsMath::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
2012-05-14 19:51:36 +00:00
|
|
|
XDebug(DebugAll,"JsMath::runNative() '%s' in '%s' [%p]",
|
|
|
|
oper.name().c_str(),toString().c_str(),this);
|
2012-02-17 16:19:17 +00:00
|
|
|
if (oper.name() == YSTRING("abs")) {
|
|
|
|
if (!oper.number())
|
|
|
|
return false;
|
2013-08-07 09:49:52 +00:00
|
|
|
int64_t n = 0;
|
2013-08-07 11:24:10 +00:00
|
|
|
for (int i = (int)oper.number(); i; i--) {
|
2012-02-17 16:19:17 +00:00
|
|
|
ExpOperation* op = popValue(stack,context);
|
|
|
|
if (op->isInteger())
|
|
|
|
n = op->number();
|
|
|
|
TelEngine::destruct(op);
|
|
|
|
}
|
|
|
|
if (n < 0)
|
|
|
|
n = -n;
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(n));
|
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("max")) {
|
|
|
|
if (!oper.number())
|
|
|
|
return false;
|
2013-08-07 09:49:52 +00:00
|
|
|
int64_t n = LLONG_MIN;
|
2013-08-07 11:24:10 +00:00
|
|
|
for (int i = (int)oper.number(); i; i--) {
|
2012-02-17 16:19:17 +00:00
|
|
|
ExpOperation* op = popValue(stack,context);
|
|
|
|
if (op->isInteger() && op->number() > n)
|
|
|
|
n = op->number();
|
|
|
|
TelEngine::destruct(op);
|
|
|
|
}
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(n));
|
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("min")) {
|
|
|
|
if (!oper.number())
|
|
|
|
return false;
|
2013-08-07 09:49:52 +00:00
|
|
|
int64_t n = LLONG_MAX;
|
2013-08-07 11:24:10 +00:00
|
|
|
for (int i = (int)oper.number(); i; i--) {
|
2012-02-17 16:19:17 +00:00
|
|
|
ExpOperation* op = popValue(stack,context);
|
|
|
|
if (op->isInteger() && op->number() < n)
|
|
|
|
n = op->number();
|
|
|
|
TelEngine::destruct(op);
|
|
|
|
}
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(n));
|
|
|
|
}
|
2013-02-12 10:16:12 +00:00
|
|
|
else if (oper.name() == YSTRING("random")) {
|
|
|
|
long min = 0;
|
|
|
|
long max = LONG_MAX;
|
|
|
|
ObjList args;
|
|
|
|
if (extractArgs(stack,oper,context,args)) {
|
|
|
|
if (args.skipNull()) {
|
|
|
|
const String* mins = static_cast<String*>(args[0]);
|
|
|
|
if (mins)
|
|
|
|
min = mins->toLong(0);
|
|
|
|
}
|
|
|
|
if (args.count() >= 2) {
|
|
|
|
const String* maxs = static_cast<String*>(args[1]);
|
|
|
|
if (maxs)
|
|
|
|
max = maxs->toLong(max);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (min < 0 || max < 0 || min >= max)
|
|
|
|
return false;
|
2016-03-07 12:05:52 +00:00
|
|
|
int64_t rand = (max > (min + 1)) ? (Random::random() % (max - min)) : 0;
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(rand + min));
|
2013-02-12 10:16:12 +00:00
|
|
|
}
|
2012-02-17 16:19:17 +00:00
|
|
|
else
|
|
|
|
return JsObject::runNative(stack,oper,context);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-04-17 15:42:40 +00:00
|
|
|
JsObject* JsDate::runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
2016-01-20 10:23:35 +00:00
|
|
|
XDebug(DebugAll,"JsDate::runConstructor '%s'(" FMT64 ")",oper.name().c_str(),oper.number());
|
2013-04-17 15:42:40 +00:00
|
|
|
ObjList args;
|
|
|
|
JsObject* obj = 0;
|
|
|
|
switch (extractArgs(stack,oper,context,args)) {
|
|
|
|
case 0:
|
|
|
|
obj = new JsDate(mutex(),Time::msecNow());
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
ExpOperation* val = static_cast<ExpOperation*>(args[0]);
|
2019-11-08 11:24:35 +00:00
|
|
|
if (val) {
|
|
|
|
if (val->isInteger())
|
|
|
|
obj = new JsDate(mutex(),val->number());
|
|
|
|
else {
|
|
|
|
// Check string
|
|
|
|
uint64_t n = Time::toEpoch(*val,val->length(),1);
|
|
|
|
if (n == (uint64_t)-1)
|
|
|
|
return JsParser::nullObject();
|
|
|
|
obj = new JsDate(mutex(),n);
|
|
|
|
}
|
|
|
|
}
|
2013-04-17 15:42:40 +00:00
|
|
|
}
|
|
|
|
break;
|
2017-06-07 14:17:15 +00:00
|
|
|
case 2:
|
2013-04-17 15:42:40 +00:00
|
|
|
case 3:
|
2017-06-07 14:17:15 +00:00
|
|
|
case 4:
|
|
|
|
case 5:
|
2013-04-17 15:42:40 +00:00
|
|
|
case 6:
|
|
|
|
case 7:
|
|
|
|
{
|
|
|
|
unsigned int parts[7];
|
|
|
|
for (int i = 0; i < 7; i++) {
|
2017-06-07 14:17:15 +00:00
|
|
|
parts[i] = (2 == i) ? 1 : 0;
|
2013-04-17 15:42:40 +00:00
|
|
|
ExpOperation* val = static_cast<ExpOperation*>(args[i]);
|
|
|
|
if (val) {
|
|
|
|
if (val->isInteger())
|
2013-08-07 11:24:10 +00:00
|
|
|
parts[i] = (int)val->number();
|
2013-04-17 15:42:40 +00:00
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2017-06-07 14:17:15 +00:00
|
|
|
// Date components use local time, year can be 0-99, month starts from 0
|
|
|
|
if (parts[0] < 100)
|
|
|
|
parts[0] += 1900;
|
|
|
|
parts[1]++;
|
2013-04-17 15:42:40 +00:00
|
|
|
u_int64_t time = Time::toEpoch(parts[0],parts[1],parts[2],parts[3],parts[4],parts[5]);
|
|
|
|
obj = new JsDate(mutex(),1000 * time + parts[6],true);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (obj && ref())
|
|
|
|
obj->params().addParam(new ExpWrapper(this,protoName()));
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2012-02-17 16:19:17 +00:00
|
|
|
bool JsDate::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
2012-05-14 19:51:36 +00:00
|
|
|
XDebug(DebugAll,"JsDate::runNative() '%s' in '%s' [%p]",
|
|
|
|
oper.name().c_str(),toString().c_str(),this);
|
2012-05-11 16:15:20 +00:00
|
|
|
if (oper.name() == YSTRING("now")) {
|
|
|
|
// Returns the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC.
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)Time::msecNow()));
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
2017-06-07 14:17:15 +00:00
|
|
|
else if (oper.name() == YSTRING("UTC")) {
|
|
|
|
ObjList args;
|
|
|
|
switch (extractArgs(stack,oper,context,args)) {
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
case 5:
|
|
|
|
case 6:
|
|
|
|
case 7:
|
|
|
|
{
|
|
|
|
unsigned int parts[7];
|
|
|
|
for (int i = 0; i < 7; i++) {
|
|
|
|
parts[i] = (2 == i) ? 1 : 0;
|
|
|
|
ExpOperation* val = static_cast<ExpOperation*>(args[i]);
|
|
|
|
if (val) {
|
|
|
|
if (val->isInteger())
|
|
|
|
parts[i] = (int)val->number();
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Year can be 0-99, month starts from 0
|
|
|
|
if (parts[0] < 100)
|
|
|
|
parts[0] += 1900;
|
|
|
|
parts[1]++;
|
|
|
|
unsigned int time = Time::toEpoch(parts[0],parts[1],parts[2],parts[3],parts[4],parts[5]);
|
|
|
|
if (time != (unsigned int)-1) {
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(1000 * (int64_t)time + parts[6]));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// fall through
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(ExpOperation::nonInteger(),"NaN"));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2012-05-11 16:15:20 +00:00
|
|
|
else if (oper.name() == YSTRING("getDate")) {
|
|
|
|
// Returns the day of the month for the specified date according to local time.
|
|
|
|
// The value returned by getDate is an integer between 1 and 31.
|
|
|
|
int year = 0;
|
|
|
|
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
|
2013-04-17 15:42:40 +00:00
|
|
|
if (Time::toDateTime(m_time + m_offs,year,month,day,hour,minute,sec))
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)day));
|
2012-05-11 16:15:20 +00:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getDay")) {
|
|
|
|
// Get the day of the week for the date (0 is Sunday and returns values 0-6)
|
2013-04-17 15:42:40 +00:00
|
|
|
int year = 0;
|
|
|
|
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0, wday = 0;
|
|
|
|
if (Time::toDateTime(m_time + m_offs,year,month,day,hour,minute,sec,&wday))
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)wday));
|
2013-04-17 15:42:40 +00:00
|
|
|
else
|
|
|
|
return false;
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getFullYear")) {
|
|
|
|
// Returns the year of the specified date according to local time.
|
|
|
|
int year = 0;
|
|
|
|
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
|
2013-04-17 15:42:40 +00:00
|
|
|
if (Time::toDateTime(m_time + m_offs,year,month,day,hour,minute,sec))
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)year));
|
2012-05-11 16:15:20 +00:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getHours")) {
|
|
|
|
// Returns the hour ( 0 - 23) of the specified date according to local time.
|
|
|
|
int year = 0;
|
|
|
|
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
|
2013-04-17 15:42:40 +00:00
|
|
|
if (Time::toDateTime(m_time + m_offs,year,month,day,hour,minute,sec))
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)hour));
|
2012-05-11 16:15:20 +00:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getMilliseconds")) {
|
2013-04-17 15:42:40 +00:00
|
|
|
// Returns just the milliseconds part ( 0 - 999 )
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)m_msec));
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getMinutes")) {
|
|
|
|
// Returns the minute ( 0 - 59 ) of the specified date according to local time.
|
|
|
|
int year = 0;
|
|
|
|
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
|
2013-04-17 15:42:40 +00:00
|
|
|
if (Time::toDateTime(m_time + m_offs,year,month,day,hour,minute,sec))
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)minute));
|
2012-05-11 16:15:20 +00:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getMonth")) {
|
2013-04-17 15:42:40 +00:00
|
|
|
// Returns the month ( 0 - 11 ) of the specified date according to local time.
|
2012-05-11 16:15:20 +00:00
|
|
|
int year = 0;
|
|
|
|
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
|
2013-04-17 15:42:40 +00:00
|
|
|
if (Time::toDateTime(m_time + m_offs,year,month,day,hour,minute,sec))
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)month - 1));
|
2012-05-11 16:15:20 +00:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getSeconds")) {
|
|
|
|
// Returns the second ( 0 - 59 ) of the specified date according to local time.
|
|
|
|
int year = 0;
|
|
|
|
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
|
2013-04-17 15:42:40 +00:00
|
|
|
if (Time::toDateTime(m_time + m_offs,year,month,day,hour,minute,sec))
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)sec));
|
2012-05-11 16:15:20 +00:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getTime")) {
|
2013-04-17 15:42:40 +00:00
|
|
|
// Returns the time in milliseconds since UNIX Epoch
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(1000 * ((int64_t)m_time) + (int64_t)m_msec));
|
2013-04-17 15:42:40 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getTimezoneOffset")) {
|
|
|
|
// Returns the UTC to local difference in minutes, positive goes west
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)(m_offs / -60)));
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getUTCDate")) {
|
2013-04-17 15:42:40 +00:00
|
|
|
// Returns the day of the month for the specified date according to local time.
|
|
|
|
// The value returned by getDate is an integer between 1 and 31.
|
|
|
|
int year = 0;
|
|
|
|
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
|
|
|
|
if (Time::toDateTime(m_time,year,month,day,hour,minute,sec))
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)day));
|
2013-04-17 15:42:40 +00:00
|
|
|
else
|
|
|
|
return false;
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getUTCDay")) {
|
2013-04-17 15:42:40 +00:00
|
|
|
// Get the day of the week for the date (0 is Sunday and returns values 0-6)
|
|
|
|
int year = 0;
|
|
|
|
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0, wday = 0;
|
|
|
|
if (Time::toDateTime(m_time,year,month,day,hour,minute,sec,&wday))
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)wday));
|
2013-04-17 15:42:40 +00:00
|
|
|
else
|
|
|
|
return false;
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getUTCFullYear")) {
|
2013-04-17 15:42:40 +00:00
|
|
|
// Returns the year of the specified date according to local time.
|
|
|
|
int year = 0;
|
|
|
|
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
|
|
|
|
if (Time::toDateTime(m_time,year,month,day,hour,minute,sec))
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)year));
|
2013-04-17 15:42:40 +00:00
|
|
|
else
|
|
|
|
return false;
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getUTCHours")) {
|
2013-04-17 15:42:40 +00:00
|
|
|
// Returns the hour ( 0 - 23) of the specified date according to local time.
|
|
|
|
int year = 0;
|
|
|
|
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
|
|
|
|
if (Time::toDateTime(m_time,year,month,day,hour,minute,sec))
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)hour));
|
2013-04-17 15:42:40 +00:00
|
|
|
else
|
|
|
|
return false;
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getUTCMilliseconds")) {
|
2013-04-17 15:42:40 +00:00
|
|
|
// Returns just the milliseconds part ( 0 - 999 )
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)m_msec));
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getUTCMinutes")) {
|
2013-04-17 15:42:40 +00:00
|
|
|
// Returns the minute ( 0 - 59 ) of the specified date according to local time.
|
|
|
|
int year = 0;
|
|
|
|
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
|
|
|
|
if (Time::toDateTime(m_time,year,month,day,hour,minute,sec))
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)minute));
|
2013-04-17 15:42:40 +00:00
|
|
|
else
|
|
|
|
return false;
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getUTCMonth")) {
|
2013-04-17 15:42:40 +00:00
|
|
|
// Returns the month ( 0 - 11 ) of the specified date according to local time.
|
|
|
|
int year = 0;
|
|
|
|
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
|
|
|
|
if (Time::toDateTime(m_time,year,month,day,hour,minute,sec))
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)month - 1));
|
2013-04-17 15:42:40 +00:00
|
|
|
else
|
|
|
|
return false;
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("getUTCSeconds")) {
|
2013-04-17 15:42:40 +00:00
|
|
|
// Returns the second ( 0 - 59 ) of the specified date according to local time.
|
|
|
|
int year = 0;
|
|
|
|
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
|
|
|
|
if (Time::toDateTime(m_time,year,month,day,hour,minute,sec))
|
2013-08-07 09:49:52 +00:00
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)sec));
|
2013-04-17 15:42:40 +00:00
|
|
|
else
|
|
|
|
return false;
|
2012-05-11 16:15:20 +00:00
|
|
|
}
|
2019-11-08 11:24:35 +00:00
|
|
|
else if (oper.name() == YSTRING("toJSON")) {
|
|
|
|
if (toString().null())
|
|
|
|
return false;
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(toString()));
|
|
|
|
}
|
2012-05-11 16:15:20 +00:00
|
|
|
else
|
|
|
|
return JsObject::runNative(stack,oper,context);
|
|
|
|
return true;
|
2012-02-17 16:19:17 +00:00
|
|
|
}
|
|
|
|
|
2012-02-10 14:53:55 +00:00
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|