michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* JS::Anchor implementation. */ michael@0: michael@0: #ifndef js_Anchor_h michael@0: #define js_Anchor_h michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include "js/TypeDecls.h" michael@0: michael@0: namespace JS { michael@0: michael@0: /* michael@0: * Protecting non-Value, non-JSObject *, non-JSString * values from collection michael@0: * michael@0: * Most of the time, the garbage collector's conservative stack scanner works michael@0: * behind the scenes, finding all live values and protecting them from being michael@0: * collected. However, when JSAPI client code obtains a pointer to data the michael@0: * scanner does not know about, owned by an object the scanner does know about, michael@0: * Care Must Be Taken. michael@0: * michael@0: * The scanner recognizes only a select set of types: pointers to JSObjects and michael@0: * similar things (JSFunctions, and so on), pointers to JSStrings, and Values. michael@0: * So while the scanner finds all live |JSString| pointers, it does not notice michael@0: * |jschar| pointers. michael@0: * michael@0: * So suppose we have: michael@0: * michael@0: * void f(JSString *str) { michael@0: * const jschar *ch = JS_GetStringCharsZ(str); michael@0: * ... do stuff with ch, but no uses of str ...; michael@0: * } michael@0: * michael@0: * After the call to |JS_GetStringCharsZ|, there are no further uses of michael@0: * |str|, which means that the compiler is within its rights to not store michael@0: * it anywhere. But because the stack scanner will not notice |ch|, there michael@0: * is no longer any live value in this frame that would keep the string michael@0: * alive. If |str| is the last reference to that |JSString|, and the michael@0: * collector runs while we are using |ch|, the string's array of |jschar|s michael@0: * may be freed out from under us. michael@0: * michael@0: * Note that there is only an issue when 1) we extract a thing X the scanner michael@0: * doesn't recognize from 2) a thing Y the scanner does recognize, and 3) if Y michael@0: * gets garbage-collected, then X gets freed. If we have code like this: michael@0: * michael@0: * void g(JSObject *obj) { michael@0: * JS::Value x; michael@0: * JS_GetProperty(obj, "x", &x); michael@0: * ... do stuff with x ... michael@0: * } michael@0: * michael@0: * there's no problem, because the value we've extracted, x, is a Value, a michael@0: * type that the conservative scanner recognizes. michael@0: * michael@0: * Conservative GC frees us from the obligation to explicitly root the types it michael@0: * knows about, but when we work with derived values like |ch|, we must root michael@0: * their owners, as the derived value alone won't keep them alive. michael@0: * michael@0: * A JS::Anchor is a kind of GC root that allows us to keep the owners of michael@0: * derived values like |ch| alive throughout the Anchor's lifetime. We could michael@0: * fix the above code as follows: michael@0: * michael@0: * void f(JSString *str) { michael@0: * JS::Anchor a_str(str); michael@0: * const jschar *ch = JS_GetStringCharsZ(str); michael@0: * ... do stuff with ch, but no uses of str ...; michael@0: * } michael@0: * michael@0: * This simply ensures that |str| will be live until |a_str| goes out of scope. michael@0: * As long as we don't retain a pointer to the string's characters for longer michael@0: * than that, we have avoided all garbage collection hazards. michael@0: */ michael@0: template class AnchorPermitted; michael@0: template<> class AnchorPermitted { }; michael@0: template<> class AnchorPermitted { }; michael@0: template<> class AnchorPermitted { }; michael@0: template<> class AnchorPermitted { }; michael@0: template<> class AnchorPermitted { }; michael@0: template<> class AnchorPermitted { }; michael@0: template<> class AnchorPermitted { }; michael@0: template<> class AnchorPermitted { }; michael@0: template<> class AnchorPermitted { }; michael@0: michael@0: template michael@0: class Anchor : AnchorPermitted michael@0: { michael@0: public: michael@0: Anchor() { } michael@0: explicit Anchor(T t) { hold = t; } michael@0: inline ~Anchor(); michael@0: michael@0: private: michael@0: T hold; michael@0: michael@0: /* michael@0: * Rooting analysis considers use of operator= to be a use of an anchor. michael@0: * For simplicity, Anchor is treated as if it contained a GC thing, from michael@0: * construction. Thus if we had michael@0: * michael@0: * void operator=(const T &t) { hold = t; } michael@0: * michael@0: * and this code michael@0: * michael@0: * JS::Anchor anchor; michael@0: * stuff that could GC, producing |str|; michael@0: * anchor = str; michael@0: * michael@0: * the last line would be seen as a hazard, because the final = would "use" michael@0: * |anchor| that is a GC thing -- which could have been moved around by the michael@0: * GC. The workaround is to structure your code so that JS::Anchor is michael@0: * always constructed, living for however long the corresponding value must michael@0: * live. michael@0: */ michael@0: void operator=(const T &t) MOZ_DELETE; michael@0: michael@0: Anchor(const Anchor &other) MOZ_DELETE; michael@0: void operator=(const Anchor &other) MOZ_DELETE; michael@0: }; michael@0: michael@0: template michael@0: inline Anchor::~Anchor() michael@0: { michael@0: #ifdef __GNUC__ michael@0: /* michael@0: * No code is generated for this. But because this is marked 'volatile', G++ will michael@0: * assume it has important side-effects, and won't delete it. (G++ never looks at michael@0: * the actual text and notices it's empty.) And because we have passed |hold| to michael@0: * it, GCC will keep |hold| alive until this point. michael@0: * michael@0: * The "memory" clobber operand ensures that G++ will not move prior memory michael@0: * accesses after the asm --- it's a barrier. Unfortunately, it also means that michael@0: * G++ will assume that all memory has changed after the asm, as it would for a michael@0: * call to an unknown function. I don't know of a way to avoid that consequence. michael@0: */ michael@0: asm volatile("":: "g" (hold) : "memory"); michael@0: #else michael@0: /* michael@0: * An adequate portable substitute, for non-structure types. michael@0: * michael@0: * The compiler promises that, by the end of an expression statement, the michael@0: * last-stored value to a volatile object is the same as it would be in an michael@0: * unoptimized, direct implementation (the "abstract machine" whose behavior the michael@0: * language spec describes). However, the compiler is still free to reorder michael@0: * non-volatile accesses across this store --- which is what we must prevent. So michael@0: * assigning the held value to a volatile variable, as we do here, is not enough. michael@0: * michael@0: * In our case, however, garbage collection only occurs at function calls, so it michael@0: * is sufficient to ensure that the destructor's store isn't moved earlier across michael@0: * any function calls that could collect. It is hard to imagine the compiler michael@0: * analyzing the program so thoroughly that it could prove that such motion was michael@0: * safe. In practice, compilers treat calls to the collector as opaque operations michael@0: * --- in particular, as operations which could access volatile variables, across michael@0: * which this destructor must not be moved. michael@0: * michael@0: * ("Objection, your honor! *Alleged* killer whale!") michael@0: * michael@0: * The disadvantage of this approach is that it does generate code for the store. michael@0: * We do need to use Anchors in some cases where cycles are tight. michael@0: * michael@0: * Note that there is a Anchor::~Anchor() specialization in Value.h. michael@0: */ michael@0: volatile T sink; michael@0: sink = hold; michael@0: #endif /* defined(__GNUC__) */ michael@0: } michael@0: michael@0: } // namespace JS michael@0: michael@0: #endif /* js_Anchor_h */