/*JSON{ "type" : "staticmethod", "class" : "Object", "name" : "getOwnPropertyDescriptor", "generate" : "jswrap_object_getOwnPropertyDescriptor", "params" : [ ["obj","JsVar","The object"], ["name","JsVar","The name of the property"] ], "return" : ["JsVar","An object with a description of the property. The values of writable/enumerable/configurable may not be entirely correct due to Espruino's implementation."] } Get information on the given property in the object, or undefined */ JsVar *jswrap_object_getOwnPropertyDescriptor(JsVar *parent, JsVar *name) { if (!jswrap_object_hasOwnProperty(parent, name)) return 0; JsVar *propName = jsvAsArrayIndex(name); JsVar *varName = jspGetVarNamedField(parent, propName, true); jsvUnLock(propName); assert(varName); // we had it! (apparently) if (!varName) return 0; JsVar *obj = jsvNewObject(); if (!obj) { jsvUnLock(varName); return 0; } //jsvTrace(varName, 5); JsVar *var = jsvSkipName(varName); bool isBuiltIn = jsvIsNewChild(varName); JsvIsInternalChecker checkerFunction = jsvGetInternalFunctionCheckerFor(parent); jsvObjectSetChild(obj, "value", var); jsvObjectSetChildAndUnLock(obj, "writable", jsvNewFromBool(true)); jsvObjectSetChildAndUnLock(obj, "enumerable", jsvNewFromBool(!checkerFunction || !checkerFunction(varName))); jsvObjectSetChildAndUnLock(obj, "configurable", jsvNewFromBool(!isBuiltIn)); jsvUnLock2(var, varName); return obj; }
// 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)) { // 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); } } }