📄 jsobj.c
字号:
}static JSHashEntry *MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap){ JSSharpObjectMap *map; JSHashTable *table; JSHashNumber hash; JSHashEntry **hep, *he; jsatomid sharpid; JSIdArray *ida; JSBool ok; jsint i, length; jsid id;#if JS_HAS_GETTER_SETTER JSObject *obj2; JSProperty *prop; uintN attrs;#endif jsval val; int stackDummy; if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); return NULL; } map = &cx->sharpObjectMap; table = map->table; hash = js_hash_object(obj); hep = JS_HashTableRawLookup(table, hash, obj); he = *hep; if (!he) { sharpid = 0; he = JS_HashTableRawAdd(table, hep, hash, obj, JS_UINT32_TO_PTR(sharpid)); if (!he) { JS_ReportOutOfMemory(cx); return NULL; } /* * Increment map->depth to protect js_EnterSharpObject from reentering * itself badly. Without this fix, if we reenter the basis case where * map->depth == 0, when unwinding the inner call we will destroy the * newly-created hash table and crash. */ ++map->depth; ida = JS_Enumerate(cx, obj); --map->depth; if (!ida) return NULL; ok = JS_TRUE; for (i = 0, length = ida->length; i < length; i++) { id = ida->vector[i];#if JS_HAS_GETTER_SETTER ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) break; if (!prop) continue; ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); if (ok) { if (OBJ_IS_NATIVE(obj2) && (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { val = JSVAL_NULL; if (attrs & JSPROP_GETTER) val = (jsval) ((JSScopeProperty*)prop)->getter; if (attrs & JSPROP_SETTER) { if (val != JSVAL_NULL) { /* Mark the getter, then set val to setter. */ ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL) != NULL); } val = (jsval) ((JSScopeProperty*)prop)->setter; } } else { ok = OBJ_GET_PROPERTY(cx, obj, id, &val); } } OBJ_DROP_PROPERTY(cx, obj2, prop);#else ok = OBJ_GET_PROPERTY(cx, obj, id, &val);#endif if (!ok) break; if (!JSVAL_IS_PRIMITIVE(val) && !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) { ok = JS_FALSE; break; } } if (!ok || !idap) JS_DestroyIdArray(cx, ida); if (!ok) return NULL; } else { sharpid = JS_PTR_TO_UINT32(he->value); if (sharpid == 0) { sharpid = ++map->sharpgen << SHARP_ID_SHIFT; he->value = JS_UINT32_TO_PTR(sharpid); } ida = NULL; } if (idap) *idap = ida; return he;}JSHashEntry *js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, jschar **sp){ JSSharpObjectMap *map; JSHashTable *table; JSIdArray *ida; JSHashNumber hash; JSHashEntry *he, **hep; jsatomid sharpid; char buf[20]; size_t len; if (JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) && cx->branchCallback && !cx->branchCallback(cx, NULL)) { return NULL; } /* Set to null in case we return an early error. */ *sp = NULL; map = &cx->sharpObjectMap; table = map->table; if (!table) { table = JS_NewHashTable(8, js_hash_object, JS_CompareValues, JS_CompareValues, NULL, NULL); if (!table) { JS_ReportOutOfMemory(cx); return NULL; } map->table = table; JS_KEEP_ATOMS(cx->runtime); } /* From this point the control must flow either through out: or bad:. */ ida = NULL; if (map->depth == 0) { he = MarkSharpObjects(cx, obj, &ida); if (!he) goto bad; JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0); if (!idap) { JS_DestroyIdArray(cx, ida); ida = NULL; } } else { hash = js_hash_object(obj); hep = JS_HashTableRawLookup(table, hash, obj); he = *hep; /* * It's possible that the value of a property has changed from the * first time the object's properties are traversed (when the property * ids are entered into the hash table) to the second (when they are * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not * idempotent. */ if (!he) { he = JS_HashTableRawAdd(table, hep, hash, obj, NULL); if (!he) { JS_ReportOutOfMemory(cx); goto bad; } sharpid = 0; goto out; } } sharpid = JS_PTR_TO_UINT32(he->value); if (sharpid != 0) { len = JS_snprintf(buf, sizeof buf, "#%u%c", sharpid >> SHARP_ID_SHIFT, (sharpid & SHARP_BIT) ? '#' : '='); *sp = js_InflateString(cx, buf, &len); if (!*sp) { if (ida) JS_DestroyIdArray(cx, ida); goto bad; } }out: JS_ASSERT(he); if ((sharpid & SHARP_BIT) == 0) { if (idap && !ida) { ida = JS_Enumerate(cx, obj); if (!ida) { if (*sp) { JS_free(cx, *sp); *sp = NULL; } goto bad; } } map->depth++; } if (idap) *idap = ida; return he;bad: /* Clean up the sharpObjectMap table on outermost error. */ if (map->depth == 0) { JS_UNKEEP_ATOMS(cx->runtime); map->sharpgen = 0; JS_HashTableDestroy(map->table); map->table = NULL; } return NULL;}voidjs_LeaveSharpObject(JSContext *cx, JSIdArray **idap){ JSSharpObjectMap *map; JSIdArray *ida; map = &cx->sharpObjectMap; JS_ASSERT(map->depth > 0); if (--map->depth == 0) { JS_UNKEEP_ATOMS(cx->runtime); map->sharpgen = 0; JS_HashTableDestroy(map->table); map->table = NULL; } if (idap) { ida = *idap; if (ida) { JS_DestroyIdArray(cx, ida); *idap = NULL; } }}JS_STATIC_DLL_CALLBACK(intN)gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg){ GC_MARK((JSContext *)arg, (JSObject *)he->key, "sharp table entry"); return JS_DHASH_NEXT;}voidjs_GCMarkSharpMap(JSContext *cx, JSSharpObjectMap *map){ JS_ASSERT(map->depth > 0); JS_ASSERT(map->table); /* * During recursive calls to MarkSharpObjects a non-native object or * object with a custom getProperty method can potentially return an * unrooted value or even cut from the object graph an argument of one of * MarkSharpObjects recursive invocations. So we must protect map->table * entries against GC. * * We can not simply use JSTempValueRooter to mark the obj argument of * MarkSharpObjects during recursion as we have to protect *all* entries * in JSSharpObjectMap including those that contains otherwise unreachable * objects just allocated through custom getProperty. Otherwise newer * allocations can re-use the address of an object stored in the hashtable * confusing js_EnterSharpObject. So to address the problem we simply * mark all objects from map->table. * * An alternative "proper" solution is to use JSTempValueRooter in * MarkSharpObjects with code to remove during finalization entries * with otherwise unreachable objects. But this is way too complex * to justify spending efforts. */ JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, cx);}#define OBJ_TOSTRING_EXTRA 4 /* for 4 local GC roots */#if JS_HAS_TOSOURCEJSBooljs_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ JSBool ok, outermost; JSHashEntry *he; JSIdArray *ida; jschar *chars, *ochars, *vsharp; const jschar *idstrchars, *vchars; size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen; char *comma; jsint i, j, length, valcnt; jsid id;#if JS_HAS_GETTER_SETTER JSObject *obj2; JSProperty *prop; uintN attrs;#endif jsval *val; JSString *gsopold[2]; JSString *gsop[2]; JSAtom *atom; JSString *idstr, *valstr, *str; int stackDummy; if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); return JS_FALSE; } /* If outermost, we need parentheses to be an expression, not a block. */ outermost = (cx->sharpObjectMap.depth == 0); he = js_EnterSharpObject(cx, obj, &ida, &chars); if (!he) return JS_FALSE; if (IS_SHARP(he)) { /* * We didn't enter -- obj is already "sharp", meaning we've visited it * already in our depth first search, and therefore chars contains a * string of the form "#n#". */ JS_ASSERT(!ida);#if JS_HAS_SHARP_VARS nchars = js_strlen(chars);#else chars[0] = '{'; chars[1] = '}'; chars[2] = 0; nchars = 2;#endif goto make_string; } JS_ASSERT(ida); ok = JS_TRUE; if (!chars) { /* If outermost, allocate 4 + 1 for "({})" and the terminator. */ chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar)); nchars = 0; if (!chars) goto error; if (outermost) chars[nchars++] = '('; } else { /* js_EnterSharpObject returned a string of the form "#n=" in chars. */ MAKE_SHARP(he); nchars = js_strlen(chars); chars = (jschar *) realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar)); if (!chars) { free(ochars); goto error; } if (outermost) { /* * No need for parentheses around the whole shebang, because #n= * unambiguously begins an object initializer, and never a block * statement. */ outermost = JS_FALSE; } }#ifdef DUMP_CALL_TABLE if (cx->options & JSOPTION_LOGCALL_TOSOURCE) { const char *classname = OBJ_GET_CLASS(cx, obj)->name; size_t classnchars = strlen(classname); static const char classpropid[] = "C"; const char *cp; size_t onchars = nchars; /* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */ classnchars += sizeof classpropid - 1 + 2 + 2; if (ida->length) classnchars += 2; /* 2 for the braces, 1 for the terminator */ chars = (jschar *) realloc((ochars = chars), (nchars + classnchars + 2 + 1) * sizeof(jschar)); if (!chars) { free(ochars); goto error; } chars[nchars++] = '{'; /* 1 from the 2 braces */ for (cp = classpropid; *cp; cp++) chars[nchars++] = (jschar) *cp; chars[nchars++] = ':'; chars[nchars++] = ' '; /* 2 for ': ' */ chars[nchars++] = '"'; for (cp = classname; *cp; cp++) chars[nchars++] = (jschar) *cp; chars[nchars++] = '"'; /* 2 quotes */ if (ida->length) { chars[nchars++] = ','; chars[nchars++] = ' '; /* 2 for ', ' */ } JS_ASSERT(nchars - onchars == 1 + classnchars); } else#endif chars[nchars++] = '{'; comma = NULL; /* * We have four local roots for cooked and raw value GC safety. Hoist the * "argv + 2" out of the loop using the val local, which refers to the raw * (unconverted, "uncooked") values. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -