Updated scripting code.
git-svn-id: http://yate.null.ro/svn/yate/trunk@4921 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
6e8889f068
commit
a242f792c5
|
@ -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=
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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: */
|
||||
|
|
|
@ -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
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue