// This is for Object.keys and Object. JsVar *jswrap_object_keys_or_property_names(JsVar *obj, bool includeNonEnumerable) { // strings are iterable, but we shouldn't try and show keys for them if (jsvIsIterable(obj) && !jsvIsString(obj)) { JsvIsInternalChecker checkerFunction = jsvGetInternalFunctionCheckerFor(obj); JsVar *arr = jsvNewWithFlags(JSV_ARRAY); if (!arr) return 0; 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) { 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 (jsvIsArray(obj) || jsvIsString(obj)) { jsvArrayAddString(arr, "length"); } } return arr; } else { jsWarn("Object.keys called on non-object"); return 0; } }
/** 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); } } }
/** 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); } } }