Added partial universal scripting implementation with Javascript support.
git-svn-id: http://voip.null.ro/svn/yate@4892 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
7165d33d25
commit
b15e963c58
|
@ -0,0 +1 @@
|
||||||
|
[general]
|
|
@ -1417,6 +1417,7 @@ AC_CONFIG_FILES([packing/rpm/yate.spec
|
||||||
libs/yiax/Makefile
|
libs/yiax/Makefile
|
||||||
libs/yxml/Makefile
|
libs/yxml/Makefile
|
||||||
libs/yjabber/Makefile
|
libs/yjabber/Makefile
|
||||||
|
libs/yscript/Makefile
|
||||||
libs/ymgcp/Makefile
|
libs/ymgcp/Makefile
|
||||||
libs/ysig/Makefile
|
libs/ysig/Makefile
|
||||||
libs/ypbx/Makefile
|
libs/ypbx/Makefile
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
# Makefile
|
||||||
|
# This file holds the make rules for the libyatescript
|
||||||
|
|
||||||
|
DEBUG :=
|
||||||
|
|
||||||
|
CXX := @CXX@ -Wall
|
||||||
|
AR := ar
|
||||||
|
DEFS :=
|
||||||
|
INCLUDES := -I@top_srcdir@ -I../.. -I@srcdir@
|
||||||
|
CFLAGS := @CFLAGS@ @MODULE_CPPFLAGS@ @INLINE_FLAGS@
|
||||||
|
LDFLAGS:= @LDFLAGS@
|
||||||
|
SONAME_OPT := @SONAME_OPT@
|
||||||
|
YATELIBS := -L../.. -lyate @LIBS@
|
||||||
|
INCFILES := @top_srcdir@/yateclass.h @srcdir@/yatescript.h
|
||||||
|
|
||||||
|
PROGS=
|
||||||
|
LIBS = libyatescript.a
|
||||||
|
OBJS = evaluator.o script.o javascript.o jsobjects.o
|
||||||
|
LIBD_DEV:= libyatescript.so
|
||||||
|
LIBD_VER:= $(LIBD_DEV).@PACKAGE_VERSION@
|
||||||
|
LIBD:= ../../$(LIBD_VER) ../../$(LIBD_DEV)
|
||||||
|
|
||||||
|
LOCALFLAGS =
|
||||||
|
LOCALLIBS =
|
||||||
|
COMPILE = $(CXX) $(DEFS) $(DEBUG) $(INCLUDES) $(CFLAGS)
|
||||||
|
LINK = $(CXX) $(LDFLAGS)
|
||||||
|
|
||||||
|
prefix = @prefix@
|
||||||
|
exec_prefix = @exec_prefix@
|
||||||
|
|
||||||
|
# include optional local make rules
|
||||||
|
-include YateLocal.mak
|
||||||
|
|
||||||
|
.PHONY: all debug ddebug xdebug
|
||||||
|
all: $(LIBS) $(LIBD) $(PROGS)
|
||||||
|
|
||||||
|
debug:
|
||||||
|
$(MAKE) all DEBUG=-g3 MODSTRIP=
|
||||||
|
|
||||||
|
ddebug:
|
||||||
|
$(MAKE) all DEBUG='-g3 -DDEBUG' MODSTRIP=
|
||||||
|
|
||||||
|
xdebug:
|
||||||
|
$(MAKE) all DEBUG='-g3 -DXDEBUG' MODSTRIP=
|
||||||
|
|
||||||
|
.PHONY: strip
|
||||||
|
strip: all
|
||||||
|
strip --strip-debug --discard-locals $(PROGS)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
@-$(RM) $(PROGS) $(LIBS) $(LIBD) $(OBJS) core 2>/dev/null
|
||||||
|
|
||||||
|
%.o: @srcdir@/%.cpp $(INCFILES)
|
||||||
|
$(COMPILE) -c $<
|
||||||
|
|
||||||
|
Makefile: @srcdir@/Makefile.in ../../config.status
|
||||||
|
cd ../.. && ./config.status
|
||||||
|
|
||||||
|
../../$(LIBD_VER): $(OBJS)
|
||||||
|
$(LINK) -o $@ $(SONAME_OPT)$(LIBD_VER) $^ $(YATELIBS)
|
||||||
|
|
||||||
|
../../$(LIBD_DEV): ../../$(LIBD_VER)
|
||||||
|
cd ../.. && ln -sf $(LIBD_VER) $(LIBD_DEV)
|
||||||
|
|
||||||
|
$(LIBS): $(OBJS)
|
||||||
|
$(AR) rcs $@ $^
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,434 @@
|
||||||
|
/**
|
||||||
|
* javascript.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
|
||||||
|
|
||||||
|
class JsContext : public ScriptContext
|
||||||
|
{
|
||||||
|
YCLASS(JsContext,ScriptContext)
|
||||||
|
public:
|
||||||
|
virtual bool runFunction(const ExpEvaluator* eval, ObjList& stack, const ExpOperation& oper, void* context);
|
||||||
|
virtual bool runField(const ExpEvaluator* eval, ObjList& stack, const ExpOperation& oper, void* context);
|
||||||
|
virtual bool runAssign(const ExpEvaluator* eval, const ExpOperation& oper, void* context);
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsCode : public ScriptCode, public ExpEvaluator
|
||||||
|
{
|
||||||
|
YCLASS(JsCode,ScriptCode)
|
||||||
|
public:
|
||||||
|
enum JsOpcode {
|
||||||
|
OpcBegin = OpcPrivate + 1,
|
||||||
|
OpcEnd,
|
||||||
|
OpcIndex,
|
||||||
|
OpcNew,
|
||||||
|
OpcFor,
|
||||||
|
OpcWhile,
|
||||||
|
OpcIf,
|
||||||
|
OpcElse,
|
||||||
|
OpcSwitch,
|
||||||
|
OpcCase,
|
||||||
|
OpcBreak,
|
||||||
|
OpcCont,
|
||||||
|
OpcIn,
|
||||||
|
OpcVar,
|
||||||
|
OpcWith,
|
||||||
|
OpcTry,
|
||||||
|
OpcCatch,
|
||||||
|
OpcFinally,
|
||||||
|
OpcThrow,
|
||||||
|
OpcReturn,
|
||||||
|
};
|
||||||
|
inline JsCode()
|
||||||
|
: ExpEvaluator(C), m_label(0)
|
||||||
|
{ debugName("JsCode"); }
|
||||||
|
virtual bool initialize(ScriptContext* context) const;
|
||||||
|
virtual bool evaluate(ScriptContext& context, ObjList& results) const;
|
||||||
|
protected:
|
||||||
|
virtual bool keywordChar(char c) const;
|
||||||
|
virtual int getKeyword(const char* str) const;
|
||||||
|
virtual bool getInstruction(const char*& expr);
|
||||||
|
virtual Opcode getOperator(const char*& expr);
|
||||||
|
virtual Opcode getUnaryOperator(const char*& expr);
|
||||||
|
virtual Opcode getPostfixOperator(const char*& expr);
|
||||||
|
virtual const char* getOperator(Opcode oper) const;
|
||||||
|
virtual int getPrecedence(ExpEvaluator::Opcode oper) const;
|
||||||
|
virtual bool getSeparator(const char*& expr, bool remove);
|
||||||
|
virtual bool runOperation(ObjList& stack, const ExpOperation& oper, void* context) const;
|
||||||
|
private:
|
||||||
|
int m_label;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAKEOP(s,o) { s, JsCode::Opc ## o }
|
||||||
|
static const TokenDict s_operators[] =
|
||||||
|
{
|
||||||
|
{ 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const TokenDict s_unaryOps[] =
|
||||||
|
{
|
||||||
|
MAKEOP("new", New),
|
||||||
|
{ 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const TokenDict s_postfixOps[] =
|
||||||
|
{
|
||||||
|
MAKEOP("++", IncPost),
|
||||||
|
MAKEOP("--", DecPost),
|
||||||
|
{ 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const TokenDict s_instr[] =
|
||||||
|
{
|
||||||
|
MAKEOP("function", Func),
|
||||||
|
MAKEOP("for", For),
|
||||||
|
MAKEOP("while", While),
|
||||||
|
MAKEOP("if", If),
|
||||||
|
MAKEOP("else", Else),
|
||||||
|
MAKEOP("switch", Switch),
|
||||||
|
MAKEOP("case", Case),
|
||||||
|
MAKEOP("break", Break),
|
||||||
|
MAKEOP("continue", Cont),
|
||||||
|
MAKEOP("in", In),
|
||||||
|
MAKEOP("var", Var),
|
||||||
|
MAKEOP("with", With),
|
||||||
|
MAKEOP("try", Try),
|
||||||
|
MAKEOP("catch", Catch),
|
||||||
|
MAKEOP("finally", Finally),
|
||||||
|
MAKEOP("throw", Throw),
|
||||||
|
MAKEOP("return", Return),
|
||||||
|
{ 0, 0 }
|
||||||
|
};
|
||||||
|
#undef MAKEOP
|
||||||
|
|
||||||
|
|
||||||
|
bool JsContext::runFunction(const ExpEvaluator* eval, ObjList& stack, const ExpOperation& oper, void* context)
|
||||||
|
{
|
||||||
|
return ScriptContext::runFunction(eval,stack,oper,context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JsContext::runField(const ExpEvaluator* eval, ObjList& stack, const ExpOperation& oper, void* context)
|
||||||
|
{
|
||||||
|
if (!eval)
|
||||||
|
return false;
|
||||||
|
XDebug(DebugAll,"JsContext::runField '%s'",oper.name().c_str());
|
||||||
|
return ScriptContext::runField(eval,stack,oper,context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JsContext::runAssign(const ExpEvaluator* eval, const ExpOperation& oper, void* context)
|
||||||
|
{
|
||||||
|
if (!eval)
|
||||||
|
return false;
|
||||||
|
XDebug(DebugAll,"JsContext::runAssign '%s'='%s'",oper.name().c_str(),oper.c_str());
|
||||||
|
return ScriptContext::runAssign(eval,oper,context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize standard globals in the execution context
|
||||||
|
bool JsCode::initialize(ScriptContext* context) const
|
||||||
|
{
|
||||||
|
if (!context)
|
||||||
|
return false;
|
||||||
|
JsObject::initialize(*context);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JsCode::evaluate(ScriptContext& context, ObjList& results) const
|
||||||
|
{
|
||||||
|
if (null())
|
||||||
|
return false;
|
||||||
|
return ExpEvaluator::evaluate(results,&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JsCode::keywordChar(char c) const
|
||||||
|
{
|
||||||
|
return ExpEvaluator::keywordChar(c) || (c == '$');
|
||||||
|
}
|
||||||
|
|
||||||
|
int JsCode::getKeyword(const char* str) const
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
for (;; len++) {
|
||||||
|
char c = *str++;
|
||||||
|
if (c <= ' ')
|
||||||
|
break;
|
||||||
|
if (keywordChar(c) || (len && (c == '.')))
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (len > 1 && (str[-2] == '.'))
|
||||||
|
len--;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JsCode::getInstruction(const char*& expr)
|
||||||
|
{
|
||||||
|
XDebug(this,DebugAll,"JsCode::getInstruction '%s'",expr);
|
||||||
|
if (skipWhites(expr) == '{') {
|
||||||
|
if (!runCompile(++expr,'}'))
|
||||||
|
return false;
|
||||||
|
if (skipWhites(expr) != '}')
|
||||||
|
return gotError("Expecting '}'",expr);
|
||||||
|
expr++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Opcode op = ExpEvaluator::getOperator(expr,s_instr);
|
||||||
|
switch ((JsOpcode)op) {
|
||||||
|
case (JsOpcode)OpcNone:
|
||||||
|
return false;
|
||||||
|
case OpcThrow:
|
||||||
|
if (!runCompile(expr))
|
||||||
|
return false;
|
||||||
|
addOpcode(op);
|
||||||
|
break;
|
||||||
|
case OpcReturn:
|
||||||
|
runCompile(expr);
|
||||||
|
addOpcode(op);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpEvaluator::Opcode JsCode::getOperator(const char*& expr)
|
||||||
|
{
|
||||||
|
XDebug(this,DebugAll,"JsCode::getOperator '%s'",expr);
|
||||||
|
Opcode op = ExpEvaluator::getOperator(expr,s_operators);
|
||||||
|
if (OpcNone != op)
|
||||||
|
return op;
|
||||||
|
return ExpEvaluator::getOperator(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpEvaluator::Opcode JsCode::getUnaryOperator(const char*& expr)
|
||||||
|
{
|
||||||
|
XDebug(this,DebugAll,"JsCode::getUnaryOperator '%s'",expr);
|
||||||
|
Opcode op = ExpEvaluator::getOperator(expr,s_unaryOps);
|
||||||
|
if (OpcNone != op)
|
||||||
|
return op;
|
||||||
|
return ExpEvaluator::getUnaryOperator(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpEvaluator::Opcode JsCode::getPostfixOperator(const char*& expr)
|
||||||
|
{
|
||||||
|
XDebug(this,DebugAll,"JsCode::getPostfixOperator '%s'",expr);
|
||||||
|
if (skipWhites(expr) == '[') {
|
||||||
|
if (!runCompile(++expr,']'))
|
||||||
|
return OpcNone;
|
||||||
|
if (skipWhites(expr) != ']') {
|
||||||
|
gotError("Expecting ']'",expr);
|
||||||
|
return OpcNone;
|
||||||
|
}
|
||||||
|
expr++;
|
||||||
|
return (Opcode)OpcIndex;
|
||||||
|
}
|
||||||
|
Opcode op = ExpEvaluator::getOperator(expr,s_postfixOps);
|
||||||
|
if (OpcNone != op)
|
||||||
|
return op;
|
||||||
|
return ExpEvaluator::getPostfixOperator(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* JsCode::getOperator(Opcode oper) const
|
||||||
|
{
|
||||||
|
if (oper < OpcPrivate)
|
||||||
|
return ExpEvaluator::getOperator(oper);
|
||||||
|
if ((int)oper == (int)OpcIndex)
|
||||||
|
return "[]";
|
||||||
|
const char* tmp = lookup(oper,s_operators);
|
||||||
|
if (!tmp) {
|
||||||
|
tmp = lookup(oper,s_unaryOps);
|
||||||
|
if (!tmp) {
|
||||||
|
tmp = lookup(oper,s_postfixOps);
|
||||||
|
if (!tmp)
|
||||||
|
tmp = lookup(oper,s_instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JsCode::getPrecedence(ExpEvaluator::Opcode oper) const
|
||||||
|
{
|
||||||
|
switch (oper) {
|
||||||
|
case OpcNew:
|
||||||
|
case OpcIndex:
|
||||||
|
return 12;
|
||||||
|
default:
|
||||||
|
return ExpEvaluator::getPrecedence(oper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JsCode::getSeparator(const char*& expr, bool remove)
|
||||||
|
{
|
||||||
|
switch (skipWhites(expr)) {
|
||||||
|
case ']':
|
||||||
|
case ';':
|
||||||
|
if (remove)
|
||||||
|
expr++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return ExpEvaluator::getSeparator(expr,remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, void* context) const
|
||||||
|
{
|
||||||
|
switch ((JsOpcode)oper.opcode()) {
|
||||||
|
case OpcBegin:
|
||||||
|
pushOne(stack,new ExpOperation(OpcBegin));
|
||||||
|
break;
|
||||||
|
case OpcEnd:
|
||||||
|
{
|
||||||
|
ExpOperation* op = popOne(stack);
|
||||||
|
ObjList* b = 0;
|
||||||
|
for (ObjList* l = stack.skipNull(); l; l=l->skipNext()) {
|
||||||
|
ExpOperation* o = static_cast<ExpOperation*>(l->get());
|
||||||
|
if (o && (o->opcode() == (Opcode)OpcBegin))
|
||||||
|
b = l;
|
||||||
|
}
|
||||||
|
if (!b) {
|
||||||
|
TelEngine::destruct(op);
|
||||||
|
return gotError("ExpEvaluator stack underflow");
|
||||||
|
}
|
||||||
|
b->clear();
|
||||||
|
pushOne(stack,op);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OpcIndex:
|
||||||
|
{
|
||||||
|
ExpOperation* op2 = popValue(stack,context);
|
||||||
|
ExpOperation* op1 = popOne(stack);
|
||||||
|
if (!op1 || !op2) {
|
||||||
|
TelEngine::destruct(op1);
|
||||||
|
TelEngine::destruct(op2);
|
||||||
|
return gotError("Stack underflow");
|
||||||
|
}
|
||||||
|
if (op1->opcode() != OpcField) {
|
||||||
|
TelEngine::destruct(op1);
|
||||||
|
TelEngine::destruct(op2);
|
||||||
|
return gotError("Expecting field name");
|
||||||
|
}
|
||||||
|
pushOne(stack,new ExpOperation(OpcField,op1->name() + "." + *op2));
|
||||||
|
TelEngine::destruct(op1);
|
||||||
|
TelEngine::destruct(op2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OpcNew:
|
||||||
|
{
|
||||||
|
ExpOperation* op = popOne(stack);
|
||||||
|
if (!op)
|
||||||
|
return gotError("Stack underflow");
|
||||||
|
if (op->opcode() != OpcField) {
|
||||||
|
TelEngine::destruct(op);
|
||||||
|
return gotError("Expecting class name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OpcThrow:
|
||||||
|
{
|
||||||
|
ExpOperation* op = popOne(stack);
|
||||||
|
if (!op)
|
||||||
|
return gotError("Stack underflow");
|
||||||
|
bool ok = false;
|
||||||
|
while (ExpOperation* drop = popAny(stack)) {
|
||||||
|
JsOpcode c = (JsOpcode)drop->opcode();
|
||||||
|
TelEngine::destruct(drop);
|
||||||
|
if (c == OpcTry) {
|
||||||
|
ok = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ok)
|
||||||
|
return gotError("'try' not found");
|
||||||
|
pushOne(stack,op);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OpcReturn:
|
||||||
|
{
|
||||||
|
ExpOperation* op = popOne(stack);
|
||||||
|
bool ok = false;
|
||||||
|
while (ExpOperation* drop = popAny(stack)) {
|
||||||
|
ok = drop->opcode() == OpcFunc;
|
||||||
|
int n = drop->number();
|
||||||
|
TelEngine::destruct(drop);
|
||||||
|
if (ok) {
|
||||||
|
DDebug(this,DebugAll,"return popping %d off stack",n);
|
||||||
|
while (n-- > 0)
|
||||||
|
TelEngine::destruct(popAny(stack));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ok) {
|
||||||
|
TelEngine::destruct(op);
|
||||||
|
return gotError("Function not found on stack");
|
||||||
|
}
|
||||||
|
pushOne(stack,op);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ExpEvaluator::runOperation(stack,oper);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
|
// Parse a piece of Javascript text
|
||||||
|
bool JsParser::parse(const char* text)
|
||||||
|
{
|
||||||
|
if (TelEngine::null(text))
|
||||||
|
return false;
|
||||||
|
// TODO
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate a string as expression or statement
|
||||||
|
ScriptRun::Status JsParser::eval(const String& text, ExpOperation** result, ScriptContext* context)
|
||||||
|
{
|
||||||
|
if (TelEngine::null(text))
|
||||||
|
return ScriptRun::Invalid;
|
||||||
|
JsCode* code = new JsCode;
|
||||||
|
if (!code->compile(text)) {
|
||||||
|
TelEngine::destruct(code);
|
||||||
|
return ScriptRun::Invalid;
|
||||||
|
}
|
||||||
|
DDebug(DebugAll,"Compiled: %s",code->dump().c_str());
|
||||||
|
code->simplify();
|
||||||
|
DDebug(DebugAll,"Simplified: %s",code->dump().c_str());
|
||||||
|
ScriptContext* ctxt = 0;
|
||||||
|
if (!context)
|
||||||
|
context = ctxt = new JsContext();
|
||||||
|
ScriptRun* runner = new ScriptRun(code,context);
|
||||||
|
TelEngine::destruct(ctxt);
|
||||||
|
code->extender(runner->context());
|
||||||
|
TelEngine::destruct(code);
|
||||||
|
ScriptRun::Status rval = runner->run();
|
||||||
|
if (result && (ScriptRun::Succeeded == rval))
|
||||||
|
*result = ExpEvaluator::popOne(runner->stack());
|
||||||
|
TelEngine::destruct(runner);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,125 @@
|
||||||
|
/**
|
||||||
|
* 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:
|
||||||
|
inline JsNative(const char* name, NamedList* list)
|
||||||
|
: JsObject(name),
|
||||||
|
m_list(list)
|
||||||
|
{ }
|
||||||
|
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:
|
||||||
|
inline JsArray()
|
||||||
|
: JsObject("Array")
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function object
|
||||||
|
class JsFunction : public JsObject
|
||||||
|
{
|
||||||
|
YCLASS(JsFunction,JsObject)
|
||||||
|
public:
|
||||||
|
inline JsFunction()
|
||||||
|
: JsObject("Function")
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Object constructor
|
||||||
|
class JsConstructor : public JsFunction
|
||||||
|
{
|
||||||
|
YCLASS(JsConstructor,JsFunction)
|
||||||
|
public:
|
||||||
|
inline JsConstructor()
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Date object
|
||||||
|
class JsDate : public JsObject
|
||||||
|
{
|
||||||
|
YCLASS(JsDate,JsObject)
|
||||||
|
public:
|
||||||
|
inline JsDate()
|
||||||
|
: JsObject("Date")
|
||||||
|
{
|
||||||
|
addParam(new ExpOperation(ExpEvaluator::OpcFunc,"now"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Math class - not really an object, all methods are static
|
||||||
|
class JsMath : public JsObject
|
||||||
|
{
|
||||||
|
YCLASS(JsMath,JsObject)
|
||||||
|
public:
|
||||||
|
inline JsMath()
|
||||||
|
: JsObject("Math")
|
||||||
|
{
|
||||||
|
addParam(new ExpOperation(ExpEvaluator::OpcFunc,"abs"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
|
// Helper function that adds an object to a parent
|
||||||
|
static inline void addObject(NamedList& params, const char* name, NamedList* obj)
|
||||||
|
{
|
||||||
|
params.addParam(new NamedPointer(name,obj,obj->toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize standard globals in the execution context
|
||||||
|
void JsObject::initialize(ScriptContext& context)
|
||||||
|
{
|
||||||
|
NamedList& params = context.params();
|
||||||
|
static_cast<String&>(params) = "[Object Global]";
|
||||||
|
if (!params.getParam(YSTRING("Object")))
|
||||||
|
addObject(params,"Object",new JsObject);
|
||||||
|
if (!params.getParam(YSTRING("Function")))
|
||||||
|
addObject(params,"Function",new JsFunction);
|
||||||
|
if (!params.getParam(YSTRING("Date")))
|
||||||
|
addObject(params,"Date",new JsDate);
|
||||||
|
if (!params.getParam(YSTRING("Math")))
|
||||||
|
addObject(params,"Math",new JsMath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,159 @@
|
||||||
|
/**
|
||||||
|
* script.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;
|
||||||
|
|
||||||
|
ScriptParser::~ScriptParser()
|
||||||
|
{
|
||||||
|
TelEngine::destruct(m_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptParser::setCode(ScriptCode* code)
|
||||||
|
{
|
||||||
|
ScriptCode* tmp = m_code;
|
||||||
|
if (tmp == code)
|
||||||
|
return;
|
||||||
|
if (code)
|
||||||
|
code->ref();
|
||||||
|
m_code = code;
|
||||||
|
TelEngine::destruct(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ScriptContext::runFunction(const ExpEvaluator* eval, ObjList& stack, const ExpOperation& oper, void* context)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptContext::runField(const ExpEvaluator* eval, ObjList& stack, const ExpOperation& oper, void* context)
|
||||||
|
{
|
||||||
|
if (!eval)
|
||||||
|
return false;
|
||||||
|
XDebug(DebugAll,"ScriptContext::runField '%s'",oper.name().c_str());
|
||||||
|
ExpEvaluator::pushOne(stack,new ExpOperation(m_params[oper.name()],oper.name()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptContext::runAssign(const ExpEvaluator* eval, const ExpOperation& oper, void* context)
|
||||||
|
{
|
||||||
|
if (!eval)
|
||||||
|
return false;
|
||||||
|
XDebug(DebugAll,"ScriptContext::runAssign '%s'='%s'",oper.name().c_str(),oper.c_str());
|
||||||
|
m_params.setParam(oper.name(),oper);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAKE_NAME(x) { #x, ScriptRun::x }
|
||||||
|
static const TokenDict s_states[] = {
|
||||||
|
MAKE_NAME(Invalid),
|
||||||
|
MAKE_NAME(Running),
|
||||||
|
MAKE_NAME(Incomplete),
|
||||||
|
MAKE_NAME(Succeeded),
|
||||||
|
MAKE_NAME(Failed),
|
||||||
|
{ 0, 0 }
|
||||||
|
};
|
||||||
|
#undef MAKE_NAME
|
||||||
|
|
||||||
|
ScriptRun::ScriptRun(ScriptCode* code, ScriptContext* context)
|
||||||
|
: Mutex(true,"ScriptRun"),
|
||||||
|
m_state(Invalid)
|
||||||
|
{
|
||||||
|
if (code)
|
||||||
|
code->ref();
|
||||||
|
m_code = code;
|
||||||
|
if (context)
|
||||||
|
context->ref();
|
||||||
|
else
|
||||||
|
context = new ScriptContext;
|
||||||
|
m_context = context;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptRun::~ScriptRun()
|
||||||
|
{
|
||||||
|
lock();
|
||||||
|
m_state = Invalid;
|
||||||
|
TelEngine::destruct(m_code);
|
||||||
|
TelEngine::destruct(m_context);
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ScriptRun::textState(Status state)
|
||||||
|
{
|
||||||
|
return lookup(state,s_states,"Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset script (but not the context) to initial state
|
||||||
|
ScriptRun::Status ScriptRun::reset()
|
||||||
|
{
|
||||||
|
Lock mylock(this);
|
||||||
|
// TODO
|
||||||
|
m_stack.clear();
|
||||||
|
return (m_state = (m_code && m_code->initialize(m_context)) ? Incomplete : Invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume execution, run one or more instructions of code
|
||||||
|
ScriptRun::Status ScriptRun::resume()
|
||||||
|
{
|
||||||
|
Lock mylock(this);
|
||||||
|
if (Running != m_state)
|
||||||
|
return m_state;
|
||||||
|
RefPointer<ScriptCode> code = m_code;
|
||||||
|
RefPointer<ScriptContext> ctxt = m_context;
|
||||||
|
if (!(code && ctxt))
|
||||||
|
return Invalid;
|
||||||
|
mylock.drop();
|
||||||
|
return code->evaluate(*ctxt,stack()) ? Succeeded : Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute one or more instructions of code from where it was left
|
||||||
|
ScriptRun::Status ScriptRun::execute()
|
||||||
|
{
|
||||||
|
Lock mylock(this);
|
||||||
|
if (Incomplete != m_state)
|
||||||
|
return m_state;
|
||||||
|
m_state = Running;
|
||||||
|
mylock.drop();
|
||||||
|
Status st = resume();
|
||||||
|
if (Running == st)
|
||||||
|
st = Incomplete;
|
||||||
|
lock();
|
||||||
|
if (Running == m_state)
|
||||||
|
m_state = st;
|
||||||
|
unlock();
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute instructions until succeeds or fails
|
||||||
|
ScriptRun::Status ScriptRun::run()
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
ScriptRun::Status s = state();
|
||||||
|
while (Incomplete == s)
|
||||||
|
s = execute();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|
File diff suppressed because it is too large
Load Diff
|
@ -1196,13 +1196,14 @@ void SS7MTP3::notify(SS7Layer2* link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
countLinks();
|
countLinks();
|
||||||
|
String text;
|
||||||
|
text << "Linkset has " << m_active << " active, ";
|
||||||
|
text << m_checked << " checked of " << m_total << " links";
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
String tmp;
|
String tmp;
|
||||||
if (link)
|
if (link)
|
||||||
tmp << "Link '" << link->toString() << "' is " << (link->operational()?"":"not ") << "operational. ";
|
tmp << "Link '" << link->toString() << "' is " << (link->operational()?"":"not ") << "operational. ";
|
||||||
Debug(this,DebugInfo,"%sLinkset has %u/%u/%u active/checked links [%p]",
|
Debug(this,DebugInfo,"%s%s [%p]",tmp.safe(),text.c_str(),this);
|
||||||
tmp.null()?"":tmp.c_str(),
|
|
||||||
m_active,m_checked,m_total,this);
|
|
||||||
#endif
|
#endif
|
||||||
// if operational status of a link changed notify upper layer
|
// if operational status of a link changed notify upper layer
|
||||||
if (act != m_active || chk != m_checked) {
|
if (act != m_active || chk != m_checked) {
|
||||||
|
@ -1252,7 +1253,7 @@ void SS7MTP3::notify(SS7Layer2* link)
|
||||||
notif.addParam("total",String(m_total));
|
notif.addParam("total",String(m_total));
|
||||||
notif.addParam("link", link ? link->toString() : "");
|
notif.addParam("link", link ? link->toString() : "");
|
||||||
notif.addParam("linkup", link ? String::boolText(link->operational()) : "");
|
notif.addParam("linkup", link ? String::boolText(link->operational()) : "");
|
||||||
|
notif.addParam("text", text);
|
||||||
mylock.drop();
|
mylock.drop();
|
||||||
SS7Layer3::notify(sls);
|
SS7Layer3::notify(sls);
|
||||||
engine()->notify(this,notif);
|
engine()->notify(this,notif);
|
||||||
|
|
|
@ -368,6 +368,10 @@ server/sipfeatures.yate: ../libs/yxml/libyatexml.a
|
||||||
server/sipfeatures.yate: LOCALFLAGS = -I@top_srcdir@/libs/yxml
|
server/sipfeatures.yate: LOCALFLAGS = -I@top_srcdir@/libs/yxml
|
||||||
server/sipfeatures.yate: LOCALLIBS = -L../libs/yxml -lyatexml
|
server/sipfeatures.yate: LOCALLIBS = -L../libs/yxml -lyatexml
|
||||||
|
|
||||||
|
javascript.yate: ../libyatescript.so
|
||||||
|
javascript.yate: LOCALFLAGS = -I@top_srcdir@/libs/yscript
|
||||||
|
javascript.yate: LOCALLIBS = -lyatescript
|
||||||
|
|
||||||
zlibcompress.yate: LOCALFLAGS = $(ZLIB_INC)
|
zlibcompress.yate: LOCALFLAGS = $(ZLIB_INC)
|
||||||
zlibcompress.yate: LOCALLIBS = $(ZLIB_LIB)
|
zlibcompress.yate: LOCALLIBS = $(ZLIB_LIB)
|
||||||
|
|
||||||
|
@ -405,6 +409,9 @@ server/ysnmpagent.yate: LOCALLIBS = -L../libs/yasn -lyasn -L../libs/ysnmp -lysnm
|
||||||
../libyatejabber.so ../libs/yjabber/libyatejabber.a: @top_srcdir@/libs/yjabber/xmpputils.h @top_srcdir@/libs/yjabber/yatejabber.h @top_srcdir@/libs/yjabber/yatejingle.h
|
../libyatejabber.so ../libs/yjabber/libyatejabber.a: @top_srcdir@/libs/yjabber/xmpputils.h @top_srcdir@/libs/yjabber/yatejabber.h @top_srcdir@/libs/yjabber/yatejingle.h
|
||||||
$(MAKE) -C ../libs/yjabber
|
$(MAKE) -C ../libs/yjabber
|
||||||
|
|
||||||
|
../libyatescript.so ../libs/yjabber/libyatescript.a: @top_srcdir@/libs/yscript/yatescript.h
|
||||||
|
$(MAKE) -C ../libs/yscript
|
||||||
|
|
||||||
../libs/ypbx/libyatepbx.a: @top_srcdir@/libs/ypbx/yatepbx.h
|
../libs/ypbx/libyatepbx.a: @top_srcdir@/libs/ypbx/yatepbx.h
|
||||||
$(MAKE) -C ../libs/ypbx
|
$(MAKE) -C ../libs/ypbx
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/**
|
||||||
|
* javascript.cpp
|
||||||
|
* This file is part of the YATE Project http://YATE.null.ro
|
||||||
|
*
|
||||||
|
* Javascript support based on libyscript
|
||||||
|
*
|
||||||
|
* 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 <yatephone.h>
|
||||||
|
#include <yatescript.h>
|
||||||
|
|
||||||
|
using namespace TelEngine;
|
||||||
|
namespace { // anonymous
|
||||||
|
|
||||||
|
class JavascriptModule : public Module
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JavascriptModule();
|
||||||
|
~JavascriptModule();
|
||||||
|
virtual void initialize();
|
||||||
|
bool unload();
|
||||||
|
protected:
|
||||||
|
virtual bool commandExecute(String& retVal, const String& line);
|
||||||
|
virtual bool commandComplete(Message& msg, const String& partLine, const String& partWord);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
INIT_PLUGIN(JavascriptModule);
|
||||||
|
|
||||||
|
UNLOAD_PLUGIN(unloadNow)
|
||||||
|
{
|
||||||
|
if (unloadNow)
|
||||||
|
return __plugin.unload();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
JavascriptModule::JavascriptModule()
|
||||||
|
: Module("javascript","misc",true)
|
||||||
|
{
|
||||||
|
Output("Loaded module Javascript");
|
||||||
|
}
|
||||||
|
|
||||||
|
JavascriptModule::~JavascriptModule()
|
||||||
|
{
|
||||||
|
Output("Unloading module Javascript");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JavascriptModule::commandExecute(String& retVal, const String& line)
|
||||||
|
{
|
||||||
|
if (!line.startsWith("js "))
|
||||||
|
return false;
|
||||||
|
String cmd = line.substr(3).trimSpaces();
|
||||||
|
if (cmd.null())
|
||||||
|
return false;
|
||||||
|
ExpOperation* rval = 0;
|
||||||
|
ScriptRun::Status st = JsParser::eval(cmd,&rval);
|
||||||
|
if (rval)
|
||||||
|
retVal << "'" << rval->name() << "'='" << *rval << "'\r\n";
|
||||||
|
else
|
||||||
|
retVal << ScriptRun::textState(st) << "\r\n";
|
||||||
|
TelEngine::destruct(rval);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JavascriptModule::commandComplete(Message& msg, const String& partLine, const String& partWord)
|
||||||
|
{
|
||||||
|
if (partLine.null() && partWord.null())
|
||||||
|
return false;
|
||||||
|
if (partLine.null() || (partLine == "help"))
|
||||||
|
itemComplete(msg.retValue(),"js",partWord);
|
||||||
|
return Module::commandComplete(msg,partLine,partWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JavascriptModule::unload()
|
||||||
|
{
|
||||||
|
uninstallRelays();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JavascriptModule::initialize()
|
||||||
|
{
|
||||||
|
Output("Initializing module Javascript");
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // anonymous namespace
|
||||||
|
|
||||||
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|
Loading…
Reference in New Issue