/*JSON{ "type" : "method", "class" : "Function", "name" : "apply", "generate" : "jswrap_function_apply_or_call", "params" : [ ["this","JsVar","The value to use as the 'this' argument when executing the function"], ["args","JsVar","Optional Array of Arguments"] ], "return" : ["JsVar","The return value of executing this function"] } This executes the function with the supplied 'this' argument and parameters */ JsVar *jswrap_function_apply_or_call(JsVar *parent, JsVar *thisArg, JsVar *argsArray) { unsigned int i; JsVar **args = 0; unsigned int argC = 0; if (jsvIsIterable(argsArray)) { argC = (unsigned int)jsvGetLength(argsArray); if (argC>64) { jsExceptionHere(JSET_ERROR, "Array passed to Function.apply is too big! Maximum 64 arguments, got %d", argC); return 0; } args = (JsVar**)alloca((size_t)argC * sizeof(JsVar*)); for (i=0;i<argC;i++) args[i] = 0; // TODO: Use jsvGetArrayItems? JsvIterator it; jsvIteratorNew(&it, argsArray); while (jsvIteratorHasElement(&it)) { JsVarInt idx = jsvGetIntegerAndUnLock(jsvIteratorGetKey(&it)); if (idx>=0 && idx<(int)argC) { assert(!args[idx]); // just in case there were dups args[idx] = jsvIteratorGetValue(&it); } jsvIteratorNext(&it); } jsvIteratorFree(&it); } else if (!jsvIsUndefined(argsArray)) { jsExceptionHere(JSET_ERROR, "Second argument to Function.apply must be iterable, got %t", argsArray); return 0; } JsVar *r = jspeFunctionCall(parent, 0, thisArg, false, (int)argC, args); jsvUnLockMany(argC, args); return r; }
/*JSON{ "type" : "method", "class" : "Object", "name" : "emit", "generate" : "jswrap_object_emit", "params" : [ ["event","JsVar","The name of the event, for instance 'data'"], ["args","JsVarArray","Optional arguments"] ] } Call the event listeners for this object, for instance ```http.emit('data', 'Foo')```. See Node.js's EventEmitter. */ void jswrap_object_emit(JsVar *parent, JsVar *event, JsVar *argArray) { if (!jsvIsObject(parent)) { jsWarn("Parent must be a proper object - not a String, Integer, etc."); return; } if (!jsvIsString(event)) { jsWarn("First argument to EventEmitter.emit(..) must be a string"); return; } char eventName[16] = "#on"; jsvGetString(event, &eventName[3], sizeof(eventName)-4); // extract data const unsigned int MAX_ARGS = 4; JsVar *args[MAX_ARGS]; unsigned int n = 0; JsvObjectIterator it; jsvObjectIteratorNew(&it, argArray); while (jsvObjectIteratorHasValue(&it)) { if (n>=MAX_ARGS) { jsWarn("Too many arguments"); break; } args[n++] = jsvObjectIteratorGetValue(&it); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); jsiQueueObjectCallbacks(parent, eventName, args, (int)n); // unlock jsvUnLockMany(n, args); }
/*JSON{ "type" : "method", "class" : "ArrayBufferView", "name" : "map", "generate" : "jswrap_arraybufferview_map", "params" : [ ["function","JsVar","Function used to map one item to another"], ["thisArg","JsVar","if specified, the function is called with 'this' set to thisArg (optional)"] ], "return" : ["JsVar","An array containing the results"], "return_object" : "ArrayBufferView" } Return an array which is made from the following: ```A.map(function) = [function(A[0]), function(A[1]), ...]``` **Note:** This returns an `ArrayBuffer` of the same type it was called on. To get an `Array`, use `Array.map`, eg. `[].map.call(myArray, x=>x+1)` */ JsVar *jswrap_arraybufferview_map(JsVar *parent, JsVar *funcVar, JsVar *thisVar) { if (!jsvIsArrayBuffer(parent)) { jsExceptionHere(JSET_ERROR, "ArrayBufferView.map can only be called on an ArrayBufferView"); return 0; } if (!jsvIsFunction(funcVar)) { jsExceptionHere(JSET_ERROR, "ArrayBufferView.map's first argument should be a function"); return 0; } if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) { jsExceptionHere(JSET_ERROR, "ArrayBufferView.map's second argument should be undefined, or an object"); return 0; } // create ArrayBuffer result JsVarDataArrayBufferViewType arrayBufferType = parent->varData.arraybuffer.type; JsVar *array = jsvNewTypedArray(arrayBufferType, (JsVarInt)jsvGetArrayBufferLength(parent)); if (!array) return 0; // now iterate JsvIterator it; // TODO: if we really are limited to ArrayBuffers, this could be an ArrayBufferIterator. jsvIteratorNew(&it, parent, JSIF_EVERY_ARRAY_ELEMENT); JsvArrayBufferIterator itdst; jsvArrayBufferIteratorNew(&itdst, array, 0); while (jsvIteratorHasElement(&it)) { JsVar *index = jsvIteratorGetKey(&it); if (jsvIsInt(index)) { JsVarInt idxValue = jsvGetInteger(index); JsVar *args[3], *mapped; args[0] = jsvIteratorGetValue(&it); args[1] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index args[2] = parent; mapped = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args); jsvUnLockMany(2,args); if (mapped) { jsvArrayBufferIteratorSetValue(&itdst, mapped); jsvUnLock(mapped); } } jsvUnLock(index); jsvIteratorNext(&it); jsvArrayBufferIteratorNext(&itdst); } jsvIteratorFree(&it); jsvArrayBufferIteratorFree(&itdst); return array; }
/*JSON{ "type" : "method", "class" : "Array", "name" : "reduce", "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_array_reduce", "params" : [ ["callback","JsVar","Function used to reduce the array"], ["initialValue","JsVar","if specified, the initial value to pass to the function"] ], "return" : ["JsVar","The value returned by the last function called"] } Execute `previousValue=initialValue` and then `previousValue = callback(previousValue, currentValue, index, array)` for each element in the array, and finally return previousValue. */ JsVar *jswrap_array_reduce(JsVar *parent, JsVar *funcVar, JsVar *initialValue) { const char *name = "reduce"; if (!jsvIsIterable(parent)) { jsExceptionHere(JSET_ERROR, "Array.%s can only be called on something iterable", name); return 0; } if (!jsvIsFunction(funcVar)) { jsExceptionHere(JSET_ERROR, "Array.%s's first argument should be a function", name); return 0; } JsVar *previousValue = jsvLockAgainSafe(initialValue); JsvIterator it; jsvIteratorNew(&it, parent); if (!previousValue) { bool isDone = false; while (!isDone && jsvIteratorHasElement(&it)) { JsVar *index = jsvIteratorGetKey(&it); if (jsvIsInt(index)) { previousValue = jsvIteratorGetValue(&it); isDone = true; } jsvUnLock(index); jsvIteratorNext(&it); } if (!previousValue) { jsExceptionHere(JSET_ERROR, "Array.%s without initial value required non-empty array", name); } } while (jsvIteratorHasElement(&it)) { JsVar *index = jsvIteratorGetKey(&it); if (jsvIsInt(index)) { JsVarInt idxValue = jsvGetInteger(index); JsVar *args[4]; args[0] = previousValue; args[1] = jsvIteratorGetValue(&it); args[2] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index args[3] = parent; previousValue = jspeFunctionCall(funcVar, 0, 0, false, 4, args); jsvUnLockMany(3,args); } jsvUnLock(index); jsvIteratorNext(&it); } jsvIteratorFree(&it); return previousValue; }
/*JSON{ "type" : "constructor", "class" : "Promise", "name" : "Promise", "generate" : "jswrap_promise_constructor", "params" : [ ["executor","JsVar","A function of the form `function (resolve, reject)`"] ], "return" : ["JsVar","A Promise"] } Create a new Promise. The executor function is executed immediately (before the constructor even returns) and */ JsVar *jswrap_promise_constructor(JsVar *executor) { JsVar *obj = jspNewObject(0, "Promise"); if (obj) { // create resolve and reject JsVar *args[2] = { jsvNewNativeFunction((void (*)(void))_jswrap_promise_queueresolve, JSWAT_VOID|JSWAT_THIS_ARG|(JSWAT_JSVAR<<JSWAT_BITS)), jsvNewNativeFunction((void (*)(void))_jswrap_promise_queuereject, JSWAT_VOID|JSWAT_THIS_ARG|(JSWAT_JSVAR<<JSWAT_BITS)) }; // bind 'this' to functions if (args[0]) jsvObjectSetChild(args[0], JSPARSE_FUNCTION_THIS_NAME, obj); if (args[1]) jsvObjectSetChild(args[1], JSPARSE_FUNCTION_THIS_NAME, obj); // call the executor jsvUnLock(jspeFunctionCall(executor, 0, obj, false, 2, args)); jsvUnLockMany(2, args); } return obj; }
/*JSON{ "type" : "method", "class" : "Object", "name" : "emit", "generate" : "jswrap_object_emit", "params" : [ ["event","JsVar","The name of the event, for instance 'data'"], ["args","JsVarArray","Optional arguments"] ] } Call the event listeners for this object, for instance ```http.emit('data', 'Foo')```. See Node.js's EventEmitter. */ void jswrap_object_emit(JsVar *parent, JsVar *event, JsVar *argArray) { if (!jsvHasChildren(parent)) { jsWarn("Parent must be an object - not a String, Integer, etc."); return; } if (!jsvIsString(event)) { jsWarn("First argument to EventEmitter.emit(..) must be a string"); return; } JsVar *eventName = jsvVarPrintf(JS_EVENT_PREFIX"%s",event); if (!eventName) return; // no memory // extract data const unsigned int MAX_ARGS = 4; JsVar *args[MAX_ARGS]; unsigned int n = 0; JsvObjectIterator it; jsvObjectIteratorNew(&it, argArray); while (jsvObjectIteratorHasValue(&it)) { if (n>=MAX_ARGS) { jsWarn("Too many arguments"); break; } args[n++] = jsvObjectIteratorGetValue(&it); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); JsVar *callback = jsvSkipNameAndUnLock(jsvFindChildFromVar(parent, eventName, 0)); jsvUnLock(eventName); if (callback) jsiQueueEvents(parent, callback, args, (int)n); jsvUnLock(callback); // unlock jsvUnLockMany(n, args); }
JsVar *_jswrap_array_iterate_with_callback(const char *name, JsVar *parent, JsVar *funcVar, JsVar *thisVar, bool wantArray, bool isBoolCallback, bool expectedValue) { if (!jsvIsIterable(parent)) { jsExceptionHere(JSET_ERROR, "Array.%s can only be called on something iterable", name); return 0; } if (!jsvIsFunction(funcVar)) { jsExceptionHere(JSET_ERROR, "Array.%s's first argument should be a function", name); return 0; } if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) { jsExceptionHere(JSET_ERROR, "Array.%s's second argument should be undefined, or an object", name); return 0; } JsVar *result = 0; if (wantArray) result = jsvNewEmptyArray(); bool isDone = false; if (result || !wantArray) { JsvIterator it; jsvIteratorNew(&it, parent); while (jsvIteratorHasElement(&it) && !isDone) { JsVar *index = jsvIteratorGetKey(&it); if (jsvIsInt(index)) { JsVarInt idxValue = jsvGetInteger(index); JsVar *args[3], *cb_result; args[0] = jsvIteratorGetValue(&it); args[1] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index args[2] = parent; cb_result = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args); jsvUnLockMany(2,args); if (cb_result) { bool matched; if (isBoolCallback) matched = (jsvGetBool(cb_result) == expectedValue); if (wantArray) { if (isBoolCallback) { // filter if (matched) { jsvArrayPushAndUnLock(result, jsvIteratorGetValue(&it)); } } else { // map JsVar *name = jsvNewFromInteger(idxValue); if (name) { // out of memory? jsvMakeIntoVariableName(name, cb_result); jsvAddName(result, name); jsvUnLock(name); } } } else { // break the loop early if expecting a particular value and didn't get it if (isBoolCallback && !matched) isDone = true; } jsvUnLock(cb_result); } } jsvUnLock(index); jsvIteratorNext(&it); } jsvIteratorFree(&it); } /* boolean result depends on whether the loop terminated early for 'some' or completed for 'every' */ if (!wantArray && isBoolCallback) { result = jsvNewFromBool(isDone != expectedValue); } return result; }
/*JSON{ "type" : "method", "class" : "String", "name" : "replace", "generate" : "jswrap_string_replace", "params" : [ ["subStr","JsVar","The string to search for"], ["newSubStr","JsVar","The string to replace it with"] ], "return" : ["JsVar","This string with `subStr` replaced"] } Search and replace ONE occurrance of `subStr` with `newSubStr` and return the result. This doesn't alter the original string. Regular expressions not supported. */ JsVar *jswrap_string_replace(JsVar *parent, JsVar *subStr, JsVar *newSubStr) { JsVar *str = jsvAsString(parent); #ifndef SAVE_ON_FLASH // Use RegExp if one is passed in if (jsvIsInstanceOf(subStr, "RegExp")) { JsVar *replace; if (jsvIsFunction(newSubStr) || jsvIsString(newSubStr)) replace = jsvLockAgain(newSubStr); else replace = jsvAsString(newSubStr); jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); bool global = jswrap_regexp_hasFlag(subStr,'g'); JsVar *match; match = jswrap_regexp_exec(subStr, str); while (match && !jsvIsNull(match) && !jspIsInterrupted()) { // get info about match JsVar *matchStr = jsvGetArrayItem(match,0); JsVarInt idx = jsvGetIntegerAndUnLock(jsvObjectGetChild(match,"index",0)); JsVarInt len = (JsVarInt)jsvGetStringLength(matchStr); // do the replacement JsVar *newStr = jsvNewFromStringVar(str, 0, (size_t)idx); JsvStringIterator dst; jsvStringIteratorNew(&dst, newStr, 0); jsvStringIteratorGotoEnd(&dst); if (jsvIsFunction(replace)) { unsigned int argCount = 0; JsVar *args[13]; args[argCount++] = jsvLockAgain(matchStr); JsVar *v; while ((v = jsvGetArrayItem(match, (JsVarInt)argCount))) args[argCount++] = v; args[argCount++] = jsvObjectGetChild(match,"index",0); args[argCount++] = jsvObjectGetChild(match,"input",0); JsVar *result = jsvAsStringAndUnLock(jspeFunctionCall(replace, 0, 0, false, (JsVarInt)argCount, args)); jsvUnLockMany(argCount, args); jsvStringIteratorAppendString(&dst, result, 0); jsvUnLock(result); } else { JsvStringIterator src; jsvStringIteratorNew(&src, replace, 0); while (jsvStringIteratorHasChar(&src)) { char ch = jsvStringIteratorGetChar(&src); if (ch=='$') { jsvStringIteratorNext(&src); ch = jsvStringIteratorGetChar(&src); JsVar *group = 0; if (ch>'0' && ch<='9') group = jsvGetArrayItem(match, ch-'0'); if (group) { jsvStringIteratorAppendString(&dst, group, 0); jsvUnLock(group); } else { jsvStringIteratorAppend(&dst, '$'); jsvStringIteratorAppend(&dst, ch); } } else { jsvStringIteratorAppend(&dst, ch); } jsvStringIteratorNext(&src); } jsvStringIteratorFree(&src); } JsVarInt lastIndex = 1+(JsVarInt)jsvStringIteratorGetIndex(&dst); jsvStringIteratorAppendString(&dst, str, (size_t)(idx+len)); jsvStringIteratorFree(&dst); jsvUnLock2(str,matchStr); str = newStr; // search again if global jsvUnLock(match); match = 0; if (global) { jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(lastIndex)); match = jswrap_regexp_exec(subStr, str); } } jsvUnLock(match); jsvUnLock(replace); // reset lastIndex if global if (global) jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); return str; } #endif newSubStr = jsvAsString(newSubStr); subStr = jsvAsString(subStr); int idx = jswrap_string_indexOf(parent, subStr, 0, false); if (idx>=0) { JsVar *newStr = jsvNewFromStringVar(str, 0, (size_t)idx); jsvAppendStringVar(newStr, newSubStr, 0, JSVAPPENDSTRINGVAR_MAXLENGTH); jsvAppendStringVar(newStr, str, (size_t)idx+jsvGetStringLength(subStr), JSVAPPENDSTRINGVAR_MAXLENGTH); jsvUnLock(str); str = newStr; } jsvUnLock2(subStr, newSubStr); return str; }