/*JSON{ "type" : "staticmethod", "class" : "console", "name" : "log", "generate" : "jswrap_interface_print", "params" : [ ["text","JsVarArray","One or more arguments to print"] ] } Print the supplied string(s) to the console **Note:** If you're connected to a computer (not a wall adaptor) via USB but **you are not running a terminal app** then when you print data Espruino may pause execution and wait until the computer requests the data it is trying to print. */ void jswrap_interface_print(JsVar *v) { assert(jsvIsArray(v)); jsiConsoleRemoveInputLine(); JsvObjectIterator it; jsvObjectIteratorNew(&it, v); while (jsvObjectIteratorHasValue(&it)) { JsVar *v = jsvObjectIteratorGetValue(&it); if (jsvIsString(v)) jsiConsolePrintStringVar(v); else jsfPrintJSON(v, JSON_PRETTY | JSON_NEWLINES); jsvUnLock(v); jsvObjectIteratorNext(&it); if (jsvObjectIteratorHasValue(&it)) jsiConsolePrint(" "); } jsvObjectIteratorFree(&it); jsiConsolePrint("\n"); }
void _jswrap_promise_add(JsVar *parent, JsVar *callback, const char *name) { if (!jsvIsFunction(callback)) { jsExceptionHere(JSET_TYPEERROR, "Callback must be a function, got %t", callback); return; } JsVar *c = jsvObjectGetChild(parent, name, 0); if (!c) { jsvObjectSetChild(parent, name, callback); } else { if (jsvIsArray(c)) { jsvArrayPush(c, callback); } else { JsVar *fns[2] = {c,callback}; JsVar *arr = jsvNewArray(fns, 2); jsvObjectSetChild(parent, name, arr); jsvUnLock(arr); } jsvUnLock(c); } }
/*JSON{ "type":"method", "class": "Function", "name" : "apply", "description" : ["This executes the function with the supplied 'this' argument and parameters"], "generate" : "jswrap_function_apply", "params" : [ [ "this", "JsVar", "The value to use as the 'this' argument when executing the function"], [ "args", "JsVar", "Optional Array of Aruments"] ], "return" : [ "JsVar", "The return value of executing this function" ] }*/ JsVar *jswrap_function_apply(JsVar *parent, JsVar *thisArg, JsVar *argsArray) { unsigned int i; JsVar **args = 0; size_t argC = 0; if (jsvIsArray(argsArray)) { argC = (unsigned int)jsvGetArrayLength(argsArray); if (argC>64) argC=64; // sanity #ifdef RT_USING_JS args = (JsVar**)rt_malloc((size_t)argC * sizeof(JsVar*)); #else args = (JsVar**)alloca((size_t)argC * sizeof(JsVar*)); #endif for (i=0;i<argC;i++) args[i] = 0; JsArrayIterator it; jsvArrayIteratorNew(&it, argsArray); while (jsvArrayIteratorHasElement(&it)) { JsVarInt idx = jsvGetIntegerAndUnLock(jsvArrayIteratorGetIndex(&it)); if (idx>=0 && idx<argC) { assert(!args[idx]); // just in case there were dups args[idx] = jsvArrayIteratorGetElement(&it); } jsvArrayIteratorNext(&it); } jsvArrayIteratorFree(&it); } else if (!jsvIsUndefined(argsArray)) { jsWarn("Second argument to Function.apply must be an array"); } JsVar *r = jspeFunctionCall(parent, 0, thisArg, false, (int)argC, args); for (i=0;i<argC;i++) jsvUnLock(args[i]); #ifdef RT_USING_JS rt_free(args); #else #error "please define RT_USING_JS on rtconfig.h" #endif return r; }
/*JSON{ "type":"method", "class": "Object", "name" : "on", "description" : ["Register an event listener for this object, for instance ```http.on('data', function(d) {...})```. See Node.js's EventEmitter."], "generate" : "jswrap_object_on", "params" : [ [ "event", "JsVar", "The name of the event, for instance 'data'"], [ "listener", "JsVar", "The listener to call when this event is received"] ] }*/ void jswrap_object_on(JsVar *parent, JsVar *event, JsVar *listener) { if (!jsvIsObject(parent)) { jsWarn("Parent must be a proper object - not a String, Integer, etc."); return; } if (!jsvIsString(event)) { jsWarn("First argument to EventEmitter.on(..) must be a string"); return; } if (!jsvIsFunction(listener) && !jsvIsString(listener)) { jsWarn("Second argument to EventEmitter.on(..) must be a function or a String (containing code)"); return; } char eventName[16] = "#on"; jsvGetString(event, &eventName[3], sizeof(eventName)-4); JsVar *eventList = jsvFindChildFromString(parent, eventName, true); JsVar *eventListeners = jsvSkipName(eventList); if (jsvIsUndefined(eventListeners)) { // just add jsvSetValueOfName(eventList, listener); } else { if (jsvIsArray(eventListeners)) { // we already have an array, just add to it jsvArrayPush(eventListeners, listener); } else { // not an array - we need to make it an array JsVar *arr = jsvNewWithFlags(JSV_ARRAY); jsvArrayPush(arr, eventListeners); jsvArrayPush(arr, listener); jsvSetValueOfName(eventList, arr); jsvUnLock(arr); } } jsvUnLock(eventListeners); jsvUnLock(eventList); }
/** This is for Object.keys and Object. However it uses a callback so doesn't allocate anything */ void jswrap_object_keys_or_property_names_cb( JsVar *obj, bool includeNonEnumerable, ///< include 'hidden' items bool includePrototype, ///< include items for the prototype too (for autocomplete) void (*callback)(void *data, JsVar *name), void *data ) { // strings are iterable, but we shouldn't try and show keys for them if (jsvIsIterable(obj)) { JsvIsInternalChecker checkerFunction = jsvGetInternalFunctionCheckerFor(obj); JsvIterator it; jsvIteratorNew(&it, obj); while (jsvIteratorHasElement(&it)) { JsVar *key = jsvIteratorGetKey(&it); if (!(checkerFunction && checkerFunction(key)) || (jsvIsStringEqual(key, JSPARSE_CONSTRUCTOR_VAR))) { /* Not sure why constructor is included in getOwnPropertyNames, but * not in for (i in ...) but it is, so we must explicitly override the * check in jsvIsInternalObjectKey! */ JsVar *name = jsvAsArrayIndexAndUnLock(jsvCopyNameOnly(key, false, false)); if (name) { callback(data, name); jsvUnLock(name); } } jsvUnLock(key); jsvIteratorNext(&it); } jsvIteratorFree(&it); } /* Search our built-in symbol table Assume that ALL builtins are non-enumerable. This isn't great but seems to work quite well right now! */ if (includeNonEnumerable) { const JswSymList *symbols = 0; JsVar *protoOwner = jspGetPrototypeOwner(obj); if (protoOwner) { // If protoOwner then this is the prototype (protoOwner is the object) symbols = jswGetSymbolListForObjectProto(protoOwner); jsvUnLock(protoOwner); } else if (!jsvIsObject(obj) || jsvIsRoot(obj)) { // get symbols, but only if we're not doing it on a basic object symbols = jswGetSymbolListForObject(obj); } while (symbols) { unsigned int i; unsigned char symbolCount = READ_FLASH_UINT8(&symbols->symbolCount); for (i=0;i<symbolCount;i++) { unsigned short strOffset = READ_FLASH_UINT16(&symbols->symbols[i].strOffset); #ifndef USE_FLASH_MEMORY JsVar *name = jsvNewFromString(&symbols->symbolChars[strOffset]); #else // On the esp8266 the string is in flash, so we have to copy it to RAM first // We can't use flash_strncpy here because it assumes that strings start on a word // boundary and that's not the case here. char buf[64], *b = buf, c; const char *s = &symbols->symbolChars[strOffset]; do { c = READ_FLASH_UINT8(s++); *b++ = c; } while (c && b != buf+64); JsVar *name = jsvNewFromString(buf); #endif //os_printf_plus("OBJ cb %s\n", buf); callback(data, name); jsvUnLock(name); } symbols = 0; if (includePrototype) { includePrototype = false; symbols = jswGetSymbolListForObjectProto(obj); } } if (jsvIsArray(obj) || jsvIsString(obj)) { JsVar *name = jsvNewFromString("length"); callback(data, name); jsvUnLock(name); } } }
void jsfGetJSONWithCallback(JsVar *var, JSONFlags flags, const char *whitespace, vcbprintf_callback user_callback, void *user_data) { JSONFlags nflags = flags + JSON_INDENT; // if we add a newline, make sure we indent any subsequent JSON more if (!whitespace) whitespace=" "; if (jsvIsUndefined(var)) { cbprintf(user_callback, user_data, "undefined"); } else { // Use IS_RECURSING flag to stop recursion if (var->flags & JSV_IS_RECURSING) { cbprintf(user_callback, user_data, " ... "); return; } var->flags |= JSV_IS_RECURSING; if (jsvIsArray(var)) { JsVarInt length = jsvGetArrayLength(var); bool limited = (flags&JSON_LIMIT) && (length>(JsVarInt)JSON_LIMIT_AMOUNT); bool needNewLine = false; cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"[ ":"["); JsVarInt lastIndex = -1; bool numeric = true; bool first = true; JsvObjectIterator it; jsvObjectIteratorNew(&it, var); while (lastIndex+1<length && numeric && !jspIsInterrupted()) { JsVar *key = jsvObjectIteratorGetKey(&it); if (!jsvObjectIteratorHasValue(&it) || jsvIsNumeric(key)) { JsVarInt index = jsvObjectIteratorHasValue(&it) ? jsvGetInteger(key) : length-1; JsVar *item = jsvObjectIteratorGetValue(&it); while (lastIndex < index) { lastIndex++; if (!limited || lastIndex<(JsVarInt)JSON_LIMITED_AMOUNT || lastIndex>=length-(JsVarInt)JSON_LIMITED_AMOUNT) { if (!first) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); first = false; if (limited && lastIndex==length-(JsVarInt)JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); bool newNeedsNewLine = ((flags&JSON_SOME_NEWLINES) && jsonNeedsNewLine(item)); if (flags&JSON_ALL_NEWLINES) { needNewLine = true; newNeedsNewLine = true; } if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, whitespace, user_callback, user_data); needNewLine = false; } if (lastIndex == index) jsfGetJSONWithCallback(item, nflags, whitespace, user_callback, user_data); else cbprintf(user_callback, user_data, (flags&JSON_NO_UNDEFINED)?"null":"undefined"); needNewLine = newNeedsNewLine; } } jsvUnLock(item); jsvObjectIteratorNext(&it); } else { numeric = false; } jsvUnLock(key); } // non-numeric - but NOT for standard JSON if ((flags&JSON_PRETTY)) jsfGetJSONForObjectItWithCallback(&it, flags, whitespace, nflags, user_callback, user_data, first); jsvObjectIteratorFree(&it); if (needNewLine) jsonNewLine(flags, whitespace, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" ]":"]"); } else if (jsvIsArrayBuffer(var)) { JsvArrayBufferIterator it; bool allZero = true; jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it)) { if (jsvArrayBufferIteratorGetFloatValue(&it)!=0) allZero = false; jsvArrayBufferIteratorNext(&it); } jsvArrayBufferIteratorFree(&it); bool asArray = flags&JSON_ARRAYBUFFER_AS_ARRAY; if (allZero && !asArray) { cbprintf(user_callback, user_data, "new %s(%d)", jswGetBasicObjectName(var), jsvGetArrayBufferLength(var)); } else { const char *aname = jswGetBasicObjectName(var); /* You can't do `new ArrayBuffer([1,2,3])` so we have to output * `new Uint8Array([1,2,3]).buffer`! */ bool isBasicArrayBuffer = strcmp(aname,"ArrayBuffer")==0; if (isBasicArrayBuffer) { aname="Uint8Array"; } cbprintf(user_callback, user_data, asArray?"[":"new %s([", aname); if (flags&JSON_ALL_NEWLINES) jsonNewLine(nflags, whitespace, user_callback, user_data); size_t length = jsvGetArrayBufferLength(var); bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT); // no newlines needed for array buffers as they only contain simple stuff jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it) && !jspIsInterrupted()) { if (!limited || it.index<JSON_LIMITED_AMOUNT || it.index>=length-JSON_LIMITED_AMOUNT) { if (it.index>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); if (flags&JSON_ALL_NEWLINES) jsonNewLine(nflags, whitespace, user_callback, user_data); if (limited && it.index==length-JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); JsVar *item = jsvArrayBufferIteratorGetValue(&it); jsfGetJSONWithCallback(item, nflags, whitespace, user_callback, user_data); jsvUnLock(item); } jsvArrayBufferIteratorNext(&it); } if (flags&JSON_ALL_NEWLINES) jsonNewLine(flags, whitespace, user_callback, user_data); jsvArrayBufferIteratorFree(&it); cbprintf(user_callback, user_data, asArray?"]":"])"); if (isBasicArrayBuffer && !asArray) cbprintf(user_callback, user_data, ".buffer"); } } else if (jsvIsObject(var)) { IOEventFlags device = (flags & JSON_SHOW_DEVICES) ? jsiGetDeviceFromClass(var) : EV_NONE; if (device!=EV_NONE) { cbprintf(user_callback, user_data, "%s", jshGetDeviceString(device)); } else { bool showContents = true; if (flags & JSON_SHOW_OBJECT_NAMES) { JsVar *proto = jsvObjectGetChild(var, JSPARSE_INHERITS_VAR, 0); if (jsvHasChildren(proto)) { JsVar *constr = jsvObjectGetChild(proto, JSPARSE_CONSTRUCTOR_VAR, 0); if (constr) { JsVar *p = jsvGetIndexOf(execInfo.root, constr, true); if (p) cbprintf(user_callback, user_data, "%v: ", p); jsvUnLock2(p,constr); /* We had the constructor - now if there was a non-default toString function * we'll execute it and print the result */ JsVar *toStringFn = jspGetNamedField(var, "toString", false); if (toStringFn && toStringFn->varData.native.ptr != (void (*)(void))jswrap_object_toString) { // Function found and it's not the default one - execute it JsVar *result = jspExecuteFunction(toStringFn,var,0,0); cbprintf(user_callback, user_data, "%v", result); jsvUnLock(result); showContents = false; // we already printed something } jsvUnLock(toStringFn); } } jsvUnLock(proto); } if (showContents) { JsvObjectIterator it; jsvObjectIteratorNew(&it, var); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"{ ":"{"); bool needNewLine = jsfGetJSONForObjectItWithCallback(&it, flags, whitespace, nflags, user_callback, user_data, true); jsvObjectIteratorFree(&it); if (needNewLine) jsonNewLine(flags, whitespace, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" }":"}"); } } } else if (jsvIsFunction(var)) { if (flags & JSON_IGNORE_FUNCTIONS) { cbprintf(user_callback, user_data, "undefined"); } else { cbprintf(user_callback, user_data, "function "); jsfGetJSONForFunctionWithCallback(var, nflags, user_callback, user_data); } } else if (jsvIsString(var) && !jsvIsName(var)) { if ((flags&JSON_LIMIT) && jsvGetStringLength(var)>JSON_LIMIT_STRING_AMOUNT) { // if the string is too big, split it and put dots in the middle JsVar *var1 = jsvNewFromStringVar(var, 0, JSON_LIMITED_STRING_AMOUNT); JsVar *var2 = jsvNewFromStringVar(var, jsvGetStringLength(var)-JSON_LIMITED_STRING_AMOUNT, JSON_LIMITED_STRING_AMOUNT); cbprintf(user_callback, user_data, "%q%s%q", var1, JSON_LIMIT_TEXT, var2); jsvUnLock2(var1, var2); } else { cbprintf(user_callback, user_data, "%q", var); } } else { cbprintf(user_callback, user_data, "%v", var); } var->flags &= ~JSV_IS_RECURSING; } }
void jsfGetJSONWithCallback(JsVar *var, JSONFlags flags, vcbprintf_callback user_callback, void *user_data) { JSONFlags nflags = flags + JSON_INDENT; // if we add a newline, make sure we indent any subsequent JSON more if (jsvIsUndefined(var)) { cbprintf(user_callback, user_data, "undefined"); } else { // Use IS_RECURSING flag to stop recursion if (var->flags & JSV_IS_RECURSING) { cbprintf(user_callback, user_data, " ... "); return; } var->flags |= JSV_IS_RECURSING; if (jsvIsArray(var)) { size_t length = (size_t)jsvGetArrayLength(var); bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT); bool needNewLine = false; size_t i; cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"[ ":"["); for (i=0;i<length && !jspIsInterrupted();i++) { if (!limited || i<JSON_LIMITED_AMOUNT || i>=length-JSON_LIMITED_AMOUNT) { if (i>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); if (limited && i==length-JSON_LIMITED_AMOUNT) { if (needNewLine) jsonNewLine(nflags, user_callback, user_data); cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); } JsVar *item = jsvGetArrayItem(var, (JsVarInt)i); if (jsvIsUndefined(item) && (flags&JSON_NO_UNDEFINED)) item = jsvNewWithFlags(JSV_NULL); bool newNeedsNewLine = (flags&JSON_NEWLINES) && jsonNeedsNewLine(item); if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, user_callback, user_data); needNewLine = false; } jsfGetJSONWithCallback(item, nflags, user_callback, user_data); needNewLine = newNeedsNewLine; jsvUnLock(item); } } if (needNewLine) jsonNewLine(flags, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" ]":"]"); } else if (jsvIsArrayBuffer(var)) { JsvArrayBufferIterator it; bool allZero = true; jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it)) { if (jsvArrayBufferIteratorGetFloatValue(&it)!=0) allZero = false; jsvArrayBufferIteratorNext(&it); } jsvArrayBufferIteratorFree(&it); if (allZero) { cbprintf(user_callback, user_data, "new %s(%d)", jswGetBasicObjectName(var), jsvGetArrayBufferLength(var)); } else { cbprintf(user_callback, user_data, "new %s([", jswGetBasicObjectName(var)); size_t length = jsvGetArrayBufferLength(var); bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT); // no newlines needed for array buffers as they only contain simple stuff jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it) && !jspIsInterrupted()) { if (!limited || it.index<JSON_LIMITED_AMOUNT || it.index>=length-JSON_LIMITED_AMOUNT) { if (it.index>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); if (limited && it.index==length-JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); JsVar *item = jsvArrayBufferIteratorGetValue(&it); jsfGetJSONWithCallback(item, nflags, user_callback, user_data); jsvUnLock(item); } jsvArrayBufferIteratorNext(&it); } jsvArrayBufferIteratorFree(&it); cbprintf(user_callback, user_data, "])"); } } else if (jsvIsObject(var)) { IOEventFlags device = (flags & JSON_SHOW_DEVICES) ? jsiGetDeviceFromClass(var) : EV_NONE; if (device!=EV_NONE) { cbprintf(user_callback, user_data, "%s", jshGetDeviceString(device)); } else { bool first = true; bool needNewLine = false; size_t sinceNewLine = 0; JsvObjectIterator it; jsvObjectIteratorNew(&it, var); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"{ ":"{"); while (jsvObjectIteratorHasValue(&it) && !jspIsInterrupted()) { JsVar *index = jsvObjectIteratorGetKey(&it); JsVar *item = jsvObjectIteratorGetValue(&it); bool hidden = jsvIsInternalObjectKey(index) || ((flags & JSON_IGNORE_FUNCTIONS) && jsvIsFunction(item)) || ((flags&JSON_NO_UNDEFINED) && jsvIsUndefined(item)); if (!hidden) { sinceNewLine++; if (!first) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); bool newNeedsNewLine = (flags&JSON_NEWLINES) && jsonNeedsNewLine(item); if ((flags&JSON_NEWLINES) && sinceNewLine>JSON_ITEMS_ON_LINE_OBJECT) needNewLine = true; if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, user_callback, user_data); needNewLine = false; sinceNewLine = 0; } cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"%q: ":"%q:", index); if (first) first = false; jsfGetJSONWithCallback(item, nflags, user_callback, user_data); needNewLine = newNeedsNewLine; } jsvUnLock(index); jsvUnLock(item); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); if (needNewLine) jsonNewLine(flags, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" }":"}"); } } else if (jsvIsFunction(var)) { if (flags & JSON_IGNORE_FUNCTIONS) { cbprintf(user_callback, user_data, "undefined"); } else { cbprintf(user_callback, user_data, "function "); jsfGetJSONForFunctionWithCallback(var, nflags, user_callback, user_data); } } else if (jsvIsString(var) && !jsvIsName(var)) { if ((flags&JSON_LIMIT) && jsvGetStringLength(var)>JSON_LIMIT_STRING_AMOUNT) { // if the string is too big, split it and put dots in the middle JsVar *var1 = jsvNewFromStringVar(var, 0, JSON_LIMITED_STRING_AMOUNT); JsVar *var2 = jsvNewFromStringVar(var, jsvGetStringLength(var)-JSON_LIMITED_STRING_AMOUNT, JSON_LIMITED_STRING_AMOUNT); cbprintf(user_callback, user_data, "%q%s%q", var1, JSON_LIMIT_TEXT, var2); jsvUnLock(var1); jsvUnLock(var2); } else { cbprintf(user_callback, user_data, "%q", var); } } else { cbprintf(user_callback, user_data, "%v", var); } var->flags &= ~JSV_IS_RECURSING; } }
/*JSON{ "type" : "method", "class" : "Array", "name" : "splice", "generate" : "jswrap_array_splice", "params" : [ ["index","int","Index at which to start changing the array. If negative, will begin that many elements from the end"], ["howMany","JsVar","An integer indicating the number of old array elements to remove. If howMany is 0, no elements are removed."], ["elements","JsVarArray","One or more items to add to the array"] ], "return" : ["JsVar","An array containing the removed elements. If only one element is removed, an array of one element is returned."] } Both remove and add items to an array */ JsVar *jswrap_array_splice(JsVar *parent, JsVarInt index, JsVar *howManyVar, JsVar *elements) { if (!jsvIsArray(parent)) return 0; JsVarInt len = jsvGetArrayLength(parent); if (index<0) index+=len; if (index<0) index=0; if (index>len) index=len; JsVarInt howMany = len; // how many to delete! if (jsvIsInt(howManyVar)) howMany = jsvGetInteger(howManyVar); if (howMany > len-index) howMany = len-index; JsVarInt newItems = jsvGetArrayLength(elements); JsVarInt shift = newItems-howMany; bool needToAdd = false; JsVar *result = jsvNewEmptyArray(); JsvObjectIterator it; jsvObjectIteratorNew(&it, parent); while (jsvObjectIteratorHasValue(&it) && !needToAdd) { bool goToNext = true; JsVar *idxVar = jsvObjectIteratorGetKey(&it); if (idxVar && jsvIsInt(idxVar)) { JsVarInt idx = jsvGetInteger(idxVar); if (idx<index) { // do nothing... } else if (idx<index+howMany) { // must delete if (result) { // append to result array JsVar *el = jsvObjectIteratorGetValue(&it); jsvArrayPushAndUnLock(result, el); } // delete goToNext = false; JsVar *toRemove = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); jsvRemoveChild(parent, toRemove); jsvUnLock(toRemove); } else { // we're greater than the amount we need to remove now needToAdd = true; goToNext = false; } } jsvUnLock(idxVar); if (goToNext) jsvObjectIteratorNext(&it); } // now we add everything JsVar *beforeIndex = jsvObjectIteratorGetKey(&it); JsvObjectIterator itElement; jsvObjectIteratorNew(&itElement, elements); while (jsvObjectIteratorHasValue(&itElement)) { JsVar *element = jsvObjectIteratorGetValue(&itElement); jsvArrayInsertBefore(parent, beforeIndex, element); jsvUnLock(element); jsvObjectIteratorNext(&itElement); } jsvObjectIteratorFree(&itElement); jsvUnLock(beforeIndex); // And finally renumber while (jsvObjectIteratorHasValue(&it)) { JsVar *idxVar = jsvObjectIteratorGetKey(&it); if (idxVar && jsvIsInt(idxVar)) { jsvSetInteger(idxVar, jsvGetInteger(idxVar)+shift); } jsvUnLock(idxVar); jsvObjectIteratorNext(&it); } // free jsvObjectIteratorFree(&it); // and reset array size jsvSetArrayLength(parent, len + shift, false); return result; }
/** This is for Object.keys and Object. However it uses a callback so doesn't allocate anything */ void jswrap_object_keys_or_property_names_cb( JsVar *obj, bool includeNonEnumerable, ///< include 'hidden' items bool includePrototype, ///< include items for the prototype too (for autocomplete) void (*callback)(void *data, JsVar *name), void *data ) { // strings are iterable, but we shouldn't try and show keys for them if (jsvIsIterable(obj)) { JsvIsInternalChecker checkerFunction = jsvGetInternalFunctionCheckerFor(obj); JsvIterator it; jsvIteratorNew(&it, obj); while (jsvIteratorHasElement(&it)) { JsVar *key = jsvIteratorGetKey(&it); if (!(checkerFunction && checkerFunction(key)) || (jsvIsStringEqual(key, JSPARSE_CONSTRUCTOR_VAR))) { /* Not sure why constructor is included in getOwnPropertyNames, but * not in for (i in ...) but it is, so we must explicitly override the * check in jsvIsInternalObjectKey! */ JsVar *name = jsvAsArrayIndexAndUnLock(jsvCopyNameOnly(key, false, false)); if (name) { callback(data, name); jsvUnLock(name); } } jsvUnLock(key); jsvIteratorNext(&it); } jsvIteratorFree(&it); } /* Search our built-in symbol table Assume that ALL builtins are non-enumerable. This isn't great but seems to work quite well right now! */ if (includeNonEnumerable) { JsVar *protoOwner = jspGetPrototypeOwner(obj); if (protoOwner) { // If protoOwner then this is the prototype (protoOwner is the object) const JswSymList *symbols = jswGetSymbolListForObjectProto(protoOwner); jsvUnLock(protoOwner); _jswrap_object_keys_or_property_names_iterator(symbols, callback, data); } else if (!jsvIsObject(obj) || jsvIsRoot(obj)) { // get symbols, but only if we're not doing it on a basic object const JswSymList *symbols = jswGetSymbolListForObject(obj); _jswrap_object_keys_or_property_names_iterator(symbols, callback, data); } if (includePrototype) { if (jsvIsObject(obj)) { JsVar *proto = jsvObjectGetChild(obj, JSPARSE_INHERITS_VAR, 0); while (jsvIsObject(proto)) { const JswSymList *symbols = jswGetSymbolListForObjectProto(proto); _jswrap_object_keys_or_property_names_iterator(symbols, callback, data); JsVar *p2 = jsvObjectGetChild(proto, JSPARSE_INHERITS_VAR, 0); jsvUnLock(proto); proto = p2; } } // finally include Object/String/etc const JswSymList *symbols = jswGetSymbolListForObjectProto(obj); _jswrap_object_keys_or_property_names_iterator(symbols, callback, data); } if (jsvIsArray(obj) || jsvIsString(obj)) { JsVar *name = jsvNewFromString("length"); callback(data, name); jsvUnLock(name); } } }
void jsvObjectIteratorNew(JsvObjectIterator *it, JsVar *obj) { assert(jsvIsArray(obj) || jsvIsObject(obj) || jsvIsFunction(obj) || jsvIsGetterOrSetter(obj)); it->var = jsvLockSafe(jsvGetFirstChild(obj)); }
/** This is for Object.keys and Object. */ JsVar *jswrap_object_keys_or_property_names( JsVar *obj, bool includeNonEnumerable, ///< include 'hidden' items bool includePrototype) { ///< include items for the prototype too (for autocomplete) JsVar *arr = jsvNewWithFlags(JSV_ARRAY); if (!arr) return 0; // strings are iterable, but we shouldn't try and show keys for them if (jsvIsIterable(obj)) { JsvIsInternalChecker checkerFunction = jsvGetInternalFunctionCheckerFor(obj); JsvIterator it; jsvIteratorNew(&it, obj); while (jsvIteratorHasElement(&it)) { JsVar *key = jsvIteratorGetKey(&it); if (!(checkerFunction && checkerFunction(key)) || (jsvIsStringEqual(key, JSPARSE_CONSTRUCTOR_VAR))) { /* Not sure why constructor is included in getOwnPropertyNames, but * not in for (i in ...) but it is, so we must explicitly override the * check in jsvIsInternalObjectKey! */ JsVar *name = jsvAsArrayIndexAndUnLock(jsvCopyNameOnly(key, false, false)); if (name) { jsvArrayPushAndUnLock(arr, name); } } jsvUnLock(key); jsvIteratorNext(&it); } jsvIteratorFree(&it); } /* Search our built-in symbol table Assume that ALL builtins are non-enumerable. This isn't great but seems to work quite well right now! */ if (includeNonEnumerable) { const JswSymList *symbols = 0; JsVar *protoOwner = jspGetPrototypeOwner(obj); if (protoOwner) { // If protoOwner then this is the prototype (protoOwner is the object) symbols = jswGetSymbolListForObjectProto(protoOwner); jsvUnLock(protoOwner); } else if (!jsvIsObject(obj) || jsvIsRoot(obj)) { // get symbols, but only if we're not doing it on a basic object symbols = jswGetSymbolListForObject(obj); } if (symbols) { unsigned int i; for (i=0;i<symbols->symbolCount;i++) jsvArrayAddString(arr, &symbols->symbolChars[symbols->symbols[i].strOffset]); } if (includePrototype) { symbols = jswGetSymbolListForObjectProto(obj); if (symbols) { unsigned int i; for (i=0;i<symbols->symbolCount;i++) jsvArrayAddString(arr, &symbols->symbolChars[symbols->symbols[i].strOffset]); } } if (jsvIsArray(obj) || jsvIsString(obj)) { jsvArrayAddString(arr, "length"); } } return arr; }