/** 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); } } }
/*JSON{ "type" : "method", "class" : "Function", "name" : "bind", "generate" : "jswrap_function_bind", "params" : [ ["this","JsVar","The value to use as the 'this' argument when executing the function"], ["params","JsVarArray","Optional Default parameters that are prepended to the call"] ], "return" : ["JsVar","The 'bound' function"] } This executes the function with the supplied 'this' argument and parameters */ JsVar *jswrap_function_bind(JsVar *parent, JsVar *thisArg, JsVar *argsArray) { if (!jsvIsFunction(parent)) { jsExceptionHere(JSET_TYPEERROR, "Function.bind expects to be called on function, got %t", parent); return 0; } JsVar *fn; if (jsvIsNativeFunction(parent)) fn = jsvNewNativeFunction(parent->varData.native.ptr, parent->varData.native.argTypes); else fn = jsvNewWithFlags(JSV_FUNCTION); if (!fn) return 0; // Old function info JsvObjectIterator fnIt; jsvObjectIteratorNew(&fnIt, parent); // add previously bound arguments while (jsvObjectIteratorHasValue(&fnIt)) { JsVar *param = jsvObjectIteratorGetKey(&fnIt); JsVar *defaultValue = jsvObjectIteratorGetValue(&fnIt); bool wasBound = jsvIsFunctionParameter(param) && defaultValue; if (wasBound) { JsVar *newParam = jsvCopy(param); if (newParam) { // could be out of memory jsvAddName(fn, newParam); jsvUnLock(newParam); } } jsvUnLock(param); jsvUnLock(defaultValue); if (!wasBound) break; jsvObjectIteratorNext(&fnIt); } // add bound arguments JsvObjectIterator argIt; jsvObjectIteratorNew(&argIt, argsArray); while (jsvObjectIteratorHasValue(&argIt)) { JsVar *defaultValue = jsvObjectIteratorGetValue(&argIt); bool addedParam = false; while (!addedParam && jsvObjectIteratorHasValue(&fnIt)) { JsVar *param = jsvObjectIteratorGetKey(&fnIt); if (!jsvIsFunctionParameter(param)) { jsvUnLock(param); break; } JsVar *newParam = jsvCopyNameOnly(param, false, true); jsvSetValueOfName(newParam, defaultValue); jsvAddName(fn, newParam); addedParam = true; jsvUnLock(param); jsvUnLock(newParam); jsvObjectIteratorNext(&fnIt); } if (!addedParam) { JsVar *paramName = jsvNewFromEmptyString(); if (paramName) { jsvMakeFunctionParameter(paramName); // force this to be called a function parameter jsvSetValueOfName(paramName, defaultValue); jsvAddName(fn, paramName); jsvUnLock(paramName); } } jsvUnLock(defaultValue); jsvObjectIteratorNext(&argIt); } jsvObjectIteratorFree(&argIt); // Copy the rest of the old function's info while (jsvObjectIteratorHasValue(&fnIt)) { JsVar *param = jsvObjectIteratorGetKey(&fnIt); JsVar *newParam = jsvCopyNameOnly(param, true, true); if (newParam) { // could be out of memory jsvAddName(fn, newParam); jsvUnLock(newParam); } jsvUnLock(param); jsvObjectIteratorNext(&fnIt); } jsvObjectIteratorFree(&fnIt); // Add 'this' jsvObjectSetChild(fn, JSPARSE_FUNCTION_THIS_NAME, thisArg); // no unlock needed return fn; }
/** 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; }