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