From ad0f68b964cb16caa4c80742f6f1abbab7f689dc Mon Sep 17 00:00:00 2001 From: paulc Date: Fri, 11 May 2012 16:15:20 +0000 Subject: [PATCH] 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 --- libs/yscript/javascript.cpp | 32 ++- libs/yscript/jsobjects.cpp | 446 +++++++++++++++++++++++++++++++++++- 2 files changed, 473 insertions(+), 5 deletions(-) diff --git a/libs/yscript/javascript.cpp b/libs/yscript/javascript.cpp index d84631b6..4a1287b7 100644 --- a/libs/yscript/javascript.cpp +++ b/libs/yscript/javascript.cpp @@ -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; diff --git a/libs/yscript/jsobjects.cpp b/libs/yscript/jsobjects.cpp index 06b9bdac..5451c022 100644 --- a/libs/yscript/jsobjects.cpp +++ b/libs/yscript/jsobjects.cpp @@ -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(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(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; }