Updated scripting code.

git-svn-id: http://yate.null.ro/svn/yate/trunk@4921 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2012-02-17 16:19:17 +00:00
parent 6e8889f068
commit a242f792c5
8 changed files with 2181 additions and 308 deletions

View File

@ -1 +1,14 @@
[general]
; General settings for the Javascript module
; scripts_dir: string: The absolute or relative path used by default to load
; scripts if no full path is specified
; Note that a trailing path separator should be added
;scripts_dir=share/scripts/
[scripts]
; routing: string: Name of the file holding the routing instructions
; Example: routing=route.js
;routing=

View File

@ -114,30 +114,44 @@ static const TokenDict s_unaryOps_sql[] =
#undef MAKEOP
#undef ASSIGN
RefObject* ExpExtender::refObj()
{
return 0;
}
bool ExpExtender::runFunction(const ExpEvaluator* eval, ObjList& stack, const ExpOperation& oper, void* context)
bool ExpExtender::hasField(ObjList& stack, const String& name, GenObject* context) const
{
return false;
}
bool ExpExtender::runField(const ExpEvaluator* eval, ObjList& stack, const ExpOperation& oper, void* context)
NamedString* ExpExtender::getField(ObjList& stack, const String& name, GenObject* context) const
{
return 0;
}
bool ExpExtender::runFunction(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
return false;
}
bool ExpExtender::runAssign(const ExpEvaluator* eval, const ExpOperation& oper, void* context)
bool ExpExtender::runField(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
return false;
}
bool ExpExtender::runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
return false;
}
ExpEvaluator::ExpEvaluator(const TokenDict* operators, const TokenDict* unaryOps)
: m_operators(operators), m_unaryOps(unaryOps), m_extender(0)
: m_operators(operators), m_unaryOps(unaryOps), m_inError(false), m_extender(0)
{
}
ExpEvaluator::ExpEvaluator(ExpEvaluator::Parser style)
: m_operators(0), m_unaryOps(0), m_extender(0)
: m_operators(0), m_unaryOps(0), m_inError(false), m_extender(0)
{
switch (style) {
case C:
@ -153,12 +167,12 @@ ExpEvaluator::ExpEvaluator(ExpEvaluator::Parser style)
ExpEvaluator::ExpEvaluator(const ExpEvaluator& original)
: m_operators(original.m_operators), m_unaryOps(original.unaryOps()),
m_extender(0)
m_inError(false), m_extender(0)
{
extender(original.extender());
for (ObjList* l = original.m_opcodes.skipNull(); l; l = l->skipNext()) {
const ExpOperation* o = static_cast<const ExpOperation*>(l->get());
m_opcodes.append(new ExpOperation(*o));
m_opcodes.append(o->clone());
}
}
@ -171,18 +185,19 @@ void ExpEvaluator::extender(ExpExtender* ext)
{
if (ext == m_extender)
return;
if (ext && !ext->ref())
if (ext && ext->refObj() && !ext->refObj()->ref())
return;
ExpExtender* tmp = m_extender;
m_extender = ext;
TelEngine::destruct(tmp);
if (tmp)
TelEngine::destruct(tmp->refObj());
}
char ExpEvaluator::skipWhites(const char*& expr)
{
if (!expr)
return 0;
while (*expr==' ' || *expr=='\t')
while (*expr==' ' || *expr=='\t' || *expr=='\r' || *expr=='\n')
expr++;
return *expr;
}
@ -193,10 +208,20 @@ bool ExpEvaluator::keywordChar(char c) const
('0' <= c && c <= '9') || (c == '_');
}
char ExpEvaluator::skipComments(const char*& expr, GenObject* context) const
{
return skipWhites(expr);
}
int ExpEvaluator::preProcess(const char*& expr, GenObject* context)
{
return -1;
}
ExpEvaluator::Opcode ExpEvaluator::getOperator(const char*& expr, const TokenDict* operators, bool caseInsensitive) const
{
XDebug(this,DebugAll,"getOperator('%s',%p,%s)",expr,operators,String::boolText(caseInsensitive));
skipWhites(expr);
skipComments(expr);
if (operators) {
bool kw = keywordChar(*expr);
for (const TokenDict* o = operators; o->token; o++) {
@ -217,38 +242,49 @@ ExpEvaluator::Opcode ExpEvaluator::getOperator(const char*& expr, const TokenDic
bool ExpEvaluator::gotError(const char* error, const char* text) const
{
if (!error)
if (!error) {
if (!text)
return false;
error = "unknown error";
Debug(this,DebugWarn,"Evaluator got error: %s%s%s",error,
}
Debug(this,DebugWarn,"Evaluator error: %s%s%s",error,
(text ? " at: " : ""),
c_safe(text));
return false;
}
bool ExpEvaluator::getInstruction(const char*& expr)
bool ExpEvaluator::gotError(const char* error, const char* text)
{
m_inError = true;
return const_cast<const ExpEvaluator*>(this)->gotError(error,text);
}
bool ExpEvaluator::getInstruction(const char*& expr, Opcode nested)
{
return false;
}
bool ExpEvaluator::getOperand(const char*& expr)
bool ExpEvaluator::getOperand(const char*& expr, bool endOk)
{
if (inError())
return false;
XDebug(this,DebugAll,"getOperand '%s'",expr);
char c = skipWhites(expr);
char c = skipComments(expr);
if (!c)
// end of string
return true;
return endOk;
if (c == '(') {
// parenthesized subexpression
if (!runCompile(++expr,')'))
return false;
if (skipWhites(expr) != ')')
if (skipComments(expr) != ')')
return gotError("Expecting ')'",expr);
expr++;
return true;
}
Opcode op = getUnaryOperator(expr);
if (op != OpcNone) {
if (!getOperand(expr))
if (!getOperand(expr,false))
return false;
addOpcode(op);
return true;
@ -260,6 +296,8 @@ bool ExpEvaluator::getOperand(const char*& expr)
bool ExpEvaluator::getNumber(const char*& expr)
{
if (inError())
return false;
XDebug(this,DebugAll,"getNumber '%s'",expr);
char* endp = 0;
long int val = ::strtol(expr,&endp,0);
@ -273,8 +311,10 @@ bool ExpEvaluator::getNumber(const char*& expr)
bool ExpEvaluator::getString(const char*& expr)
{
if (inError())
return false;
XDebug(this,DebugAll,"getString '%s'",expr);
char c = skipWhites(expr);
char c = skipComments(expr);
if (c == '"' || c == '\'') {
char sep = c;
const char* start = ++expr;
@ -286,6 +326,7 @@ bool ExpEvaluator::getString(const char*& expr)
addOpcode(str);
return true;
}
expr--;
return gotError("Expecting string end");
}
return false;
@ -304,25 +345,27 @@ int ExpEvaluator::getKeyword(const char* str) const
bool ExpEvaluator::getFunction(const char*& expr)
{
if (inError())
return false;
XDebug(this,DebugAll,"getFunction '%s'",expr);
skipWhites(expr);
skipComments(expr);
int len = getKeyword(expr);
const char* s = expr+len;
skipWhites(expr);
if ((len <= 0) || (skipWhites(s) != '('))
skipComments(expr);
if ((len <= 0) || (skipComments(s) != '('))
return false;
s++;
int argc = 0;
// parameter list
do {
if (!runCompile(s,')')) {
if (!argc && (skipWhites(s) == ')'))
if (!argc && (skipComments(s) == ')'))
break;
return false;
}
argc++;
} while (getSeparator(s,true));
if (skipWhites(s) != ')')
if (skipComments(s) != ')')
return gotError("Expecting ')' after function",s);
String str(expr,len);
expr = s+1;
@ -333,8 +376,10 @@ bool ExpEvaluator::getFunction(const char*& expr)
bool ExpEvaluator::getField(const char*& expr)
{
if (inError())
return false;
XDebug(this,DebugAll,"getField '%s'",expr);
skipWhites(expr);
skipComments(expr);
int len = getKeyword(expr);
if (len <= 0)
return false;
@ -342,7 +387,7 @@ bool ExpEvaluator::getField(const char*& expr)
return false;
String str(expr,len);
expr += len;
DDebug(this,DebugAll,"Found %s",str.safe());
DDebug(this,DebugAll,"Found field '%s'",str.safe());
addOpcode(OpcField,str);
return true;
}
@ -432,14 +477,14 @@ bool ExpEvaluator::getRightAssoc(ExpEvaluator::Opcode oper) const
bool ExpEvaluator::getSeparator(const char*& expr, bool remove)
{
if (skipWhites(expr) != ',')
if (skipComments(expr) != ',')
return false;
if (remove)
expr++;
return true;
}
bool ExpEvaluator::runCompile(const char*& expr, char stop)
bool ExpEvaluator::runCompile(const char*& expr, char stop, Opcode nested)
{
typedef struct {
Opcode code;
@ -447,33 +492,43 @@ bool ExpEvaluator::runCompile(const char*& expr, char stop)
} StackedOpcode;
StackedOpcode stack[10];
unsigned int stackPos = 0;
DDebug(this,DebugInfo,"runCompile '%s'",expr);
if (skipWhites(expr) == ')')
DDebug(this,DebugInfo,"runCompile '%s' '%1s'",expr,&stop);
if (skipComments(expr) == ')')
return false;
m_inError = false;
if (expr[0] == '*' && !expr[1]) {
expr++;
addOpcode(OpcField,"*");
return true;
}
for (;;) {
while (skipWhites(expr) && getInstruction(expr))
while (skipComments(expr) && getInstruction(expr,nested))
;
if (inError())
return false;
if (stop && (skipComments(expr) == stop))
return true;
if (!getOperand(expr))
return false;
Opcode oper;
while ((oper = getPostfixOperator(expr)) != OpcNone)
addOpcode(oper);
char c = skipWhites(expr);
if (inError())
return false;
char c = skipComments(expr);
if (!c || c == stop || getSeparator(expr,false)) {
while (stackPos)
addOpcode(stack[--stackPos].code);
return true;
}
if (inError())
return false;
oper = getOperator(expr);
if (oper == OpcNone)
return gotError("Operator expected",expr);
return gotError("Operator or separator expected",expr);
int precedence = 2 * getPrecedence(oper);
int precAdj = precedence;
// precedence being equal favor right associative operators
if (getRightAssoc(oper))
precAdj++;
while (stackPos && stack[stackPos-1].prec >= precAdj)
@ -523,7 +578,10 @@ bool ExpEvaluator::trySimplify()
if (o->opcode() == OpcLAnd || o->opcode() == OpcAnd || o->opcode() == OpcMul) {
if ((op1->opcode() == OpcPush && !op1->number() && op2->opcode() == OpcField) ||
(op2->opcode() == OpcPush && !op2->number() && op1->opcode() == OpcField)) {
(m_opcodes+i)->set(new ExpOperation(0));
if (o->opcode() == OpcLAnd)
(m_opcodes+i)->set(new ExpOperation(false));
else
(m_opcodes+i)->set(new ExpOperation((long int)0));
m_opcodes.remove(op1);
m_opcodes.remove(op2);
i -= 2;
@ -534,7 +592,7 @@ bool ExpEvaluator::trySimplify()
if (o->opcode() == OpcLOr) {
if ((op1->opcode() == OpcPush && op1->number() && op2->opcode() == OpcField) ||
(op2->opcode() == OpcPush && op2->number() && op1->opcode() == OpcField)) {
(m_opcodes+i)->set(new ExpOperation(1));
(m_opcodes+i)->set(new ExpOperation(true));
m_opcodes.remove(op1);
m_opcodes.remove(op2);
i -= 2;
@ -544,8 +602,8 @@ bool ExpEvaluator::trySimplify()
}
if ((op1->opcode() == OpcPush) && (op2->opcode() == OpcPush)) {
ObjList stack;
pushOne(stack,new ExpOperation(*op1));
pushOne(stack,new ExpOperation(*op2));
pushOne(stack,op1->clone());
pushOne(stack,op2->clone());
if (runOperation(stack,*o)) {
// replace operators and operation with computed constant
(m_opcodes+i)->set(popOne(stack));
@ -566,7 +624,7 @@ bool ExpEvaluator::trySimplify()
continue;
if (op->opcode() == OpcPush) {
ObjList stack;
pushOne(stack,new ExpOperation(op));
pushOne(stack,op->clone());
if (runOperation(stack,*o)) {
// replace unary operator and operation with computed constant
(m_opcodes+i)->set(popOne(stack));
@ -591,7 +649,7 @@ bool ExpEvaluator::trySimplify()
return done;
}
void ExpEvaluator::addOpcode(ExpEvaluator::Opcode oper, bool barrier)
ExpOperation* ExpEvaluator::addOpcode(ExpEvaluator::Opcode oper, bool barrier)
{
DDebug(this,DebugAll,"addOpcode %u",oper);
if (oper == OpcAs) {
@ -604,25 +662,49 @@ void ExpEvaluator::addOpcode(ExpEvaluator::Opcode oper, bool barrier)
o->String::operator=(o->name());
}
}
m_opcodes.append(new ExpOperation(oper,0,0,barrier));
ExpOperation* op = new ExpOperation(oper,0,ExpOperation::nonInteger(),barrier);
m_opcodes.append(op);
return op;
}
void ExpEvaluator::addOpcode(ExpEvaluator::Opcode oper, const String& name, long int value, bool barrier)
ExpOperation* ExpEvaluator::addOpcode(ExpEvaluator::Opcode oper, long int value, bool barrier)
{
DDebug(this,DebugAll,"addOpcode %u %lu",oper,value);
ExpOperation* op = new ExpOperation(oper,0,value,barrier);
m_opcodes.append(op);
return op;
}
ExpOperation* ExpEvaluator::addOpcode(ExpEvaluator::Opcode oper, const String& name, long int value, bool barrier)
{
DDebug(this,DebugAll,"addOpcode %u '%s' %ld",oper,name.c_str(),value);
m_opcodes.append(new ExpOperation(oper,name,value,barrier));
ExpOperation* op = new ExpOperation(oper,name,value,barrier);
m_opcodes.append(op);
return op;
}
void ExpEvaluator::addOpcode(const String& value)
ExpOperation* ExpEvaluator::addOpcode(const String& value)
{
DDebug(this,DebugAll,"addOpcode ='%s'",value.c_str());
m_opcodes.append(new ExpOperation(value));
ExpOperation* op = new ExpOperation(value);
m_opcodes.append(op);
return op;
}
void ExpEvaluator::addOpcode(long int value)
ExpOperation* ExpEvaluator::addOpcode(long int value)
{
DDebug(this,DebugAll,"addOpcode =%ld",value);
m_opcodes.append(new ExpOperation(value));
ExpOperation* op = new ExpOperation(value);
m_opcodes.append(op);
return op;
}
ExpOperation* ExpEvaluator::addOpcode(bool value)
{
DDebug(this,DebugAll,"addOpcode =%s",String::boolText(value));
ExpOperation* op = new ExpOperation(value);
m_opcodes.append(op);
return op;
}
void ExpEvaluator::pushOne(ObjList& stack, ExpOperation* oper)
@ -642,11 +724,13 @@ ExpOperation* ExpEvaluator::popOne(ObjList& stack)
stack.remove();
}
if (o && o->barrier()) {
XDebug(DebugAll,"Not popping barrier %u: '%s'='%s'",o->opcode(),o->name().c_str(),o->c_str());
XDebug(DebugInfo,"Not popping barrier %u: '%s'='%s'",o->opcode(),o->name().c_str(),o->c_str());
return 0;
}
stack.remove(o,false);
DDebug(DebugInfo,"Popped: %p",o);
XDebug(DebugAll,"Popped: %p%s%s",o,
(YOBJECT(ExpFunction,o) ? " function" : ""),
(YOBJECT(ExpWrapper,o) ? " wrapper" : ""));
return o;
}
@ -665,7 +749,7 @@ ExpOperation* ExpEvaluator::popAny(ObjList& stack)
return o;
}
ExpOperation* ExpEvaluator::popValue(ObjList& stack, void* context) const
ExpOperation* ExpEvaluator::popValue(ObjList& stack, GenObject* context) const
{
ExpOperation* oper = popOne(stack);
if (!oper || (oper->opcode() != OpcField))
@ -675,12 +759,18 @@ ExpOperation* ExpEvaluator::popValue(ObjList& stack, void* context) const
return ok ? popOne(stack) : 0;
}
bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, void* context) const
bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, GenObject* context) const
{
DDebug(this,DebugAll,"runOperation(%p,%u,%p) %s",&stack,oper.opcode(),context,getOperator(oper.opcode()));
XDebug(this,DebugAll,"stack: %s",dump(stack).c_str());
bool boolRes = true;
switch (oper.opcode()) {
case OpcPush:
pushOne(stack,new ExpOperation(oper));
case OpcField:
pushOne(stack,oper.clone());
break;
case OpcNone:
case OpcLabel:
break;
case OpcAnd:
case OpcOr:
@ -692,6 +782,8 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, void*
case OpcMul:
case OpcDiv:
case OpcMod:
boolRes = false;
// fall through
case OpcEq:
case OpcNe:
case OpcLt:
@ -781,8 +873,14 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, void*
}
TelEngine::destruct(op1);
TelEngine::destruct(op2);
DDebug(this,DebugAll,"Numeric result: %lu",val);
pushOne(stack,new ExpOperation(val));
if (boolRes) {
DDebug(this,DebugAll,"Bool result: '%s'",String::boolText(val != 0));
pushOne(stack,new ExpOperation(val != 0));
}
else {
DDebug(this,DebugAll,"Numeric result: %lu",val);
pushOne(stack,new ExpOperation(val));
}
}
break;
case OpcLAnd:
@ -809,7 +907,7 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, void*
TelEngine::destruct(op1);
TelEngine::destruct(op2);
DDebug(this,DebugAll,"Bool result: '%s'",String::boolText(val));
pushOne(stack,new ExpOperation(val ? 1 : 0));
pushOne(stack,new ExpOperation(val));
}
break;
case OpcCat:
@ -837,7 +935,7 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, void*
TelEngine::destruct(op2);
return gotError("ExpEvaluator stack underflow");
}
pushOne(stack,new ExpOperation(*op1,*op2));
pushOne(stack,op1->clone(*op2));
TelEngine::destruct(op1);
TelEngine::destruct(op2);
}
@ -853,25 +951,22 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, void*
TelEngine::destruct(op);
switch (oper.opcode()) {
case OpcNeg:
val = -val;
pushOne(stack,new ExpOperation(-val));
break;
case OpcNot:
val = ~val;
pushOne(stack,new ExpOperation(~val));
break;
case OpcLNot:
val = val ? 0 : 1;
pushOne(stack,new ExpOperation(!val));
break;
default:
pushOne(stack,new ExpOperation(val));
break;
}
pushOne(stack,new ExpOperation(val));
}
break;
case OpcFunc:
return runFunction(stack,oper,context) || gotError("Function call failed");
case OpcField:
pushOne(stack,new ExpOperation(oper));
break;
case OpcIncPre:
case OpcDecPre:
case OpcIncPost:
@ -911,7 +1006,7 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, void*
break;
}
(*fld) = num;
bool ok = runAssign(*fld,context);
bool ok = runAssign(stack,*fld,context);
TelEngine::destruct(fld);
if (!ok) {
TelEngine::destruct(val);
@ -934,9 +1029,11 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, void*
TelEngine::destruct(val);
return gotError("Expecting LValue in assignment");
}
ExpOperation op(*val,fld->name());
ExpOperation* op = val->clone(fld->name());
TelEngine::destruct(fld);
if (!runAssign(op,context)) {
bool ok = runAssign(stack,*op,context);
TelEngine::destruct(op);
if (!ok) {
TelEngine::destruct(val);
return gotError("Assignment failed");
}
@ -958,7 +1055,7 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, void*
TelEngine::destruct(val);
return gotError("Expecting LValue in assignment");
}
pushOne(stack,new ExpOperation(*fld));
pushOne(stack,fld->clone());
pushOne(stack,fld);
pushOne(stack,val);
ExpOperation op((Opcode)(oper.opcode() & ~OpcAssign),
@ -975,7 +1072,7 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, void*
return true;
}
bool ExpEvaluator::runFunction(ObjList& stack, const ExpOperation& oper, void* context) const
bool ExpEvaluator::runFunction(ObjList& stack, const ExpOperation& oper, GenObject* context) const
{
DDebug(this,DebugAll,"runFunction(%p,'%s' %ld, %p) ext=%p",
&stack,oper.name().c_str(),oper.number(),context,(void*)m_extender);
@ -994,27 +1091,27 @@ bool ExpEvaluator::runFunction(ObjList& stack, const ExpOperation& oper, void* c
if (oper.name() == YSTRING("now")) {
if (oper.number())
return gotError("Function expects no arguments");
pushOne(stack,new ExpOperation(Time::secNow()));
pushOne(stack,new ExpOperation((long int)Time::secNow()));
return true;
}
return m_extender && m_extender->runFunction(this,stack,oper,context);
return m_extender && m_extender->runFunction(stack,oper,context);
}
bool ExpEvaluator::runField(ObjList& stack, const ExpOperation& oper, void* context) const
bool ExpEvaluator::runField(ObjList& stack, const ExpOperation& oper, GenObject* context) const
{
DDebug(this,DebugAll,"runField(%p,'%s',%p) ext=%p",
&stack,oper.name().c_str(),context,(void*)m_extender);
return m_extender && m_extender->runField(this,stack,oper,context);
return m_extender && m_extender->runField(stack,oper,context);
}
bool ExpEvaluator::runAssign(const ExpOperation& oper, void* context) const
bool ExpEvaluator::runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context) const
{
DDebug(this,DebugAll,"runAssign('%s'='%s',%p) ext=%p",
oper.name().c_str(),oper.c_str(),context,(void*)m_extender);
return m_extender && m_extender->runAssign(this,oper,context);
return m_extender && m_extender->runAssign(stack,oper,context);
}
bool ExpEvaluator::runEvaluate(const ObjList& opcodes, ObjList& stack, void* context) const
bool ExpEvaluator::runEvaluate(const ObjList& opcodes, ObjList& stack, GenObject* context) const
{
DDebug(this,DebugInfo,"runEvaluate(%p,%p,%p)",&opcodes,&stack,context);
for (const ObjList* l = opcodes.skipNull(); l; l = l->skipNext()) {
@ -1025,7 +1122,7 @@ bool ExpEvaluator::runEvaluate(const ObjList& opcodes, ObjList& stack, void* con
return true;
}
bool ExpEvaluator::runEvaluate(const ObjVector& opcodes, ObjList& stack, void* context, unsigned int index) const
bool ExpEvaluator::runEvaluate(const ObjVector& opcodes, ObjList& stack, GenObject* context, unsigned int index) const
{
DDebug(this,DebugInfo,"runEvaluate(%p,%p,%p,%u)",&opcodes,&stack,context,index);
for (; index < opcodes.length(); index++) {
@ -1036,12 +1133,12 @@ bool ExpEvaluator::runEvaluate(const ObjVector& opcodes, ObjList& stack, void* c
return true;
}
bool ExpEvaluator::runEvaluate(ObjList& stack, void* context) const
bool ExpEvaluator::runEvaluate(ObjList& stack, GenObject* context) const
{
return runEvaluate(m_opcodes,stack,context);
}
bool ExpEvaluator::runAllFields(ObjList& stack, void* context) const
bool ExpEvaluator::runAllFields(ObjList& stack, GenObject* context) const
{
DDebug(this,DebugAll,"runAllFields(%p,%p)",&stack,context);
bool ok = true;
@ -1065,20 +1162,31 @@ bool ExpEvaluator::runAllFields(ObjList& stack, void* context) const
return ok;
}
int ExpEvaluator::compile(const char* expr)
int ExpEvaluator::compile(const char* expr, GenObject* context)
{
if (!skipWhites(expr))
if (!skipComments(expr,context))
return 0;
int res = 0;
do {
for (;;) {
int pre;
m_inError = false;
while ((pre = preProcess(expr,context)) >= 0)
res += pre;
if (inError())
return 0;
if (!runCompile(expr))
return 0;
res++;
} while (getSeparator(expr,true));
return skipWhites(expr) ? 0 : res;
bool sep = false;
while (getSeparator(expr,true))
sep = true;
if (!sep)
break;
}
return skipComments(expr,context) ? 0 : res;
}
bool ExpEvaluator::evaluate(ObjList* results, void* context) const
bool ExpEvaluator::evaluate(ObjList* results, GenObject* context) const
{
if (results) {
results->clear();
@ -1089,7 +1197,7 @@ bool ExpEvaluator::evaluate(ObjList* results, void* context) const
return runEvaluate(res,context);
}
int ExpEvaluator::evaluate(NamedList& results, unsigned int index, const char* prefix, void* context) const
int ExpEvaluator::evaluate(NamedList& results, unsigned int index, const char* prefix, GenObject* context) const
{
ObjList stack;
if (!evaluate(stack,context))
@ -1109,7 +1217,7 @@ int ExpEvaluator::evaluate(NamedList& results, unsigned int index, const char* p
return column;
}
int ExpEvaluator::evaluate(Array& results, unsigned int index, void* context) const
int ExpEvaluator::evaluate(Array& results, unsigned int index, GenObject* context) const
{
Debug(this,DebugStub,"Please implement ExpEvaluator::evaluate(Array)");
return -1;
@ -1146,6 +1254,33 @@ void ExpEvaluator::dump(const ObjList& codes, String& res) const
}
ExpOperation* ExpFunction::clone(const char* name) const
{
XDebug(DebugInfo,"ExpFunction::clone('%s') [%p]",name,this);
return new ExpFunction(name,number());
}
ExpOperation* ExpWrapper::clone(const char* name) const
{
XDebug(DebugInfo,"ExpWrapper::clone('%s') [%p]",name,this);
RefObject* r = YOBJECT(RefObject,object());
if (r)
r->ref();
return new ExpWrapper(object(),name);
}
void* ExpWrapper::getObject(const String& name) const
{
if (name == YSTRING("ExpWrapper"))
return const_cast<ExpWrapper*>(this);
void* obj = ExpOperation::getObject(name);
if (obj)
return obj;
return m_object ? m_object->getObject(name) : 0;
}
TableEvaluator::TableEvaluator(const TableEvaluator& original)
: m_select(original.m_select), m_where(original.m_where),
m_limit(original.m_limit), m_limitVal(original.m_limitVal)
@ -1176,7 +1311,7 @@ void TableEvaluator::extender(ExpExtender* ext)
m_limit.extender(ext);
}
bool TableEvaluator::evalWhere(void* context)
bool TableEvaluator::evalWhere(GenObject* context)
{
if (m_where.null())
return true;
@ -1190,14 +1325,14 @@ bool TableEvaluator::evalWhere(void* context)
return (o->opcode() == ExpEvaluator::OpcPush) && o->number();
}
bool TableEvaluator::evalSelect(ObjList& results, void* context)
bool TableEvaluator::evalSelect(ObjList& results, GenObject* context)
{
if (m_select.null())
return false;
return m_select.evaluate(results,context);
}
unsigned int TableEvaluator::evalLimit(void* context)
unsigned int TableEvaluator::evalLimit(GenObject* context)
{
if (m_limitVal == (unsigned int)-2) {
m_limitVal = (unsigned int)-1;

View File

@ -22,18 +22,25 @@
*/
#include "yatescript.h"
#include <yatengine.h>
using namespace TelEngine;
namespace { // anonymous
class JsContext : public ScriptContext
class JsContext : public JsObject, public Mutex
{
YCLASS(JsContext,ScriptContext)
YCLASS(JsContext,JsObject)
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);
inline JsContext()
: JsObject("Context",this), Mutex(true,"JsContext")
{ }
virtual bool runFunction(ObjList& stack, const ExpOperation& oper, GenObject* context);
virtual bool runField(ObjList& stack, const ExpOperation& oper, GenObject* context);
virtual bool runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context);
private:
GenObject* resolveTop(ObjList& stack, const String& name, GenObject* context);
GenObject* resolve(ObjList& stack, String& name, GenObject* context);
};
class JsCode : public ScriptCode, public ExpEvaluator
@ -44,6 +51,7 @@ public:
OpcBegin = OpcPrivate + 1,
OpcEnd,
OpcIndex,
OpcTypeof,
OpcNew,
OpcFor,
OpcWhile,
@ -51,6 +59,7 @@ public:
OpcElse,
OpcSwitch,
OpcCase,
OpcDefault,
OpcBreak,
OpcCont,
OpcIn,
@ -61,25 +70,64 @@ public:
OpcFinally,
OpcThrow,
OpcReturn,
OpcJump,
OpcJumpTrue,
OpcJumpFalse,
OpcJRel,
OpcJRelTrue,
OpcJRelFalse,
OpcTrue,
OpcFalse,
OpcInclude,
};
inline JsCode()
: ExpEvaluator(C), m_label(0)
: ExpEvaluator(C), m_label(0), m_depth(0)
{ debugName("JsCode"); }
virtual bool initialize(ScriptContext* context) const;
virtual bool evaluate(ScriptContext& context, ObjList& results) const;
virtual bool evaluate(ScriptRun& runner, ObjList& results) const;
bool link();
protected:
virtual bool keywordChar(char c) const;
virtual int getKeyword(const char* str) const;
virtual bool getInstruction(const char*& expr);
virtual char skipComments(const char*& expr, GenObject* context = 0) const;
virtual int preProcess(const char*& expr, GenObject* context = 0);
virtual bool getInstruction(const char*& expr, Opcode nested);
virtual bool getNumber(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;
virtual bool runOperation(ObjList& stack, const ExpOperation& oper, GenObject* context) const;
virtual bool runFunction(ObjList& stack, const ExpOperation& oper, GenObject* context) const;
virtual bool runField(ObjList& stack, const ExpOperation& oper, GenObject* context) const;
virtual bool runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context) const;
private:
int m_label;
ObjVector m_linked;
bool preProcessInclude(const char*& expr, GenObject* context);
bool evalList(ObjList& stack, GenObject* context) const;
bool evalVector(ObjList& stack, GenObject* context) const;
bool jumpToLabel(long int label, GenObject* context) const;
bool jumpRelative(long int offset, GenObject* context) const;
long int m_label;
int m_depth;
};
class JsRunner : public ScriptRun
{
friend class JsCode;
public:
inline JsRunner(ScriptCode* code, ScriptContext* context)
: ScriptRun(code,context),
m_paused(false), m_opcode(0), m_index(0)
{ }
virtual Status reset();
virtual Status resume();
private:
bool m_paused;
ObjList* m_opcode;
unsigned int m_index;
};
#define MAKEOP(s,o) { s, JsCode::Opc ## o }
@ -91,6 +139,7 @@ static const TokenDict s_operators[] =
static const TokenDict s_unaryOps[] =
{
MAKEOP("new", New),
MAKEOP("typeof", Typeof),
{ 0, 0 }
};
@ -110,6 +159,7 @@ static const TokenDict s_instr[] =
MAKEOP("else", Else),
MAKEOP("switch", Switch),
MAKEOP("case", Case),
MAKEOP("default", Default),
MAKEOP("break", Break),
MAKEOP("continue", Cont),
MAKEOP("in", In),
@ -122,28 +172,114 @@ static const TokenDict s_instr[] =
MAKEOP("return", Return),
{ 0, 0 }
};
static const TokenDict s_bools[] =
{
MAKEOP("false", False),
MAKEOP("true", True),
{ 0, 0 }
};
static const TokenDict s_preProc[] =
{
MAKEOP("#include", Include),
{ 0, 0 }
};
#undef MAKEOP
bool JsContext::runFunction(const ExpEvaluator* eval, ObjList& stack, const ExpOperation& oper, void* context)
GenObject* JsContext::resolveTop(ObjList& stack, const String& name, GenObject* context)
{
return ScriptContext::runFunction(eval,stack,oper,context);
XDebug(DebugAll,"JsContext::resolveTop '%s'",name.c_str());
for (ObjList* l = stack.skipNull(); l; l = l->skipNext()) {
JsObject* jso = YOBJECT(JsObject,l->get());
if (jso && jso->hasField(stack,name,context))
return jso;
}
return this;
}
bool JsContext::runField(const ExpEvaluator* eval, ObjList& stack, const ExpOperation& oper, void* context)
GenObject* JsContext::resolve(ObjList& stack, String& name, GenObject* context)
{
if (!eval)
return false;
XDebug(DebugAll,"JsContext::runField '%s'",oper.name().c_str());
return ScriptContext::runField(eval,stack,oper,context);
if (name.find('.') < 0)
return resolveTop(stack,name,context);
ObjList* list = name.split('.',true);
GenObject* obj = 0;
for (ObjList* l = list->skipNull(); l; ) {
const String* s = static_cast<const String*>(l->get());
l = l->skipNext();
if (TelEngine::null(s)) {
// consecutive dots - not good
obj = 0;
break;
}
if (!obj)
obj = resolveTop(stack,*s,context);
if (!l) {
name = *s;
break;
}
ExpExtender* ext = YOBJECT(ExpExtender,obj);
if (ext)
obj = ext->getField(stack,*s,context);
}
TelEngine::destruct(list);
XDebug(DebugAll,"JsContext::resolve got '%s' %p for '%s'",
(obj ? obj->toString().c_str() : 0),obj,name.c_str());
return obj;
}
bool JsContext::runAssign(const ExpEvaluator* eval, const ExpOperation& oper, void* context)
bool JsContext::runFunction(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
if (!eval)
return false;
XDebug(DebugAll,"JsContext::runAssign '%s'='%s'",oper.name().c_str(),oper.c_str());
return ScriptContext::runAssign(eval,oper,context);
XDebug(DebugAll,"JsContext::runFunction '%s' [%p]",oper.name().c_str(),this);
String name = oper.name();
GenObject* o = resolve(stack,name,context);
if (o && o != this) {
ExpExtender* ext = YOBJECT(ExpExtender,o);
if (ext) {
ExpOperation op(oper,name);
return ext->runFunction(stack,op,context);
}
}
if (name == YSTRING("isNaN")) {
if (oper.number() != 1)
return false;
ExpOperation* op = ExpEvaluator::popOne(stack);
if (!op)
return false;
ExpEvaluator::pushOne(stack,new ExpOperation(!op->isInteger()));
return true;
}
return JsObject::runFunction(stack,oper,context);
}
bool JsContext::runField(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
XDebug(DebugAll,"JsContext::runField '%s' [%p]",oper.name().c_str(),this);
String name = oper.name();
GenObject* o = resolve(stack,name,context);
if (o && o != this) {
ExpExtender* ext = YOBJECT(ExpExtender,o);
if (ext) {
ExpOperation op(oper,name);
return ext->runField(stack,op,context);
}
}
return JsObject::runField(stack,oper,context);
}
bool JsContext::runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
XDebug(DebugAll,"JsContext::runAssign '%s'='%s' [%p]",oper.name().c_str(),oper.c_str(),this);
String name = oper.name();
GenObject* o = resolve(stack,name,context);
if (o && o != this) {
ExpExtender* ext = YOBJECT(ExpExtender,o);
if (ext) {
ExpOperation op(oper,name);
return ext->runAssign(stack,op,context);
}
}
return JsObject::runAssign(stack,oper,context);
}
@ -152,15 +288,60 @@ bool JsCode::initialize(ScriptContext* context) const
{
if (!context)
return false;
JsObject::initialize(*context);
JsObject::initialize(context);
return true;
}
bool JsCode::evaluate(ScriptContext& context, ObjList& results) const
bool JsCode::evaluate(ScriptRun& runner, ObjList& results) const
{
if (null())
return false;
return ExpEvaluator::evaluate(results,&context);
bool ok = m_linked.length() ? evalVector(results,&runner) : evalList(results,&runner);
if (!ok)
return false;
if (static_cast<JsRunner&>(runner).m_paused)
return true;
if (!runAllFields(results,&runner))
return gotError("Could not evaluate all fields");
return true;
}
// Convert list to vector and fix label relocations
bool JsCode::link()
{
if (!m_opcodes.count())
return false;
m_linked.assign(m_opcodes);
unsigned int n = m_linked.count();
if (!n)
return false;
for (unsigned int i = 0; i < n; i++) {
const ExpOperation* l = static_cast<const ExpOperation*>(m_linked[i]);
if (!l || l->opcode() != OpcLabel)
continue;
long int lbl = l->number();
for (unsigned int j = 0; j < n; i++) {
ExpOperation* jmp = static_cast<const ExpOperation*>(m_linked[j]);
if (!jmp || jmp->number() != lbl)
continue;
Opcode op = OpcNone;
switch (jmp->opcode()) {
case (Opcode)OpcJump:
op = (Opcode)OpcJRel;
break;
case (Opcode)OpcJumpTrue:
op = (Opcode)OpcJRelTrue;
break;
case (Opcode)OpcJumpFalse:
op = (Opcode)OpcJRelFalse;
break;
default:
continue;
}
long int offs = i - j;
m_linked.set(new ExpOperation(op,0,offs,jmp->barrier()),j);
}
}
}
bool JsCode::keywordChar(char c) const
@ -171,30 +352,120 @@ bool JsCode::keywordChar(char c) const
int JsCode::getKeyword(const char* str) const
{
int len = 0;
const char*s = str;
for (;; len++) {
char c = *str++;
char c = *s++;
if (c <= ' ')
break;
if (keywordChar(c) || (len && (c == '.')))
continue;
break;
}
if (len > 1 && (str[-2] == '.'))
if (len > 1 && (s[-2] == '.'))
len--;
if (len && ExpEvaluator::getOperator(str,s_instr) != OpcNone)
return 0;
return len;
}
bool JsCode::getInstruction(const char*& expr)
char JsCode::skipComments(const char*& expr, GenObject* context) const
{
XDebug(this,DebugAll,"JsCode::getInstruction '%s'",expr);
if (skipWhites(expr) == '{') {
if (!runCompile(++expr,'}'))
return false;
if (skipWhites(expr) != '}')
char c = skipWhites(expr);
while (c == '/') {
if (expr[1] == '/') {
// comment to end of line
expr+=2;
while ((c = *expr) && (c != '\r') && (c != '\n'))
expr++;
c = skipWhites(expr);
}
else if (expr[1] == '*') {
/* comment to close */
expr++;
while ((c = *expr) && (c != '*' || expr[1] != '/'))
expr++;
if (c) {
expr+=2;
c = skipWhites(expr);
}
}
else
break;
}
return c;
}
bool JsCode::preProcessInclude(const char*& expr, GenObject* context)
{
if (m_depth > 5)
return gotError("Possible recursive include");
JsParser* parser = YOBJECT(JsParser,context);
if (!parser)
return false;
char c = skipComments(expr);
if (c == '"' || c == '\'') {
char sep = c;
const char* start = ++expr;
while ((c = *expr++)) {
if (c != sep)
continue;
String str(start,expr-start-1);
DDebug(this,DebugAll,"Found include '%s'",str.safe());
parser->adjustPath(str);
m_depth++;
bool ok = parser->parseFile(str,true);
m_depth--;
return ok || gotError("Failed to include " + str);
}
expr--;
return gotError("Expecting string end");
}
return gotError("Expecting include file",expr);
}
int JsCode::preProcess(const char*& expr, GenObject* context)
{
int rval = -1;
for (;;) {
switch ((JsOpcode)ExpEvaluator::getOperator(expr,s_preProc)) {
case OpcInclude:
if (preProcessInclude(expr,context)) {
if (rval < 0)
rval = 1;
else
rval++;
}
else
return -1;
break;
default:
return rval;
}
}
}
bool JsCode::getInstruction(const char*& expr, Opcode nested)
{
if (inError())
return false;
XDebug(this,DebugAll,"JsCode::getInstruction '%s' %u",expr,nested);
if (skipComments(expr) == '{') {
expr++;
for (;;) {
if (!runCompile(expr,'}',nested))
return false;
bool sep = false;
while (skipComments(expr) && getSeparator(expr,true))
sep = true;
if (*expr == '}' || !sep)
break;
}
if (*expr != '}')
return gotError("Expecting '}'",expr);
expr++;
return true;
}
const char* saved = expr;
Opcode op = ExpEvaluator::getOperator(expr,s_instr);
switch ((JsOpcode)op) {
case (JsOpcode)OpcNone:
@ -208,14 +479,105 @@ bool JsCode::getInstruction(const char*& expr)
runCompile(expr);
addOpcode(op);
break;
case OpcIf:
if (skipComments(expr) != '(')
return gotError("Expecting '('",expr);
if (!runCompile(++expr,')'))
return false;
if (skipComments(expr) != ')')
return gotError("Expecting ')'",expr);
{
ExpOperation* cond = addOpcode((Opcode)OpcJumpFalse,++m_label);
if (!runCompile(++expr,';'))
return false;
const char* save = expr;
if ((JsOpcode)ExpEvaluator::getOperator(expr,s_instr) == OpcElse) {
ExpOperation* jump = addOpcode((Opcode)OpcJump,++m_label);
addOpcode(OpcLabel,cond->number());
if (!runCompile(++expr))
return false;
addOpcode(OpcLabel,jump->number());
}
else {
expr = save;
addOpcode(OpcLabel,cond->number());
}
}
break;
case OpcElse:
expr = saved;
return false;
case OpcWhile:
{
ExpOperation* lbl = addOpcode(OpcLabel,++m_label);
if (skipComments(expr) != '(')
return gotError("Expecting '('",expr);
if (!runCompile(++expr,')',op))
return false;
if (skipComments(expr) != ')')
return gotError("Expecting ')'",expr);
ExpOperation* jump = addOpcode((Opcode)OpcJumpFalse,++m_label);
if (!runCompile(++expr))
return false;
addOpcode((Opcode)OpcJump,lbl->number());
addOpcode(OpcLabel,jump->number());
}
break;
case OpcCase:
if (OpcSwitch != (JsOpcode)nested)
return gotError("Case not in switch",saved);
break;
case OpcDefault:
if (OpcSwitch != (JsOpcode)nested)
return gotError("Default not in switch",saved);
break;
case OpcTry:
addOpcode(op);
if (!runCompile(expr,0,op))
return false;
{
if ((JsOpcode)ExpEvaluator::getOperator(expr,s_instr) == OpcCatch) {
if (skipComments(expr) != '(')
return gotError("Expecting '('",expr);
if (!getField(++expr))
return gotError("Expecting formal argument",expr);
if (skipComments(expr) != ')')
return gotError("Expecting ')'",expr);
if (!runCompile(++expr))
return false;
}
if ((JsOpcode)ExpEvaluator::getOperator(expr,s_instr) == OpcFinally) {
if (!runCompile(expr))
return false;
}
}
default:
break;
}
return true;
}
bool JsCode::getNumber(const char*& expr)
{
if (inError())
return false;
switch ((JsOpcode)ExpEvaluator::getOperator(expr,s_bools)) {
case OpcFalse:
addOpcode(false);
return true;
case OpcTrue:
addOpcode(true);
return true;
default:
break;
}
return ExpEvaluator::getNumber(expr);
}
ExpEvaluator::Opcode JsCode::getOperator(const char*& expr)
{
if (inError())
return OpcNone;
XDebug(this,DebugAll,"JsCode::getOperator '%s'",expr);
Opcode op = ExpEvaluator::getOperator(expr,s_operators);
if (OpcNone != op)
@ -225,6 +587,8 @@ ExpEvaluator::Opcode JsCode::getOperator(const char*& expr)
ExpEvaluator::Opcode JsCode::getUnaryOperator(const char*& expr)
{
if (inError())
return OpcNone;
XDebug(this,DebugAll,"JsCode::getUnaryOperator '%s'",expr);
Opcode op = ExpEvaluator::getOperator(expr,s_unaryOps);
if (OpcNone != op)
@ -234,11 +598,13 @@ ExpEvaluator::Opcode JsCode::getUnaryOperator(const char*& expr)
ExpEvaluator::Opcode JsCode::getPostfixOperator(const char*& expr)
{
if (inError())
return OpcNone;
XDebug(this,DebugAll,"JsCode::getPostfixOperator '%s'",expr);
if (skipWhites(expr) == '[') {
if (skipComments(expr) == '[') {
if (!runCompile(++expr,']'))
return OpcNone;
if (skipWhites(expr) != ']') {
if (skipComments(expr) != ']') {
gotError("Expecting ']'",expr);
return OpcNone;
}
@ -282,7 +648,9 @@ int JsCode::getPrecedence(ExpEvaluator::Opcode oper) const
bool JsCode::getSeparator(const char*& expr, bool remove)
{
switch (skipWhites(expr)) {
if (inError())
return false;
switch (skipComments(expr)) {
case ']':
case ';':
if (remove)
@ -292,11 +660,11 @@ bool JsCode::getSeparator(const char*& expr, bool remove)
return ExpEvaluator::getSeparator(expr,remove);
}
bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, void* context) const
bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, GenObject* context) const
{
switch ((JsOpcode)oper.opcode()) {
case OpcBegin:
pushOne(stack,new ExpOperation(OpcBegin));
pushOne(stack,new ExpOperation((Opcode)OpcBegin));
break;
case OpcEnd:
{
@ -334,15 +702,56 @@ bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, void* contex
TelEngine::destruct(op2);
}
break;
case OpcTypeof:
{
ExpOperation* op = popValue(stack,context);
if (!op)
return gotError("Stack underflow");
switch (op->opcode()) {
case OpcPush:
{
const char* txt = "string";
ExpWrapper* w = YOBJECT(ExpWrapper,op);
if (w)
txt = w->object() ? "object" : "undefined";
else if (op->isInteger())
txt = "number";
pushOne(stack,new ExpOperation(txt));
}
break;
case OpcFunc:
pushOne(stack,new ExpOperation("function"));
break;
default:
pushOne(stack,new ExpOperation("internal"));
}
TelEngine::destruct(op);
}
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");
switch (op->opcode()) {
case OpcField:
break;
case OpcPush:
{
ExpWrapper* w = YOBJECT(ExpWrapper,op);
if (w && w->object()) {
pushOne(stack,op);
return true;
}
}
// fall through
default:
TelEngine::destruct(op);
return gotError("Expecting class name");
}
ExpFunction ctr(op->name(),op->number());
TelEngine::destruct(op);
return runOperation(stack,ctr,context);
}
break;
case OpcThrow:
@ -360,7 +769,7 @@ bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, void* contex
}
}
if (!ok)
return gotError("'try' not found");
return gotError("Uncaught exception: " + *op);
pushOne(stack,op);
}
break;
@ -381,27 +790,225 @@ bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, void* contex
}
if (!ok) {
TelEngine::destruct(op);
return gotError("Function not found on stack");
return gotError("Return outside function call");
}
pushOne(stack,op);
}
break;
case OpcJumpTrue:
case OpcJumpFalse:
case OpcJRelTrue:
case OpcJRelFalse:
{
ExpOperation* op = popValue(stack,context);
if (!op)
return gotError("Stack underflow");
bool val = op->number() != 0;
TelEngine::destruct(op);
switch ((JsOpcode)oper.opcode()) {
case OpcJumpTrue:
case OpcJRelTrue:
if (!val)
return true;
break;
case OpcJumpFalse:
case OpcJRelFalse:
if (val)
return true;
break;
default:
break;
}
}
// fall through
case OpcJump:
case OpcJRel:
switch ((JsOpcode)oper.opcode()) {
case OpcJump:
case OpcJumpTrue:
case OpcJumpFalse:
return jumpToLabel(oper.number(),context) || gotError("Label not found");
case OpcJRel:
case OpcJRelTrue:
case OpcJRelFalse:
return jumpRelative(oper.number(),context) || gotError("Relative jump failed");
default:
return false;
}
break;
default:
return ExpEvaluator::runOperation(stack,oper);
return ExpEvaluator::runOperation(stack,oper,context);
}
return true;
}
bool JsCode::runFunction(ObjList& stack, const ExpOperation& oper, GenObject* context) const
{
DDebug(this,DebugAll,"runFunction(%p,'%s' %ld, %p) ext=%p",
&stack,oper.name().c_str(),oper.number(),context,extender());
if (context) {
ScriptRun* sr = static_cast<ScriptRun*>(context);
if (sr->context()->runFunction(stack,oper,context))
return true;
}
return extender() && extender()->runFunction(stack,oper,context);
}
bool JsCode::runField(ObjList& stack, const ExpOperation& oper, GenObject* context) const
{
DDebug(this,DebugAll,"runField(%p,'%s',%p) ext=%p",
&stack,oper.name().c_str(),context,extender());
if (context) {
ScriptRun* sr = static_cast<ScriptRun*>(context);
if (sr->context()->runField(stack,oper,context))
return true;
}
return extender() && extender()->runField(stack,oper,context);
}
bool JsCode::runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context) const
{
DDebug(this,DebugAll,"runAssign('%s'='%s',%p) ext=%p",
oper.name().c_str(),oper.c_str(),context,extender());
if (context) {
ScriptRun* sr = static_cast<ScriptRun*>(context);
if (sr->context()->runAssign(stack,oper,context))
return true;
}
return extender() && extender()->runAssign(stack,oper,context);
}
bool JsCode::evalList(ObjList& stack, GenObject* context) const
{
XDebug(this,DebugInfo,"evalList(%p,%p)",&stack,context);
JsRunner* runner = static_cast<JsRunner*>(context);
ObjList*& opcode = runner->m_opcode;
if (!opcode)
opcode = m_opcodes.skipNull();
while (opcode) {
const ExpOperation* o = static_cast<const ExpOperation*>(opcode->get());
opcode = opcode->skipNext();
if (!runOperation(stack,*o,context))
return false;
if (runner->m_paused)
break;
}
return true;
}
bool JsCode::evalVector(ObjList& stack, GenObject* context) const
{
XDebug(this,DebugInfo,"evalVector(%p,%p)",&stack,context);
JsRunner* runner = static_cast<JsRunner*>(context);
unsigned int& index = runner->m_index;
while (index < m_linked.length()) {
const ExpOperation* o = static_cast<const ExpOperation*>(m_linked[index++]);
if (o && !runOperation(stack,*o,context))
return false;
if (runner->m_paused)
break;
}
return true;
}
bool JsCode::jumpToLabel(long int label, GenObject* context) const
{
if (!context)
return false;
JsRunner* runner = static_cast<JsRunner*>(context);
for (ObjList* l = m_opcodes.skipNull(); l; l = l->skipNext()) {
const ExpOperation* o = static_cast<const ExpOperation*>(l->get());
if (o->opcode() == OpcLabel && o->number() == label) {
runner->m_opcode = l;
return true;
}
}
return false;
}
bool JsCode::jumpRelative(long int offset, GenObject* context) const
{
if (!context)
return false;
unsigned int& index = static_cast<JsRunner*>(context)->m_index;
long int i = index + offset;
if (i < 0 || i > m_linked.length())
return false;
index = i;
return true;
}
ScriptRun::Status JsRunner::reset()
{
Status s = ScriptRun::reset();
m_opcode = 0;
m_index = 0;
return s;
}
ScriptRun::Status JsRunner::resume()
{
Lock mylock(this);
if (Running != state())
return state();
RefPointer<ScriptCode> c = code();
if (!(c && context()))
return Invalid;
m_paused = false;
mylock.drop();
if (!c->evaluate(*this,stack()))
return Failed;
return m_paused ? Incomplete : Succeeded;
}
}; // anonymous namespace
// Adjust a script file include path
void JsParser::adjustPath(String& script)
{
if (script.null() || script.startsWith(Engine::pathSeparator()))
return;
script = m_basePath + script;
}
// Create Javascript context
ScriptContext* JsParser::createContext() const
{
return new JsContext;
}
ScriptRun* JsParser::createRunner(ScriptCode* code, ScriptContext* context) const
{
if (!code)
return 0;
ScriptContext* ctxt = 0;
if (!context)
context = ctxt = createContext();
ScriptRun* runner = new JsRunner(code,context);
TelEngine::destruct(ctxt);
return runner;
}
// Parse a piece of Javascript text
bool JsParser::parse(const char* text)
bool JsParser::parse(const char* text, bool fragment)
{
if (TelEngine::null(text))
return false;
// TODO
return false;
if (fragment)
return code() && static_cast<JsCode*>(code())->compile(text,this);
JsCode* code = new JsCode;
setCode(code);
code->deref();
if (!code->compile(text,this)) {
setCode(0);
return false;
}
DDebug(DebugAll,"Compiled: %s",code->dump().c_str());
code->simplify();
DDebug(DebugAll,"Simplified: %s",code->dump().c_str());
return true;
}
// Evaluate a string as expression or statement
@ -409,21 +1016,10 @@ ScriptRun::Status JsParser::eval(const String& text, ExpOperation** result, Scri
{
if (TelEngine::null(text))
return ScriptRun::Invalid;
JsCode* code = new JsCode;
if (!code->compile(text)) {
TelEngine::destruct(code);
JsParser parser;
if (!parser.parse(text))
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* runner = parser.createRunner(context);
ScriptRun::Status rval = runner->run();
if (result && (ScriptRun::Succeeded == rval))
*result = ExpEvaluator::popOne(runner->stack());

View File

@ -32,9 +32,8 @@ class JsNative : public JsObject
{
YCLASS(JsNative,JsObject)
public:
inline JsNative(const char* name, NamedList* list)
: JsObject(name),
m_list(list)
inline JsNative(const char* name, NamedList* list, Mutex* mtx)
: JsObject(name,mtx), m_list(list)
{ }
virtual NamedList& list()
{ return *m_list; }
@ -49,18 +48,8 @@ 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")
inline JsArray(Mutex* mtx)
: JsObject("Array",mtx)
{ }
};
@ -69,20 +58,46 @@ class JsConstructor : public JsFunction
{
YCLASS(JsConstructor,JsFunction)
public:
inline JsConstructor()
inline JsConstructor(Mutex* mtx)
: JsFunction(mtx)
{ }
};
// Object object
class JsObjectObj : public JsObject
{
YCLASS(JsObjectObj,JsObject)
public:
inline JsObjectObj(Mutex* mtx)
: JsObject("Object",mtx,true)
{
params().addParam(new ExpFunction("constructor"));
}
protected:
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
};
// Date object
class JsDate : public JsObject
{
YCLASS(JsDate,JsObject)
public:
inline JsDate()
: JsObject("Date")
inline JsDate(Mutex* mtx)
: JsObject("Date",mtx,true)
{
addParam(new ExpOperation(ExpEvaluator::OpcFunc,"now"));
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"));
}
protected:
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
};
// Math class - not really an object, all methods are static
@ -90,36 +105,232 @@ class JsMath : public JsObject
{
YCLASS(JsMath,JsObject)
public:
inline JsMath()
: JsObject("Math")
inline JsMath(Mutex* mtx)
: JsObject("Math",mtx,true)
{
addParam(new ExpOperation(ExpEvaluator::OpcFunc,"abs"));
params().addParam(new ExpFunction("abs"));
params().addParam(new ExpFunction("max"));
params().addParam(new ExpFunction("min"));
}
protected:
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
};
}; // anonymous namespace
// Helper function that adds an object to a parent
static inline void addObject(NamedList& params, const char* name, NamedList* obj)
static inline void addObject(NamedList& params, const char* name, JsObject* obj)
{
params.addParam(new NamedPointer(name,obj,obj->toString()));
}
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"));
}
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()));
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;
}
// Initialize standard globals in the execution context
void JsObject::initialize(ScriptContext& 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);
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));
if (!p.getParam(YSTRING("Date")))
addObject(p,"Date",new JsDate(mtx));
if (!p.getParam(YSTRING("Math")))
addObject(p,"Math",new JsMath(mtx));
if (!p.getParam(YSTRING("isNaN")))
p.addParam(new ExpFunction("isNaN"));
}
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;
}
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);
}
bool JsFunction::runDefined(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
return false;
}
/* vi: set ts=8 sw=4 sts=4 noet: */

View File

@ -25,11 +25,46 @@
using namespace TelEngine;
namespace { // anonymous
class BasicContext: public ScriptContext, public Mutex
{
YCLASS(BasicContext,ScriptContext)
public:
inline explicit BasicContext()
: Mutex(true,"BasicContext")
{ }
virtual Mutex* mutex()
{ return this; }
};
}; // anonymous namespace
ScriptParser::~ScriptParser()
{
TelEngine::destruct(m_code);
}
bool ScriptParser::parseFile(const char* name, bool fragment)
{
if (TelEngine::null(name))
return false;
XDebug(DebugAll,"Opening script '%s'",name);
File f;
if (!f.openPath(name))
return false;
int64_t len = f.length();
if (len <= 0 || len > 65535)
return false;
DataBlock data(0,len+1);
char* text = (char*)data.data();
if (f.readData(text,len) != len)
return false;
text[len] = '\0';
return parse(text,fragment);
}
void ScriptParser::setCode(ScriptCode* code)
{
ScriptCode* tmp = m_code;
@ -41,25 +76,56 @@ void ScriptParser::setCode(ScriptCode* code)
TelEngine::destruct(tmp);
}
ScriptContext* ScriptParser::createContext() const
{
return new BasicContext;
}
bool ScriptContext::runFunction(const ExpEvaluator* eval, ObjList& stack, const ExpOperation& oper, void* context)
ScriptRun* ScriptParser::createRunner(ScriptCode* code, ScriptContext* context) const
{
if (!code)
return 0;
ScriptContext* ctxt = 0;
if (!context)
context = ctxt = createContext();
ScriptRun* runner = new ScriptRun(code,context);
TelEngine::destruct(ctxt);
return runner;
}
// RTTI Interface access
void* ScriptContext::getObject(const String& name) const
{
if (name == YSTRING("ExpExtender"))
return const_cast<ExpExtender*>(static_cast<const ExpExtender*>(this));
return RefObject::getObject(name);
}
bool ScriptContext::hasField(ObjList& stack, const String& name, GenObject* context) const
{
return m_params.getParam(name) != 0;
}
NamedString* ScriptContext::getField(ObjList& stack, const String& name, GenObject* context) const
{
return m_params.getParam(name);
}
bool ScriptContext::runFunction(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
return false;
}
bool ScriptContext::runField(const ExpEvaluator* eval, ObjList& stack, const ExpOperation& oper, void* context)
bool ScriptContext::runField(ObjList& stack, const ExpOperation& oper, GenObject* 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()));
ExpEvaluator::pushOne(stack,new ExpOperation(m_params[oper.name()],oper.name(),true));
return true;
}
bool ScriptContext::runAssign(const ExpEvaluator* eval, const ExpOperation& oper, void* context)
bool ScriptContext::runAssign(ObjList& stack, const ExpOperation& oper, GenObject* 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;
@ -80,19 +146,21 @@ ScriptRun::ScriptRun(ScriptCode* code, ScriptContext* context)
: Mutex(true,"ScriptRun"),
m_state(Invalid)
{
XDebug(DebugAll,"ScriptRun::ScriptRun(%p,%p) [%p]",code,context,this);
if (code)
code->ref();
m_code = code;
if (context)
context->ref();
else
context = new ScriptContext;
context = new BasicContext;
m_context = context;
reset();
}
ScriptRun::~ScriptRun()
{
XDebug(DebugAll,"ScriptRun::~ScriptRun [%p]",this);
lock();
m_state = Invalid;
TelEngine::destruct(m_code);
@ -121,11 +189,10 @@ ScriptRun::Status ScriptRun::resume()
if (Running != m_state)
return m_state;
RefPointer<ScriptCode> code = m_code;
RefPointer<ScriptContext> ctxt = m_context;
if (!(code && ctxt))
if (!(code && context()))
return Invalid;
mylock.drop();
return code->evaluate(*ctxt,stack()) ? Succeeded : Failed;
return code->evaluate(*this,stack()) ? Succeeded : Failed;
}
// Execute one or more instructions of code from where it was left
@ -156,4 +223,17 @@ ScriptRun::Status ScriptRun::run()
return s;
}
// Execute an assignment operation
bool ScriptRun::runAssign(const ExpOperation& oper, GenObject* context)
{
Lock mylock(this);
if (Invalid == m_state || !m_code || !m_context)
return false;
RefPointer<ScriptContext> ctxt = m_context;
mylock.drop();
ObjList noStack;
Lock ctxLock(ctxt->mutex());
return ctxt->runAssign(noStack,oper,context);
}
/* vi: set ts=8 sw=4 sts=4 noet: */

File diff suppressed because it is too large Load Diff

View File

@ -369,9 +369,9 @@ server/sipfeatures.yate: ../libs/yxml/libyatexml.a
server/sipfeatures.yate: LOCALFLAGS = -I@top_srcdir@/libs/yxml
server/sipfeatures.yate: LOCALLIBS = -L../libs/yxml -lyatexml
javascript.yate: ../libyatescript.so
javascript.yate: LOCALFLAGS = -I@top_srcdir@/libs/yscript
javascript.yate: LOCALLIBS = -lyatescript
javascript.yate: ../libyatescript.so ../libs/ypbx/libyatepbx.a
javascript.yate: LOCALFLAGS = -I@top_srcdir@/libs/yscript -I@top_srcdir@/libs/ypbx
javascript.yate: LOCALLIBS = -lyatescript -L../libs/ypbx -lyatepbx
zlibcompress.yate: LOCALFLAGS = $(ZLIB_INC)
zlibcompress.yate: LOCALLIBS = $(ZLIB_LIB)

View File

@ -2,7 +2,7 @@
* javascript.cpp
* This file is part of the YATE Project http://YATE.null.ro
*
* Javascript support based on libyscript
* Javascript channel support based on libyscript
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2011 Null Team
@ -22,26 +22,128 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <yatephone.h>
#include <yatepbx.h>
#include <yatescript.h>
using namespace TelEngine;
namespace { // anonymous
class JavascriptModule : public Module
class JsModule : public ChanAssistList
{
public:
JavascriptModule();
~JavascriptModule();
enum {
Preroute = AssistPrivate
};
JsModule();
virtual ~JsModule();
virtual void initialize();
virtual void init(int priority);
virtual ChanAssist* create(Message& msg, const String& id);
bool unload();
virtual bool received(Message& msg, int id);
virtual bool received(Message& msg, int id, ChanAssist* assist);
inline JsParser& parser()
{ return m_assistCode; }
protected:
virtual bool commandExecute(String& retVal, const String& line);
virtual bool commandComplete(Message& msg, const String& partLine, const String& partWord);
private:
JsParser m_assistCode;
};
class JsAssist : public ChanAssist
{
public:
inline JsAssist(ChanAssistList* list, const String& id, ScriptRun* runner)
: ChanAssist(list, id), m_runner(runner)
{ }
virtual ~JsAssist();
virtual void msgStartup(Message& msg);
virtual void msgHangup(Message& msg);
virtual void msgExecute(Message& msg);
virtual bool msgPreroute(Message& msg);
virtual bool msgRoute(Message& msg);
virtual bool msgDisconnect(Message& msg, const String& reason);
bool init();
private:
bool runFunction(const char* name, Message& msg);
ScriptRun* m_runner;
};
INIT_PLUGIN(JavascriptModule);
#define MKDEBUG(lvl) params().addParam(new ExpOperation((long int)Debug ## lvl,"Debug" # lvl))
class JsEngine : public JsObject
{
YCLASS(JsEngine,JsObject)
public:
inline JsEngine(Mutex* mtx)
: JsObject("Engine",mtx,true)
{
MKDEBUG(Fail);
MKDEBUG(GoOn);
MKDEBUG(Conf);
MKDEBUG(Stub);
MKDEBUG(Warn);
MKDEBUG(Mild);
MKDEBUG(Call);
MKDEBUG(Note);
MKDEBUG(Info);
MKDEBUG(All);
params().addParam(new ExpFunction("Output"));
params().addParam(new ExpFunction("Debug"));
}
static void initialize(ScriptContext* context);
protected:
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
};
#undef MKDEBUG
class JsMessage : public JsObject
{
YCLASS(JsMessage,JsObject)
public:
inline JsMessage(Mutex* mtx)
: JsObject("Message",mtx,true), m_message(0)
{
XDebug(DebugAll,"JsMessage::JsMessage() [%p]",this);
params().addParam(new ExpFunction("constructor"));
params().addParam(new ExpFunction("enqueue"));
params().addParam(new ExpFunction("dispatch"));
}
inline JsMessage(Message* message, Mutex* mtx)
: JsObject("Message",mtx), m_message(message)
{
XDebug(DebugAll,"JsMessage::JsMessage(%p) [%p]",message,this);
params().addParam(new ExpFunction("broadcast"));
params().addParam(new ExpFunction("acknowledge"));
}
virtual ~JsMessage()
{
XDebug(DebugAll,"JsMessage::~JsMessage() [%p]",this);
}
static void initialize(ScriptContext* context);
protected:
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
Message* m_message;
};
class JsChannel : public JsObject
{
YCLASS(JsChannel,JsObject)
public:
inline JsChannel(JsAssist* assist, Mutex* mtx)
: JsObject("Channel",mtx,true), m_assist(assist)
{
params().addParam(new ExpFunction("id"));
}
static void initialize(ScriptContext* context, JsAssist* assist);
protected:
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
JsAssist* m_assist;
};
static String s_basePath;
INIT_PLUGIN(JsModule);
UNLOAD_PLUGIN(unloadNow)
{
@ -50,35 +152,226 @@ UNLOAD_PLUGIN(unloadNow)
return true;
}
JavascriptModule::JavascriptModule()
: Module("javascript","misc",true)
// Helper function that adds an object to a parent
static inline void addObject(NamedList& params, const char* name, JsObject* obj)
{
params.addParam(new NamedPointer(name,obj,obj->toString()));
}
bool JsEngine::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
if (oper.name() == YSTRING("Output")) {
String str;
for (long int i = oper.number(); i; i--) {
ExpOperation* op = popValue(stack,context);
if (str)
str = *op + " " + str;
else
str = *op;
}
if (str)
Output("%s",str.c_str());
}
else if (oper.name() == YSTRING("Debug")) {
int level = DebugNote;
String str;
for (long int i = oper.number(); i; i--) {
ExpOperation* op = popValue(stack,context);
if ((i == 1) && oper.number() > 1 && op->isInteger())
level = op->number();
else {
if (str)
str = *op + " " + str;
else
str = *op;
}
}
if (str) {
if (level > DebugAll)
level = DebugAll;
else if (level < DebugGoOn)
level = DebugGoOn;
Debug(&__plugin,level,"%s",str.c_str());
}
}
else
return JsObject::runNative(stack,oper,context);
return true;
}
void JsEngine::initialize(ScriptContext* context)
{
if (!context)
return;
Mutex* mtx = context->mutex();
Lock mylock(mtx);
NamedList& params = context->params();
if (!params.getParam(YSTRING("Engine")))
addObject(params,"Engine",new JsEngine(mtx));
}
bool JsMessage::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
if (oper.name() == YSTRING("constructor")) {
ExpEvaluator::pushOne(stack,new ExpWrapper(new JsMessage(mutex())));
}
else
return JsObject::runNative(stack,oper,context);
return true;
}
void JsMessage::initialize(ScriptContext* context)
{
if (!context)
return;
Mutex* mtx = context->mutex();
Lock mylock(mtx);
NamedList& params = context->params();
if (!params.getParam(YSTRING("Message")))
addObject(params,"Message",new JsMessage(mtx));
}
bool JsChannel::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
if (oper.name() == YSTRING("id")) {
for (long int i = oper.number(); i; i--) {
TelEngine::destruct(ExpEvaluator::popOne(stack));
}
RefPointer<JsAssist> ja = m_assist;
if (ja)
ExpEvaluator::pushOne(stack,new ExpOperation(ja->id()));
else
return false;
}
else
return JsObject::runNative(stack,oper,context);
return true;
}
void JsChannel::initialize(ScriptContext* context, JsAssist* assist)
{
if (!context)
return;
Mutex* mtx = context->mutex();
Lock mylock(mtx);
NamedList& params = context->params();
if (!params.getParam(YSTRING("Channel")))
addObject(params,"Channel",new JsChannel(assist,mtx));
}
JsAssist::~JsAssist()
{
TelEngine::destruct(m_runner);
}
bool JsAssist::init()
{
if (!m_runner)
return false;
JsObject::initialize(m_runner->context());
JsEngine::initialize(m_runner->context());
JsChannel::initialize(m_runner->context(),this);
JsMessage::initialize(m_runner->context());
ScriptRun::Status rval = m_runner->run();
m_runner->reset();
return (ScriptRun::Succeeded == rval);
}
bool JsAssist::runFunction(const char* name, Message& msg)
{
if (!m_runner)
return false;
DDebug(&__plugin,DebugInfo,"Running function %s in '%s'",name,id().c_str());
ScriptRun* runner = __plugin.parser().createRunner(m_runner->code(),m_runner->context());
JsMessage* jm = new JsMessage(&msg,m_runner->context()->mutex());
ExpWrapper mw(jm,"message");
runner->runAssign(mw);
ScriptRun::Status rval = runner->run();
TelEngine::destruct(runner);
return (ScriptRun::Succeeded == rval);
}
void JsAssist::msgStartup(Message& msg)
{
runFunction("onStartup",msg);
}
void JsAssist::msgHangup(Message& msg)
{
runFunction("onHangup",msg);
}
void JsAssist::msgExecute(Message& msg)
{
runFunction("onExecute",msg);
}
bool JsAssist::msgPreroute(Message& msg)
{
return false;
}
bool JsAssist::msgRoute(Message& msg)
{
return false;
}
bool JsAssist::msgDisconnect(Message& msg, const String& reason)
{
return false;
}
JsModule::JsModule()
: ChanAssistList("javascript",true)
{
Output("Loaded module Javascript");
}
JavascriptModule::~JavascriptModule()
JsModule::~JsModule()
{
Output("Unloading module Javascript");
}
bool JavascriptModule::commandExecute(String& retVal, const String& line)
bool JsModule::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";
JsParser parser;
parser.basePath(s_basePath);
if (!parser.parse(cmd)) {
retVal << "parsing failed\r\n";
return true;
}
ScriptRun* runner = parser.createRunner();
JsObject::initialize(runner->context());
JsEngine::initialize(runner->context());
JsMessage::initialize(runner->context());
ScriptRun::Status st = runner->run();
if (st == ScriptRun::Succeeded) {
while (ExpOperation* op = ExpEvaluator::popOne(runner->stack())) {
retVal << "'" << op->name() << "'='" << *op << "'\r\n";
TelEngine::destruct(op);
}
}
else
retVal << ScriptRun::textState(st) << "\r\n";
TelEngine::destruct(rval);
TelEngine::destruct(runner);
return true;
}
bool JavascriptModule::commandComplete(Message& msg, const String& partLine, const String& partWord)
bool JsModule::commandComplete(Message& msg, const String& partLine, const String& partWord)
{
if (partLine.null() && partWord.null())
return false;
@ -87,16 +380,105 @@ bool JavascriptModule::commandComplete(Message& msg, const String& partLine, con
return Module::commandComplete(msg,partLine,partWord);
}
bool JavascriptModule::unload()
bool JsModule::received(Message& msg, int id)
{
String* chanId = msg.getParam("id");
if (!TelEngine::null(chanId)) {
switch (id) {
case Preroute:
case Route:
{
Lock mylock(this);
RefPointer <JsAssist> ca = static_cast<JsAssist*>(find(*chanId));
switch (id) {
case Preroute:
if (ca) {
mylock.drop();
return ca->msgPreroute(msg);
}
ca = static_cast<JsAssist*>(create(msg,*chanId));
if (ca) {
calls().append(ca);
mylock.drop();
ca->msgStartup(msg);
return ca->msgPreroute(msg);
}
return false;
case Route:
if (ca) {
mylock.drop();
return ca->msgRoute(msg);
}
ca = static_cast<JsAssist*>(create(msg,*chanId));
if (ca) {
calls().append(ca);
mylock.drop();
ca->msgStartup(msg);
return ca->msgRoute(msg);
}
return false;
}
}
}
}
return ChanAssistList::received(msg,id);
}
bool JsModule::received(Message& msg, int id, ChanAssist* assist)
{
return ChanAssistList::received(msg,id,assist);
}
ChanAssist* JsModule::create(Message& msg, const String& id)
{
lock();
ScriptRun* runner = m_assistCode.createRunner();
unlock();
if (!runner)
return 0;
DDebug(this,DebugAll,"Creating Javascript for '%s'",id.c_str());
JsAssist* ca = new JsAssist(this,id,runner);
if (ca->init())
return ca;
TelEngine::destruct(ca);
return 0;
}
bool JsModule::unload()
{
uninstallRelays();
return true;
}
void JavascriptModule::initialize()
void JsModule::initialize()
{
Output("Initializing module Javascript");
ChanAssistList::initialize();
setup();
Configuration cfg(Engine::configFile("javascript"));
String tmp = Engine::sharedPath();
tmp << Engine::pathSeparator() << "scripts";
tmp = cfg.getValue("general","scripts_dir",tmp);
if (!tmp.endsWith(Engine::pathSeparator()))
tmp += Engine::pathSeparator();
s_basePath = tmp;
lock();
m_assistCode.clear();
m_assistCode.basePath(tmp);
tmp = cfg.getValue("scripts","routing");
m_assistCode.adjustPath(tmp);
if (m_assistCode.parseFile(tmp))
Debug(this,DebugInfo,"Parsed routing script: %s",tmp.c_str());
else if (tmp)
Debug(this,DebugWarn,"Failed to parse script: %s",tmp.c_str());
unlock();
}
void JsModule::init(int priority)
{
ChanAssistList::init(priority);
installRelay(Route,priority);
Engine::install(new MessageRelay("call.preroute",this,Preroute,priority));
}
}; // anonymous namespace