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
|
|
|
|
* Copyright (C) 2011 Null Team
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "yatescript.h"
|
|
|
|
|
|
|
|
using namespace TelEngine;
|
|
|
|
|
|
|
|
namespace { // anonymous
|
|
|
|
|
|
|
|
// Base class for all native objects that hold a NamedList
|
|
|
|
class JsNative : public JsObject
|
|
|
|
{
|
|
|
|
YCLASS(JsNative,JsObject)
|
|
|
|
public:
|
2012-02-17 16:19:17 +00:00
|
|
|
inline JsNative(const char* name, NamedList* list, Mutex* mtx)
|
|
|
|
: JsObject(name,mtx), m_list(list)
|
2012-02-10 14:53:55 +00:00
|
|
|
{ }
|
|
|
|
virtual NamedList& list()
|
|
|
|
{ return *m_list; }
|
|
|
|
virtual const NamedList& list() const
|
|
|
|
{ return *m_list; }
|
|
|
|
private:
|
|
|
|
NamedList* m_list;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Array object
|
|
|
|
class JsArray : public JsObject
|
|
|
|
{
|
|
|
|
YCLASS(JsArray,JsObject)
|
|
|
|
public:
|
2012-02-17 16:19:17 +00:00
|
|
|
inline JsArray(Mutex* mtx)
|
|
|
|
: JsObject("Array",mtx)
|
2012-05-02 15:11:48 +00:00
|
|
|
{
|
|
|
|
params().addParam(new ExpFunction("push"));
|
|
|
|
}
|
|
|
|
protected:
|
|
|
|
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
|
2012-02-10 14:53:55 +00:00
|
|
|
};
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
params().addParam(new ExpFunction("constructor"));
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
: JsObject("Date",mtx,true)
|
2012-02-10 14:53:55 +00:00
|
|
|
{
|
2012-02-17 16:19:17 +00:00
|
|
|
params().addParam(new ExpFunction("now"));
|
|
|
|
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"));
|
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
|
|
|
};
|
|
|
|
|
|
|
|
// 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"));
|
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
|
|
|
|
|
|
|
|
|
|
|
|
// Helper function that adds an object to a parent
|
2012-02-17 16:19:17 +00:00
|
|
|
static inline void addObject(NamedList& params, const char* name, JsObject* obj)
|
2012-02-10 14:53:55 +00:00
|
|
|
{
|
|
|
|
params.addParam(new NamedPointer(name,obj,obj->toString()));
|
|
|
|
}
|
|
|
|
|
2012-02-17 16:19:17 +00:00
|
|
|
JsObject::JsObject(const char* name, Mutex* mtx, bool frozen)
|
|
|
|
: ScriptContext(String("[Object ") + name + "]"),
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
JsObject::~JsObject()
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"JsObject::~JsObject '%s' [%p]",toString().c_str(),this);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
NamedString* param = params().getParam(oper.name());
|
|
|
|
if (!param)
|
|
|
|
return false;
|
|
|
|
ExpFunction* ef = YOBJECT(ExpFunction,param);
|
|
|
|
if (ef)
|
|
|
|
return runNative(stack,oper,context);
|
|
|
|
JsFunction* jf = YOBJECT(JsFunction,param);
|
|
|
|
if (jf)
|
|
|
|
return jf->runDefined(stack,oper,context);
|
|
|
|
JsObject* jso = YOBJECT(JsObject,param);
|
|
|
|
if (jso) {
|
|
|
|
ExpFunction op("constructor",oper.number());
|
|
|
|
return jso->runFunction(stack,op,context);
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
const String* param = params().getParam(oper.name());
|
|
|
|
if (param) {
|
|
|
|
ExpFunction* ef = YOBJECT(ExpFunction,param);
|
|
|
|
if (ef)
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpFunction(oper.name(),ef->number()));
|
|
|
|
else {
|
|
|
|
JsFunction* jf = YOBJECT(JsFunction,param);
|
|
|
|
if (jf)
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpFunction(oper.name()));
|
|
|
|
else {
|
|
|
|
JsObject* jo = YOBJECT(JsObject,param);
|
|
|
|
if (jo) {
|
|
|
|
jo->ref();
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpWrapper(jo,oper.name()));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(*param,oper.name(),true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpWrapper(0,oper.name()));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JsObject::runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"JsObject::runAssign() '%s'='%s' in '%s' [%p]",
|
|
|
|
oper.name().c_str(),oper.c_str(),toString().c_str(),this);
|
|
|
|
if (frozen()) {
|
|
|
|
Debug(DebugNote,"Object '%s' is frozen",toString().c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ExpFunction* ef = YOBJECT(ExpFunction,&oper);
|
|
|
|
if (ef)
|
|
|
|
params().setParam(new ExpFunction(oper.name(),oper.number()));
|
|
|
|
else {
|
|
|
|
ExpWrapper* w = YOBJECT(ExpWrapper,&oper);
|
|
|
|
if (w) {
|
|
|
|
GenObject* o = w->object();
|
|
|
|
RefObject* r = YOBJECT(RefObject,o);
|
|
|
|
if (r)
|
|
|
|
r->ref();
|
|
|
|
if (o)
|
|
|
|
params().setParam(new NamedPointer(oper.name(),o,o->toString()));
|
|
|
|
else
|
|
|
|
params().clearParam(oper.name());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
params().setParam(oper.name(),oper);
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
for (long int i = oper.number(); i; i--) {
|
|
|
|
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;
|
|
|
|
bool ok = runField(stack,*oper,context);
|
|
|
|
TelEngine::destruct(oper);
|
|
|
|
return ok ? ExpEvaluator::popOne(stack) : 0;
|
|
|
|
}
|
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();
|
|
|
|
static_cast<String&>(p) = "[Object Global]";
|
|
|
|
if (!p.getParam(YSTRING("Object")))
|
|
|
|
addObject(p,"Object",new JsObjectObj(mtx));
|
|
|
|
if (!p.getParam(YSTRING("Function")))
|
|
|
|
addObject(p,"Function",new JsFunction(mtx));
|
2012-03-05 09:53:31 +00:00
|
|
|
if (!p.getParam(YSTRING("Array")))
|
|
|
|
addObject(p,"Array",new JsArray(mtx));
|
2012-02-17 16:19:17 +00:00
|
|
|
if (!p.getParam(YSTRING("Date")))
|
|
|
|
addObject(p,"Date",new JsDate(mtx));
|
|
|
|
if (!p.getParam(YSTRING("Math")))
|
|
|
|
addObject(p,"Math",new JsMath(mtx));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool JsObjectObj::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
if (oper.name() == YSTRING("constructor"))
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpWrapper(new JsObject("Object",mutex())));
|
|
|
|
else
|
|
|
|
return JsObject::runNative(stack,oper,context);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-05-02 15:11:48 +00:00
|
|
|
|
|
|
|
bool JsArray::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
return JsObject::runNative(stack,oper,context);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-17 16:19:17 +00:00
|
|
|
bool JsMath::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
if (oper.name() == YSTRING("abs")) {
|
|
|
|
if (!oper.number())
|
|
|
|
return false;
|
|
|
|
long int n = 0;
|
|
|
|
for (long int i = oper.number(); i; i--) {
|
|
|
|
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;
|
|
|
|
long int n = LONG_MIN;
|
|
|
|
for (long int i = oper.number(); i; i--) {
|
|
|
|
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;
|
|
|
|
long int n = LONG_MAX;
|
|
|
|
for (long int i = oper.number(); i; i--) {
|
|
|
|
ExpOperation* op = popValue(stack,context);
|
|
|
|
if (op->isInteger() && op->number() < n)
|
|
|
|
n = op->number();
|
|
|
|
TelEngine::destruct(op);
|
|
|
|
}
|
|
|
|
ExpEvaluator::pushOne(stack,new ExpOperation(n));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return JsObject::runNative(stack,oper,context);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool JsDate::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
return JsObject::runNative(stack,oper,context);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-02 15:11:48 +00:00
|
|
|
JsFunction::JsFunction(Mutex* mtx)
|
|
|
|
: JsObject("Function",mtx,true)
|
|
|
|
{
|
|
|
|
params().addParam(new ExpFunction("apply"));
|
|
|
|
params().addParam(new ExpFunction("call"));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JsFunction::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
if (oper.name() == YSTRING("apply")) {
|
|
|
|
// func.apply(new_this,["array","of","params",...])
|
|
|
|
if (oper.number() != 2)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (oper.name() == YSTRING("call")) {
|
|
|
|
// func.call(new_this,param1,param2,...)
|
|
|
|
if (!oper.number())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return JsObject::runNative(stack,oper,context);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-02-17 16:19:17 +00:00
|
|
|
bool JsFunction::runDefined(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
|
|
|
{
|
|
|
|
return false;
|
2012-02-10 14:53:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|