Work in progress: added methods of Array class and const Object / Array parser.

git-svn-id: http://yate.null.ro/svn/yate/trunk@5057 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2012-05-11 16:15:20 +00:00
parent 2186c58ec6
commit ad0f68b964
2 changed files with 473 additions and 5 deletions

View File

@ -747,26 +747,54 @@ bool JsCode::getSeparator(const char*& expr, bool remove)
return ExpEvaluator::getSeparator(expr,remove);
}
// Parse an inline Javascript [ array ]
// Parse an inline Javascript Array: [ item1, item2, ... ]
JsObject* JsCode::parseArray(const char*& expr, bool constOnly)
{
if (skipComments(expr) != '[')
return 0;
expr++;
JsObject* jso = new JsObject;
do {
bool ok = constOnly ? (getNumber(expr) || getString(expr)) : getOperand(expr,false);
if (!ok) {
TelEngine::destruct(jso);
break;
}
} while (jso && (skipComments(expr) == ',') && expr++);
if (skipComments(expr) != ']')
TelEngine::destruct(jso);
return jso;
}
// Parse an inline Javascript { object }
// Parse an inline Javascript Object: { prop1: value1, prop2: value2, ... }
JsObject* JsCode::parseObject(const char*& expr, bool constOnly)
{
if (skipComments(expr) != '{')
return 0;
expr++;
JsObject* jso = new JsObject;
do {
skipComments(expr);
int len = getKeyword(expr);
if (len <= 0) {
TelEngine::destruct(jso);
break;
}
String name;
name.assign(expr,len);
expr += len;
if (skipComments(expr) != ':') {
TelEngine::destruct(jso);
break;
}
expr++;
bool ok = constOnly ? (getNumber(expr) || getString(expr)) : getOperand(expr,false);
if (!ok) {
TelEngine::destruct(jso);
break;
}
} while (jso && (skipComments(expr) == ',') && expr++);
if (skipComments(expr) != '}')
TelEngine::destruct(jso);
return jso;

View File

@ -49,12 +49,32 @@ class JsArray : public JsObject
YCLASS(JsArray,JsObject)
public:
inline JsArray(Mutex* mtx)
: JsObject("Array",mtx)
: JsObject("Array",mtx), m_length(0)
{
params().addParam(new ExpFunction("push"));
params().addParam(new ExpFunction("pop"));
params().addParam(new ExpFunction("length"));
params().addParam(new ExpFunction("concat"));
params().addParam(new ExpFunction("join"));
params().addParam(new ExpFunction("reverse"));
params().addParam(new ExpFunction("shift"));
params().addParam(new ExpFunction("unshift"));
params().addParam(new ExpFunction("slice"));
params().addParam(new ExpFunction("splice"));
params().addParam(new ExpFunction("sort"));
}
inline long length()
{ return m_length; }
inline void setLength(long len)
{ m_length = len; params().setParam("length",String((int)len)); }
protected:
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
private:
bool runNativeSlice(ObjList& stack, const ExpOperation& oper, GenObject* context);
bool runNativeSplice(ObjList& stack, const ExpOperation& oper, GenObject* context);
bool runNativeSort(ObjList& stack, const ExpOperation& oper, GenObject* context);
long m_length;
};
// Object object
@ -89,6 +109,15 @@ public:
params().addParam(new ExpFunction("getMonth"));
params().addParam(new ExpFunction("getSeconds"));
params().addParam(new ExpFunction("getTime"));
params().addParam(new ExpFunction("getUTCDate"));
params().addParam(new ExpFunction("getUTCDay"));
params().addParam(new ExpFunction("getUTCFullYear"));
params().addParam(new ExpFunction("getUTCHours"));
params().addParam(new ExpFunction("getUTCMilliseconds"));
params().addParam(new ExpFunction("getUTCMinutes"));
params().addParam(new ExpFunction("getUTCMonth"));
params().addParam(new ExpFunction("getUTCSeconds"));
}
protected:
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
@ -119,6 +148,19 @@ static inline void addObject(NamedList& params, const char* name, JsObject* obj)
params.addParam(new NamedPointer(name,obj,obj->toString()));
}
// Helper function that pops arguments off a stack to a list in proper order
static int extractArgs(JsObject* obj, ObjList& stack, const ExpOperation& oper, GenObject* context, ObjList& arguments)
{
if (!obj || !oper.number())
return 0;
for (long int i = oper.number(); i; i--) {
ExpOperation* op = obj->popValue(stack,context);
arguments.insert(op);
}
return oper.number();
}
JsObject::JsObject(const char* name, Mutex* mtx, bool frozen)
: ScriptContext(String("[Object ") + name + "]"),
m_frozen(frozen), m_mutex(mtx)
@ -285,7 +327,298 @@ bool JsObjectObj::runNative(ObjList& stack, const ExpOperation& oper, GenObject*
bool JsArray::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
return JsObject::runNative(stack,oper,context);
if (oper.name() == YSTRING("push")) {
// Adds one or more elements to the end of an array and returns the new length of the array.
if (!oper.number())
return false;
for (long int i = oper.number(); i; i--) {
ExpOperation* op = popValue(stack,context);
ExpWrapper* obj = YOBJECT(ExpWrapper,op);
if (!obj)
continue;
JsObject* jo = (JsObject*)obj->getObject(YSTRING("JsObject"));
if (!jo)
continue;
jo->ref();
params().addParam(new NamedPointer(String((unsigned int)(m_length + i - 1)),jo));
TelEngine::destruct(op);
}
m_length += oper.number();
ExpEvaluator::pushOne(stack,new ExpOperation(length()));
}
else if (oper.name() == YSTRING("pop")) {
// Removes the last element from an array and returns that element
if (m_length < 1)
ExpEvaluator::pushOne(stack,new ExpWrapper(0,0));
NamedString* last = 0;
while (!last) {
last = params().getParam(String((int)--m_length));
if (m_length == 0)
break;
}
if (!last)
ExpEvaluator::pushOne(stack,new ExpWrapper(0,0));
else {
NamedPointer* np = (NamedPointer*)last->getObject(YSTRING("NamedPointer"));
if (!np)
ExpEvaluator::pushOne(stack,new ExpOperation(last->toString()));
else
ExpEvaluator::pushOne(stack,new ExpWrapper(np->userData(),0));
}
// clear last
params().clearParam(last);
}
else if (oper.name() == YSTRING("length")) {
// Reflects the number of elements in an array.
ExpEvaluator::pushOne(stack,new ExpOperation(length()));
}
else if (oper.name() == YSTRING("concat")) {
// Returns a new array comprised of this array joined with other array(s) and/or value(s).
// var num1 = [1, 2, 3];
// var num2 = [4, 5, 6];
// var num3 = [7, 8, 9];
//
// creates array [1, 2, 3, 4, 5, 6, 7, 8, 9]; num1, num2, num3 are unchanged
// var nums = num1.concat(num2, num3);
// var alpha = ['a', 'b', 'c'];
// creates array ["a", "b", "c", 1, 2, 3], leaving alpha unchanged
// var alphaNumeric = alpha.concat(1, [2, 3]);
if (!oper.number())
return false;
JsArray* array = new JsArray(mutex());
// copy this array
for (long int i = 0; i < m_length; i++)
array->params().addParam(params().getParam(String((int)i)));
array->setLength(length());
// add parameters (JsArray of JsObject)
for (long int i = oper.number(); i; i--) {
ExpOperation* op = popValue(stack,context);
ExpWrapper* obj = YOBJECT(ExpWrapper,op);
if (!obj)
continue;
JsArray* ja = (JsArray*)obj->getObject(YSTRING("JsArray"));
if (ja) {
for (long int i = 0; i < ja->length(); i++)
array->params().addParam(String((int)(i + array->length())),ja->params().getValue(String((int)i)));
array->setLength(array->length() + ja->length());
}
else {
JsObject* jo = (JsObject*)obj->getObject(YSTRING("JsObject"));
if (jo) {
array->params().addParam(new NamedPointer(String((unsigned int)array->length()),jo));
array->setLength(array->length() + 1);
jo->ref();
}
else
continue;
}
TelEngine::destruct(op);
}
ExpEvaluator::pushOne(stack,new ExpWrapper(array,0));
}
else if (oper.name() == YSTRING("join")) {
// Joins all elements of an array into a string
// var a = new Array("Wind","Rain","Fire");
// var myVar1 = a.join(); // assigns "Wind,Rain,Fire" to myVar1
// var myVar2 = a.join(", "); // assigns "Wind, Rain, Fire" to myVar2
// var myVar3 = a.join(" + "); // assigns "Wind + Rain + Fire" to myVar3
String separator = ",";
if (oper.number()) {
ExpOperation* op = popValue(stack,context);
separator = op->toString();
}
String result;
for (long int i = 0; i < length(); i++)
result.append(params()[String((int)i)],separator);
ExpEvaluator::pushOne(stack,new ExpOperation(result));
}
else if (oper.name() == YSTRING("reverse")) {
// Reverses the order of the elements of an array -- the first becomes the last, and the last becomes the first.
// var myArray = ["one", "two", "three"];
// myArray.reverse(); => three, two, one
NamedList reversed("");
String separator = ",";
String toCopy;
for (long int i = 0; i < length(); i++)
toCopy.append(params()[String((int)i)],separator);
reversed.copyParams(params(),toCopy);
for (long int i = length(); i; i--)
params().setParam(String((int)(length() - i)),reversed.getValue(String((int)(i - 1))));
}
else if (oper.name() == YSTRING("shift")) {
// Removes the first element from an array and returns that element
// var myFish = ["angel", "clown", "mandarin", "surgeon"];
// println("myFish before: " + myFish);
// var shifted = myFish.shift();
// println("myFish after: " + myFish);
// println("Removed this element: " + shifted);
// This example displays the following:
// myFish before: angel,clown,mandarin,surgeon
// myFish after: clown,mandarin,surgeon
// Removed this element: angel
if (!length())
ExpEvaluator::pushOne(stack,new ExpWrapper(0,0));
else {
ExpEvaluator::pushOne(stack,new ExpOperation(params().getValue("0")));
// shift : value n+1 becomes value n
for (long int i = 0; i < length() - 1; i++)
params().setParam(String((int)i),params().getValue(String((int)i + 1)));
params().clearParam(String((int)(length() - 1)));
setLength(length() - 1);
}
}
else if (oper.name() == YSTRING("unshift")) {
// Adds one or more elements to the front of an array and returns the new length of the array
// myFish = ["angel", "clown"];
// println("myFish before: " + myFish);
// unshifted = myFish.unshift("drum", "lion");
// println("myFish after: " + myFish);
// println("New length: " + unshifted);
// This example displays the following:
// myFish before: ["angel", "clown"]
// myFish after: ["drum", "lion", "angel", "clown"]
// New length: 4
// shift array
long shift = oper.number();
for (long int i = length(); i; i--)
params().setParam(String((int)(i - 1 + shift)),params().getValue(String((int)(i - 1))));
for (long int i = shift; i; i--) {
ExpOperation* op = popValue(stack,context);
ExpWrapper* obj = YOBJECT(ExpWrapper,op);
if (!obj)
continue;
JsObject* jo = (JsObject*)obj->getObject(YSTRING("JsObject"));
if (!jo)
continue;
jo->ref();
params().clearParam(String((int)(i - 1)));
params().setParam(new NamedPointer(String((int)(i - 1)),jo));
TelEngine::destruct(op);
}
setLength(length() + shift);
ExpEvaluator::pushOne(stack,new ExpOperation(length()));
}
else if (oper.name() == YSTRING("slice"))
return runNativeSlice(stack,oper,context);
else if (oper.name() == YSTRING("splice"))
return runNativeSplice(stack,oper,context);
else if (oper.name() == YSTRING("sort")) {
return runNativeSort(stack,oper,context);
}
else if (oper.name() == YSTRING("toString")) {
// Override the JsObject toString method
// var monthNames = ['Jan', 'Feb', 'Mar', 'Apr'];
// var myVar = monthNames.toString(); // assigns "Jan,Feb,Mar,Apr" to myVar.
String separator = ",";
String result;
for (long int i = 0; i < length(); i++)
result.append(params()[String((int)i)],separator);
ExpEvaluator::pushOne(stack,new ExpOperation(result));
}
else
return JsObject::runNative(stack,oper,context);
return true;
}
bool JsArray::runNativeSlice(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
// Extracts a section of an array and returns a new array.
// var myHonda = { color: "red", wheels: 4, engine: { cylinders: 4, size: 2.2 } };
// var myCar = [myHonda, 2, "cherry condition", "purchased 1997"];
// var newCar = myCar.slice(0, 2);
if (!oper.number())
return false;
// begin | end > 0 offset from the start of the array
// < 0 offset from the end of the array
// end missing -> go to end of array
int begin = length(), end = length();
for (long int i = oper.number(); i; i--) {
ExpOperation* op = popValue(stack,context);
if (op->isInteger()) {
end = begin;
begin = op->number();
}
TelEngine::destruct(op);
}
if (begin < 0)
begin = length() + begin;
if (end < 0)
end = length() + end;
if (end < begin)
return false;
// TODO
//JsArray* slice = new JsArray(mutex());
for (long int i = begin; i < end; i++) {
// slice->params().addParam(new NamedString(String((int)(i - begin),
}
return true;
}
bool JsArray::runNativeSplice(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
// Changes the content of an array, adding new elements while removing old elements.
// Returns an array containing the removed elements
// array.splice(index , howMany[, element1[, ...[, elementN]]])
// array.splice(index ,[ howMany[, element1[, ...[, elementN]]]])
ObjList arguments;
int argc = extractArgs(this,stack,oper,context,arguments);
if (!argc)
return false;
// get start index
ExpOperation* op = static_cast<ExpOperation*>(arguments[0]);
int begin = op->number();
if (begin < 0)
begin = length() + begin;
// get count to delete
int count = length() - begin;
if (arguments.count() > 1) {
// get count
op = static_cast<ExpOperation*>(arguments[1]);
count = op->number();
}
// remove elements
JsArray* removed = new JsArray(mutex());
for (int i = begin; i < begin + count; i++) {
removed->params().setParam(String(begin + count - begin),params().getValue(String(i)));
params().clearParam(String(i));
}
removed->setLength(count);
// add the trailing array elements
// index for trailing array elements arfer removing the specified count
int shiftIdx = begin + count;
// shift how many positions for trailing array elements
int shiftWith = arguments.count() > 2 ? arguments.count() - 2 - count : -count;
if (shiftWith > 0) {
// shift everything starting at index shiftIdx with shiftWith positions to the right
for (int i = length(); i > shiftIdx; i--)
params().setParam(String(i - 1 + shiftWith),params().getValue(String(i - 1)));
}
else if (shiftWith < 0) {
// shift everything starting at index shiftIdx with shiftWith positions to the left
for (int i = shiftIdx; i < length(); i++)
params().setParam(String(i + shiftWith),params().getValue(String(i)));
}
// insert the new ones
for (int i = begin;(arguments.count() > 2) && (i < length()); i++) {
GenObject* obj = arguments[2 + i - begin];
params().setParam(new NamedPointer(String(i),obj));
}
// set length
setLength(arguments.count() > 2 ? length() + arguments.count() - 2 - count : length() - count);
// push the removed array on stack
ExpEvaluator::pushOne(stack,new ExpWrapper(removed,0));
return true;
}
bool JsArray::runNativeSort(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
// TODO
return false;
}
@ -337,7 +670,114 @@ bool JsMath::runNative(ObjList& stack, const ExpOperation& oper, GenObject* cont
bool JsDate::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
{
return JsObject::runNative(stack,oper,context);
if (oper.name() == YSTRING("now")) {
// Returns the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC.
ExpEvaluator::pushOne(stack,new ExpOperation((long int)Time::msecNow())); // should check conversion from u_int64_t
}
else if (oper.name() == YSTRING("getDate")) {
// Returns the day of the month for the specified date according to local time.
// The value returned by getDate is an integer between 1 and 31.
// !NOTE : assuming that the date for this object is kept in params()
unsigned int time = params().getIntValue("time");
int year = 0;
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
if (Time::toDateTime(time,year,month,day,hour,minute,sec))
ExpEvaluator::pushOne(stack,new ExpOperation((long int)day));
else
return false;
}
else if (oper.name() == YSTRING("getDay")) {
// Get the day of the week for the date (0 is Sunday and returns values 0-6)
// TODO
}
else if (oper.name() == YSTRING("getFullYear")) {
// Returns the year of the specified date according to local time.
// !NOTE : assuming that the date for this object is kept in params()
unsigned int time = params().getIntValue("time");
int year = 0;
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
if (Time::toDateTime(time,year,month,day,hour,minute,sec))
ExpEvaluator::pushOne(stack,new ExpOperation((long int)year));
else
return false;
}
else if (oper.name() == YSTRING("getHours")) {
// Returns the hour ( 0 - 23) of the specified date according to local time.
// !NOTE : assuming that the date for this object is kept in params()
unsigned int time = params().getIntValue("time");
int year = 0;
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
if (Time::toDateTime(time,year,month,day,hour,minute,sec))
ExpEvaluator::pushOne(stack,new ExpOperation((long int)hour));
else
return false;
}
else if (oper.name() == YSTRING("getMilliseconds")) {
// TODO
}
else if (oper.name() == YSTRING("getMinutes")) {
// Returns the minute ( 0 - 59 ) of the specified date according to local time.
// !NOTE : assuming that the date for this object is kept in params()
unsigned int time = params().getIntValue("time");
int year = 0;
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
if (Time::toDateTime(time,year,month,day,hour,minute,sec))
ExpEvaluator::pushOne(stack,new ExpOperation((long int)minute));
else
return false;
}
else if (oper.name() == YSTRING("getMonth")) {
// Returns the minute ( 0 - 11 ) of the specified date according to local time.
// !NOTE : assuming that the date for this object is kept in params()
unsigned int time = params().getIntValue("time");
int year = 0;
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
if (Time::toDateTime(time,year,month,day,hour,minute,sec))
ExpEvaluator::pushOne(stack,new ExpOperation((long int)month - 1));
else
return false;
}
else if (oper.name() == YSTRING("getSeconds")) {
// Returns the second ( 0 - 59 ) of the specified date according to local time.
// !NOTE : assuming that the date for this object is kept in params()
unsigned int time = params().getIntValue("time");
int year = 0;
unsigned int month = 0, day = 0, hour = 0, minute = 0, sec = 0;
if (Time::toDateTime(time,year,month,day,hour,minute,sec))
ExpEvaluator::pushOne(stack,new ExpOperation((long int)sec));
else
return false;
}
else if (oper.name() == YSTRING("getTime")) {
// TODO
}
else if (oper.name() == YSTRING("getUTCDate")) {
// TODO
}
else if (oper.name() == YSTRING("getUTCDay")) {
// TODO
}
else if (oper.name() == YSTRING("getUTCFullYear")) {
// TODO
}
else if (oper.name() == YSTRING("getUTCHours")) {
// TODO
}
else if (oper.name() == YSTRING("getUTCMilliseconds")) {
// TODO
}
else if (oper.name() == YSTRING("getUTCMinutes")) {
// TODO
}
else if (oper.name() == YSTRING("getUTCMonth")) {
// TODO
}
else if (oper.name() == YSTRING("getUTCSeconds")) {
// TODO
}
else
return JsObject::runNative(stack,oper,context);
return true;
}