/* ES5 15.12.3 JO. */ static JSBool JO(JSContext *cx, HandleObject obj, StringifyContext *scx) { /* * This method implements the JO algorithm in ES5 15.12.3, but: * * * The algorithm is somewhat reformulated to allow the final string to * be streamed into a single buffer, rather than be created and copied * into place incrementally as the ES5 algorithm specifies it. This * requires moving portions of the Str call in 8a into this algorithm * (and in JA as well). */ /* Steps 1-2, 11. */ CycleDetector detect(cx, scx, obj); if (!detect.init(cx)) return JS_FALSE; if (!scx->sb.append('{')) return JS_FALSE; /* Steps 5-7. */ Maybe<AutoIdVector> ids; const AutoIdVector *props; if (scx->replacer && !scx->replacer->isCallable()) { JS_ASSERT(JS_IsArrayObject(cx, scx->replacer)); props = &scx->propertyList; } else { JS_ASSERT_IF(scx->replacer, scx->propertyList.length() == 0); ids.construct(cx); if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, ids.addr())) return false; props = ids.addr(); } /* My kingdom for not-quite-initialized-from-the-start references. */ const AutoIdVector &propertyList = *props; /* Steps 8-10, 13. */ bool wroteMember = false; for (size_t i = 0, len = propertyList.length(); i < len; i++) { /* * Steps 8a-8b. Note that the call to Str is broken up into 1) getting * the property; 2) processing for toJSON, calling the replacer, and * handling boxed Number/String/Boolean objects; 3) filtering out * values which process to |undefined|, and 4) stringifying all values * which pass the filter. */ const jsid &id = propertyList[i]; Value outputValue; if (!obj->getGeneric(cx, id, &outputValue)) return false; if (!PreprocessValue(cx, obj, id, &outputValue, scx)) return false; if (IsFilteredValue(outputValue)) continue; /* Output a comma unless this is the first member to write. */ if (wroteMember && !scx->sb.append(',')) return false; wroteMember = true; if (!WriteIndent(cx, scx, scx->depth)) return false; JSString *s = IdToString(cx, id); if (!s) return false; if (!Quote(cx, scx->sb, s) || !scx->sb.append(':') || !(scx->gap.empty() || scx->sb.append(' ')) || !Str(cx, outputValue, scx)) { return false; } } if (wroteMember && !WriteIndent(cx, scx, scx->depth - 1)) return false; return scx->sb.append('}'); }