Added native JSON parser and stringifier.
git-svn-id: http://voip.null.ro/svn/yate@5869 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
08e8bec9e3
commit
d92a383e0f
|
@ -174,8 +174,8 @@ public:
|
|||
bool link();
|
||||
inline bool traceable() const
|
||||
{ return m_traceable; }
|
||||
JsObject* parseArray(ParsePoint& expr, bool constOnly);
|
||||
JsObject* parseObject(ParsePoint& expr, bool constOnly);
|
||||
JsObject* parseArray(ParsePoint& expr, bool constOnly, Mutex* mtx);
|
||||
JsObject* parseObject(ParsePoint& expr, bool constOnly, Mutex* mtx);
|
||||
inline const NamedList& pragmas() const
|
||||
{ return m_pragmas; }
|
||||
inline static unsigned int getLineNo(unsigned int line)
|
||||
|
@ -227,6 +227,7 @@ private:
|
|||
bool parseVar(ParsePoint& expr);
|
||||
bool parseTry(ParsePoint& expr, GenObject* nested);
|
||||
bool parseFuncDef(ParsePoint& expr, bool publish);
|
||||
bool parseSimple(ParsePoint& expr, bool constOnly, Mutex* mtx = 0);
|
||||
bool evalList(ObjList& stack, GenObject* context) const;
|
||||
bool evalVector(ObjList& stack, GenObject* context) const;
|
||||
bool jumpToLabel(long int label, GenObject* context) const;
|
||||
|
@ -1908,10 +1909,15 @@ bool JsCode::getSeparator(ParsePoint& expr, bool remove)
|
|||
}
|
||||
|
||||
bool JsCode::getSimple(ParsePoint& expr, bool constOnly)
|
||||
{
|
||||
return parseSimple(expr,constOnly);
|
||||
}
|
||||
|
||||
bool JsCode::parseSimple(ParsePoint& expr, bool constOnly, Mutex* mtx)
|
||||
{
|
||||
if (inError())
|
||||
return false;
|
||||
XDebug(this,DebugAll,"JsCode::getSimple(%s) '%.30s'",String::boolText(constOnly),(const char*)expr);
|
||||
XDebug(this,DebugAll,"JsCode::parseSimple(%s) '%.30s'",String::boolText(constOnly),(const char*)expr);
|
||||
skipComments(expr);
|
||||
ParsePoint save = expr;
|
||||
switch ((JsOpcode)ExpEvaluator::getOperator(expr,s_constants)) {
|
||||
|
@ -1936,9 +1942,9 @@ bool JsCode::getSimple(ParsePoint& expr, bool constOnly)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
JsObject* jso = parseArray(expr,constOnly);
|
||||
JsObject* jso = parseArray(expr,constOnly,mtx);
|
||||
if (!jso)
|
||||
jso = parseObject(expr,constOnly);
|
||||
jso = parseObject(expr,constOnly,mtx);
|
||||
if (!jso)
|
||||
return ExpEvaluator::getSimple(expr,constOnly);
|
||||
addOpcode(new ExpWrapper(ExpEvaluator::OpcCopy,jso));
|
||||
|
@ -1946,12 +1952,12 @@ bool JsCode::getSimple(ParsePoint& expr, bool constOnly)
|
|||
}
|
||||
|
||||
// Parse an inline Javascript Array: [ item1, item2, ... ]
|
||||
JsObject* JsCode::parseArray(ParsePoint& expr, bool constOnly)
|
||||
JsObject* JsCode::parseArray(ParsePoint& expr, bool constOnly, Mutex* mtx)
|
||||
{
|
||||
if (skipComments(expr) != '[')
|
||||
return 0;
|
||||
expr++;
|
||||
JsArray* jsa = new JsArray(0,"[object Array]");
|
||||
JsArray* jsa = new JsArray(mtx,"[object Array]");
|
||||
for (bool first = true; ; first = false) {
|
||||
if (skipComments(expr) == ']') {
|
||||
expr++;
|
||||
|
@ -1986,7 +1992,7 @@ JsObject* JsCode::parseArray(ParsePoint& expr, bool constOnly)
|
|||
jsa->push(new ExpWrapper(0,"undefined"));
|
||||
continue;
|
||||
}
|
||||
bool ok = constOnly ? getSimple(expr,true) : getOperand(expr,false);
|
||||
bool ok = constOnly ? parseSimple(expr,true,mtx) : getOperand(expr,false);
|
||||
if (!ok) {
|
||||
TelEngine::destruct(jsa);
|
||||
break;
|
||||
|
@ -2001,12 +2007,12 @@ JsObject* JsCode::parseArray(ParsePoint& expr, bool constOnly)
|
|||
|
||||
|
||||
// Parse an inline Javascript Object: { prop1: value1, "prop 2": value2, ... }
|
||||
JsObject* JsCode::parseObject(ParsePoint& expr, bool constOnly)
|
||||
JsObject* JsCode::parseObject(ParsePoint& expr, bool constOnly, Mutex* mtx)
|
||||
{
|
||||
if (skipComments(expr) != '{')
|
||||
return 0;
|
||||
expr++;
|
||||
JsObject* jso = new JsObject(0,"[object Object]");
|
||||
JsObject* jso = new JsObject(mtx,"[object Object]");
|
||||
for (bool first = true; ; first = false) {
|
||||
if (skipComments(expr) == '}') {
|
||||
expr++;
|
||||
|
@ -2040,7 +2046,7 @@ JsObject* JsCode::parseObject(ParsePoint& expr, bool constOnly)
|
|||
break;
|
||||
}
|
||||
expr++;
|
||||
bool ok = constOnly ? getSimple(expr,true) : getOperand(expr,false);
|
||||
bool ok = constOnly ? parseSimple(expr,true,mtx) : getOperand(expr,false);
|
||||
if (!ok) {
|
||||
TelEngine::destruct(jso);
|
||||
break;
|
||||
|
@ -3505,13 +3511,17 @@ ScriptRun::Status JsParser::eval(const String& text, ExpOperation** result, Scri
|
|||
}
|
||||
|
||||
// Parse JSON using native methods
|
||||
JsObject* JsParser::parseJSON(const char* text)
|
||||
ExpOperation* JsParser::parseJSON(const char* text, Mutex* mtx)
|
||||
{
|
||||
if (!text)
|
||||
return 0;
|
||||
ExpOperation* ret = 0;
|
||||
JsCode* code = new JsCode;
|
||||
ParsePoint pp(text,code);
|
||||
JsObject* jso = code->parseObject(pp,true);
|
||||
if (code->parseSimple(pp,true,mtx))
|
||||
ret = code->popOpcode();
|
||||
TelEngine::destruct(code);
|
||||
return jso;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Return a "null" object wrapper
|
||||
|
|
|
@ -2545,9 +2545,10 @@ public:
|
|||
/**
|
||||
* Parse a complete block of JSON text
|
||||
* @param text JSON text to parse
|
||||
* @return JsObject holding the content of JSON, must be dereferenced after use, NULL if parse error
|
||||
* @param mtx Pointer to the mutex that serializes this object
|
||||
* @return ExpOperation holding the content of JSON, must be dereferenced after use, NULL if parse error
|
||||
*/
|
||||
static JsObject* parseJSON(const char* text);
|
||||
static ExpOperation* parseJSON(const char* text, Mutex* mtx = 0);
|
||||
|
||||
/**
|
||||
* Get a "null" object wrapper that will identity match another "null"
|
||||
|
|
|
@ -575,6 +575,26 @@ private:
|
|||
Hasher* m_hasher;
|
||||
};
|
||||
|
||||
class JsJSON : public JsObject
|
||||
{
|
||||
YCLASS(JsJSON,JsObject)
|
||||
public:
|
||||
inline JsJSON(Mutex* mtx)
|
||||
: JsObject("JSON",mtx,true)
|
||||
{
|
||||
params().addParam(new ExpFunction("parse"));
|
||||
params().addParam(new ExpFunction("stringify"));
|
||||
params().addParam(new ExpFunction("loadFile"));
|
||||
params().addParam(new ExpFunction("saveFile"));
|
||||
}
|
||||
static void initialize(ScriptContext* context);
|
||||
protected:
|
||||
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
|
||||
static ExpOperation* stringify(const ExpOperation* oper, int spaces);
|
||||
static void stringify(const NamedString* ns, String& buf, int spaces, int indent = 0);
|
||||
static String strEscape(const char* str);
|
||||
};
|
||||
|
||||
class JsChannel : public JsObject
|
||||
{
|
||||
YCLASS(JsChannel,JsObject)
|
||||
|
@ -2545,6 +2565,200 @@ void JsXML::initialize(ScriptContext* context)
|
|||
addConstructor(params,"XML",new JsXML(mtx));
|
||||
}
|
||||
|
||||
|
||||
bool JsJSON::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
||||
{
|
||||
ObjList args;
|
||||
if (oper.name() == YSTRING("parse")) {
|
||||
if (extractArgs(stack,oper,context,args) != 1)
|
||||
return false;
|
||||
ExpOperation* op = JsParser::parseJSON(static_cast<ExpOperation*>(args[0])->c_str(),mutex());
|
||||
if (!op)
|
||||
op = new ExpWrapper(0,"JSON");
|
||||
ExpEvaluator::pushOne(stack,op);
|
||||
}
|
||||
else if (oper.name() == YSTRING("stringify")) {
|
||||
if (extractArgs(stack,oper,context,args) < 1)
|
||||
return false;
|
||||
int spaces = args[2] ? static_cast<ExpOperation*>(args[2])->number() : 0;
|
||||
ExpOperation* op = stringify(static_cast<ExpOperation*>(args[0]),spaces);
|
||||
if (!op)
|
||||
op = new ExpWrapper(0,"JSON");
|
||||
ExpEvaluator::pushOne(stack,op);
|
||||
}
|
||||
else if (oper.name() == YSTRING("loadFile")) {
|
||||
if (extractArgs(stack,oper,context,args) != 1)
|
||||
return false;
|
||||
ExpOperation* op = 0;
|
||||
ExpOperation* file = static_cast<ExpOperation*>(args[0]);
|
||||
if (!TelEngine::null(file)) {
|
||||
File f;
|
||||
if (f.openPath(*file)) {
|
||||
int64_t len = f.length();
|
||||
if (len > 0 && len <= 65536) {
|
||||
DataBlock buf(0,len + 1);
|
||||
char* text = (char*)buf.data();
|
||||
if (f.readData(text,len) == len) {
|
||||
text[len] = '\0';
|
||||
op = JsParser::parseJSON(text,mutex());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!op)
|
||||
op = new ExpWrapper(0,"JSON");
|
||||
ExpEvaluator::pushOne(stack,op);
|
||||
}
|
||||
else if (oper.name() == YSTRING("saveFile")) {
|
||||
if (extractArgs(stack,oper,context,args) < 2)
|
||||
return false;
|
||||
ExpOperation* file = static_cast<ExpOperation*>(args[0]);
|
||||
bool ok = !TelEngine::null(file);
|
||||
if (ok) {
|
||||
ok = false;
|
||||
int spaces = args[2] ? static_cast<ExpOperation*>(args[2])->number() : 0;
|
||||
ExpOperation* op = stringify(static_cast<ExpOperation*>(args[1]),spaces);
|
||||
if (op) {
|
||||
File f;
|
||||
if (f.openPath(*file,true,false,true)) {
|
||||
int len = op->length();
|
||||
ok = f.writeData(op->c_str(),len) == len;
|
||||
}
|
||||
}
|
||||
TelEngine::destruct(op);
|
||||
}
|
||||
ExpEvaluator::pushOne(stack,new ExpOperation(ok));
|
||||
}
|
||||
else
|
||||
return JsObject::runNative(stack,oper,context);
|
||||
return true;
|
||||
}
|
||||
|
||||
ExpOperation* JsJSON::stringify(const ExpOperation* oper, int spaces)
|
||||
{
|
||||
if (!oper)
|
||||
return 0;
|
||||
if (spaces < 0)
|
||||
spaces = 0;
|
||||
else if (spaces > 10)
|
||||
spaces = 10;
|
||||
ExpOperation* ret = new ExpOperation("","JSON");
|
||||
stringify(oper,*ret,spaces);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void JsJSON::stringify(const NamedString* ns, String& buf, int spaces, int indent)
|
||||
{
|
||||
const ExpOperation* oper = YOBJECT(ExpOperation,ns);
|
||||
if (!oper) {
|
||||
if (ns)
|
||||
buf << strEscape(*ns);
|
||||
else
|
||||
buf << "null";
|
||||
return;
|
||||
}
|
||||
if (JsParser::isNull(*oper) || JsParser::isUndefined(*oper)) {
|
||||
buf << "null";
|
||||
return;
|
||||
}
|
||||
const char* nl = spaces ? "\r\n" : "";
|
||||
JsObject* jso = YOBJECT(JsObject,oper);
|
||||
JsArray* jsa = YOBJECT(JsArray,jso);
|
||||
if (jsa) {
|
||||
if (jsa->length() <= 0) {
|
||||
buf << "[]";
|
||||
return;
|
||||
}
|
||||
String li(' ',indent);
|
||||
String ci(' ',indent + spaces);
|
||||
buf << "[" << nl;
|
||||
for (int32_t i = 0; ; ) {
|
||||
const NamedString* p = jsa->params().getParam(String(i));
|
||||
if (!p)
|
||||
continue;
|
||||
buf << ci;
|
||||
stringify(p,buf,spaces,indent + spaces);
|
||||
if (++i < jsa->length())
|
||||
buf << "," << nl;
|
||||
else {
|
||||
buf << nl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
buf << li << "]";
|
||||
return;
|
||||
}
|
||||
if (jso) {
|
||||
switch (jso->params().count()) {
|
||||
case 1:
|
||||
if (!jso->params().getParam(protoName()))
|
||||
break;
|
||||
// fall through
|
||||
case 0:
|
||||
buf << "{}";
|
||||
return;
|
||||
}
|
||||
ObjList* l = jso->params().paramList()->skipNull();
|
||||
String li(' ',indent);
|
||||
String ci(' ',indent + spaces);
|
||||
const char* sep = spaces ? ": " : ":";
|
||||
buf << "{" << nl;
|
||||
while (l) {
|
||||
const NamedString* p = static_cast<const NamedString*>(l->get());
|
||||
l = l->skipNext();
|
||||
if (p->name() == protoName())
|
||||
continue;
|
||||
buf << ci << strEscape(p->name()) << sep;
|
||||
stringify(p,buf,spaces,indent + spaces);
|
||||
p = static_cast<const NamedString*>(l->get());
|
||||
if (p->name() == protoName())
|
||||
l = l->skipNext();
|
||||
if (l)
|
||||
buf << ",";
|
||||
buf << nl;
|
||||
}
|
||||
buf << li << "}";
|
||||
return;
|
||||
}
|
||||
if (oper->isBoolean())
|
||||
buf << String::boolText(oper->valBoolean());
|
||||
else if (oper->isNumber()) {
|
||||
if (oper->isInteger())
|
||||
buf << oper->number();
|
||||
else
|
||||
buf << "null";
|
||||
}
|
||||
else
|
||||
buf << strEscape(*oper);
|
||||
}
|
||||
|
||||
String JsJSON::strEscape(const char* str)
|
||||
{
|
||||
{
|
||||
String s("\"");
|
||||
char c;
|
||||
while (str && (c = *str++)) {
|
||||
if (c == '\"' || c == '\\')
|
||||
s += "\\";
|
||||
s += c;
|
||||
}
|
||||
s += "\"";
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
void JsJSON::initialize(ScriptContext* context)
|
||||
{
|
||||
if (!context)
|
||||
return;
|
||||
Mutex* mtx = context->mutex();
|
||||
Lock mylock(mtx);
|
||||
NamedList& params = context->params();
|
||||
if (!params.getParam(YSTRING("JSON")))
|
||||
addObject(params,"JSON",new JsJSON(mtx));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* class JsTimeEvent
|
||||
*/
|
||||
|
@ -2958,6 +3172,7 @@ bool JsAssist::init()
|
|||
JsConfigFile::initialize(ctx);
|
||||
JsXML::initialize(ctx);
|
||||
JsHasher::initialize(ctx);
|
||||
JsJSON::initialize(ctx);
|
||||
if (ScriptRun::Invalid == m_runner->reset(true))
|
||||
return false;
|
||||
ScriptContext* chan = YOBJECT(ScriptContext,ctx->getField(m_runner->stack(),YSTRING("Channel"),m_runner));
|
||||
|
@ -3290,6 +3505,7 @@ bool JsGlobal::runMain()
|
|||
JsConfigFile::initialize(runner->context());
|
||||
JsXML::initialize(runner->context());
|
||||
JsHasher::initialize(runner->context());
|
||||
JsJSON::initialize(runner->context());
|
||||
ScriptRun::Status st = runner->run();
|
||||
TelEngine::destruct(runner);
|
||||
return (ScriptRun::Succeeded == st);
|
||||
|
@ -3394,6 +3610,7 @@ bool JsModule::evalContext(String& retVal, const String& cmd, ScriptContext* con
|
|||
JsConfigFile::initialize(runner->context());
|
||||
JsXML::initialize(runner->context());
|
||||
JsHasher::initialize(runner->context());
|
||||
JsJSON::initialize(runner->context());
|
||||
}
|
||||
ScriptRun::Status st = runner->run();
|
||||
if (st == ScriptRun::Succeeded) {
|
||||
|
|
Loading…
Reference in New Issue