Bug 642381, part 3: Hoist Maybe into mfbt and eliminate Gecko's use of jstl. r=jorendorff,luke
authorChris Jones <jones.chris.g@gmail.com>
Thu, 28 Apr 2011 17:48:52 -0500
changeset 69130 3dd6ec45084c7a79fbbae8aee42769a91dcf00ee
parent 69129 c8dcc08a4a8a02f006d2a8aa7f3e0ea9fc764fd1
child 69131 61bbaedfc2a3934a7fa821012756f6a3ee63046b
push idunknown
push userunknown
push dateunknown
reviewersjorendorff, luke
bugs642381
milestone6.0a1
Bug 642381, part 3: Hoist Maybe into mfbt and eliminate Gecko's use of jstl. r=jorendorff,luke
dom/base/nsJSEnvironment.cpp
dom/base/nsJSEnvironment.h
js/jetpack/JetpackActorCommon.cpp
js/src/Makefile.in
js/src/jstl.h
layout/generic/nsLineLayout.cpp
mfbt/Types.h
mfbt/Util.h
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -111,16 +111,18 @@
 // Force PR_LOGGING so we can get JS strict warnings even in release builds
 #define FORCE_PR_LOG 1
 #endif
 #include "prlog.h"
 #include "prthread.h"
 
 #include "mozilla/FunctionTimer.h"
 
+using namespace mozilla;
+
 const size_t gStackSize = 8192;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gJSDiagnostics;
 #endif
 
 // Thank you Microsoft!
 #ifndef WINCE
@@ -1881,18 +1883,18 @@ nsJSContext::CallEventHandler(nsISupport
 
     jsval funval = OBJECT_TO_JSVAL(funobj);
     JSAutoEnterCompartment ac;
     if (!ac.enter(mContext, funobj) || !JS_WrapObject(mContext, &target)) {
       sSecurityManager->PopContextPrincipal(mContext);
       return NS_ERROR_FAILURE;
     }
 
-    js::Maybe<nsAutoPoolRelease> poolRelease;
-    js::Maybe<js::AutoArrayRooter> tvr;
+    Maybe<nsAutoPoolRelease> poolRelease;
+    Maybe<js::AutoArrayRooter> tvr;
 
     // Use |target| as the scope for wrapping the arguments, since aScope is
     // the safe scope in many cases, which isn't very useful.  Wrapping aTarget
     // was OK because those typically have PreCreate methods that give them the
     // right scope anyway, and we want to make sure that the arguments end up
     // in the same scope as aTarget.
     rv = ConvertSupportsTojsvals(aargv, target, &argc,
                                  &argv, poolRelease, tvr);
@@ -2370,18 +2372,18 @@ nsJSContext::InitializeExternalClasses()
 nsresult
 nsJSContext::SetProperty(void *aTarget, const char *aPropName, nsISupports *aArgs)
 {
   PRUint32  argc;
   jsval    *argv = nsnull;
 
   JSAutoRequest ar(mContext);
 
-  js::Maybe<nsAutoPoolRelease> poolRelease;
-  js::Maybe<js::AutoArrayRooter> tvr;
+  Maybe<nsAutoPoolRelease> poolRelease;
+  Maybe<js::AutoArrayRooter> tvr;
 
   nsresult rv;
   rv = ConvertSupportsTojsvals(aArgs, GetNativeGlobal(), &argc,
                                &argv, poolRelease, tvr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   jsval vargs;
 
@@ -2411,18 +2413,18 @@ nsJSContext::SetProperty(void *aTarget, 
   return rv;
 }
 
 nsresult
 nsJSContext::ConvertSupportsTojsvals(nsISupports *aArgs,
                                      void *aScope,
                                      PRUint32 *aArgc,
                                      jsval **aArgv,
-                                     js::Maybe<nsAutoPoolRelease> &aPoolRelease,
-                                     js::Maybe<js::AutoArrayRooter> &aRooter)
+                                     Maybe<nsAutoPoolRelease> &aPoolRelease,
+                                     Maybe<js::AutoArrayRooter> &aRooter)
 {
   nsresult rv = NS_OK;
 
   // If the array implements nsIJSArgArray, just grab the values directly.
   nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs);
   if (fastArray != nsnull)
     return fastArray->GetArgs(aArgc, reinterpret_cast<void **>(aArgv));
 
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -46,16 +46,18 @@
 #include "prtime.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsScriptNameSpaceManager.h"
 
 class nsIXPConnectJSObjectHolder;
 class nsAutoPoolRelease;
 namespace js {
 class AutoArrayRooter;
+}
+namespace mozilla {
 template <class> class Maybe;
 }
 
 class nsJSContext : public nsIScriptContext,
                     public nsIXPCScriptNotify
 {
 public:
   nsJSContext(JSRuntime *aRuntime);
@@ -200,18 +202,18 @@ public:
 protected:
   nsresult InitializeExternalClasses();
 
   // Helper to convert xpcom datatypes to jsvals.
   nsresult ConvertSupportsTojsvals(nsISupports *aArgs,
                                    void *aScope,
                                    PRUint32 *aArgc,
                                    jsval **aArgv,
-                                   js::Maybe<nsAutoPoolRelease> &aPoolRelease,
-                                   js::Maybe<js::AutoArrayRooter> &aRooter);
+                                   mozilla::Maybe<nsAutoPoolRelease> &aPoolRelease,
+                                   mozilla::Maybe<js::AutoArrayRooter> &aRooter);
 
   nsresult AddSupportsPrimitiveTojsvals(nsISupports *aArg, jsval *aArgv);
 
   // given an nsISupports object (presumably an event target or some other
   // DOM object), get (or create) the JSObject wrapping it.
   nsresult JSObjectFromInterface(nsISupports *aSup, void *aScript, 
                                  JSObject **aRet);
 
--- a/js/jetpack/JetpackActorCommon.cpp
+++ b/js/jetpack/JetpackActorCommon.cpp
@@ -35,36 +35,27 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "base/basictypes.h"
 #include "jscntxt.h"
 
 #include "jsapi.h"
-#include "jstl.h"
 #include "jshashtable.h"
 
 #include "mozilla/jetpack/JetpackActorCommon.h"
 #include "mozilla/jetpack/PJetpack.h"
 #include "mozilla/jetpack/PHandleParent.h"
 #include "mozilla/jetpack/PHandleChild.h"
 #include "mozilla/jetpack/Handle.h"
 
 #include "nsJSUtils.h"
 
-using mozilla::jetpack::JetpackActorCommon;
-using mozilla::jetpack::PHandleParent;
-using mozilla::jetpack::HandleParent;
-using mozilla::jetpack::PHandleChild;
-using mozilla::jetpack::HandleChild;
-using mozilla::jetpack::KeyValue;
-using mozilla::jetpack::PrimVariant;
-using mozilla::jetpack::CompVariant;
-using mozilla::jetpack::Variant;
+using namespace mozilla::jetpack;
 
 class JetpackActorCommon::OpaqueSeenType
 {
 public:
   typedef JSObject* KeyType;
   typedef size_t IdType;
   typedef js::HashMap<
     KeyType, IdType,
@@ -171,17 +162,17 @@ JetpackActorCommon::jsval_to_PrimVariant
 
 bool
 JetpackActorCommon::jsval_to_CompVariant(JSContext* cx, JSType type, jsval from,
                                          CompVariant* to, OpaqueSeenType* seen)
 {
   if (type != JSTYPE_OBJECT)
     return false;
 
-  js::Maybe<OpaqueSeenType> lost;
+  Maybe<OpaqueSeenType> lost;
   if (!seen) {
     lost.construct();
     seen = lost.addr();
     if (!seen->ok())
       return false;
   }
 
   OpaqueSeenType::KeyType obj = JSVAL_TO_OBJECT(from);
@@ -332,17 +323,17 @@ JetpackActorCommon::jsval_from_PrimVaria
 }
 
 bool
 JetpackActorCommon::jsval_from_CompVariant(JSContext* cx,
                                            const CompVariant& from,
                                            jsval* to,
                                            OpaqueSeenType* seen)
 {
-  js::Maybe<OpaqueSeenType> lost;
+  Maybe<OpaqueSeenType> lost;
   if (!seen) {
     lost.construct();
     seen = lost.addr();
     if (!seen->ok())
       return false;
   }
 
   JSObject* obj = NULL;
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -287,16 +287,17 @@ EXPORTS_vm = \
 #
 VPATH		+= \
 		$(srcdir)/../../mfbt \
 		$(NULL)
 
 EXPORTS_NAMESPACES += mozilla
 
 EXPORTS_mozilla = \
+		Types.h		\
 		Util.h          \
 		$(NULL)
 
 ifdef ENABLE_TRACEJIT
 VPATH		+= \
 		$(srcdir)/tracejit \
 		$(srcdir)/nanojit \
 
@@ -723,16 +724,21 @@ distclean::
 	$(RM) $(DIST_GARBAGE)
 
 # our build system doesn't handle subdir srcs very gracefully today
 export::
 	$(MKDIR) -p nanojit
 
 DEFINES		+= -DEXPORT_JS_API
 
+# mfbt is always packed with us, so if we're building a shared object,
+# we need to declare "exported" mfbt symbols on its behalf when we use
+# its headers.
+DEFINES		+= -DIMPL_MFBT
+
 # Some platforms that have stdint.h include it in system headers.  So
 # to reliably get limit macros defined, we'd always have to define the
 # one below before including any header, but that's obscure and
 # fragile, so we do it here.
 DEFINES		+= -D__STDC_LIMIT_MACROS
 
 INCLUDES	+= -I$(srcdir)
 
--- a/js/src/jstl.h
+++ b/js/src/jstl.h
@@ -229,133 +229,16 @@ RoundUpPow2(size_t x)
  */
 template <class T>
 JS_ALWAYS_INLINE size_t
 PointerRangeSize(T *begin, T *end)
 {
     return (size_t(end) - size_t(begin)) / sizeof(T);
 }
 
-/*
- * This utility pales in comparison to Boost's aligned_storage. The utility
- * simply assumes that JSUint64 is enough alignment for anyone. This may need
- * to be extended one day...
- *
- * As an important side effect, pulling the storage into this template is
- * enough obfuscation to confuse gcc's strict-aliasing analysis into not giving
- * false negatives when we cast from the char buffer to whatever type we've
- * constructed using the bytes.
- */
-template <size_t nbytes>
-struct AlignedStorage
-{
-    union U {
-        char bytes[nbytes];
-        uint64 _;
-    } u;
-
-    const void *addr() const { return u.bytes; }
-    void *addr() { return u.bytes; }
-};
-
-template <class T>
-struct AlignedStorage2
-{
-    union U {
-        char bytes[sizeof(T)];
-        uint64 _;
-    } u;
-
-    const T *addr() const { return (const T *)u.bytes; }
-    T *addr() { return (T *)u.bytes; }
-};
-
-/*
- * Small utility for lazily constructing objects without using dynamic storage.
- * When a Maybe<T> is constructed, it is |empty()|, i.e., no value of T has
- * been constructed and no T destructor will be called when the Maybe<T> is
- * destroyed. Upon calling |construct|, a T object will be constructed with the
- * given arguments and that object will be destroyed when the owning Maybe<T>
- * is destroyed.
- *
- * N.B. GCC seems to miss some optimizations with Maybe and may generate extra
- * branches/loads/stores. Use with caution on hot paths.
- */
-template <class T>
-class Maybe
-{
-    AlignedStorage2<T> storage;
-    bool constructed;
-
-    T &asT() { return *storage.addr(); }
-
-    explicit Maybe(const Maybe &other);
-    const Maybe &operator=(const Maybe &other);
-
-  public:
-    Maybe() { constructed = false; }
-    ~Maybe() { if (constructed) asT().~T(); }
-
-    bool empty() const { return !constructed; }
-
-    void construct() {
-        JS_ASSERT(!constructed);
-        new(storage.addr()) T();
-        constructed = true;
-    }
-
-    template <class T1>
-    void construct(const T1 &t1) {
-        JS_ASSERT(!constructed);
-        new(storage.addr()) T(t1);
-        constructed = true;
-    }
-
-    template <class T1, class T2>
-    void construct(const T1 &t1, const T2 &t2) {
-        JS_ASSERT(!constructed);
-        new(storage.addr()) T(t1, t2);
-        constructed = true;
-    }
-
-    template <class T1, class T2, class T3>
-    void construct(const T1 &t1, const T2 &t2, const T3 &t3) {
-        JS_ASSERT(!constructed);
-        new(storage.addr()) T(t1, t2, t3);
-        constructed = true;
-    }
-
-    template <class T1, class T2, class T3, class T4>
-    void construct(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4) {
-        JS_ASSERT(!constructed);
-        new(storage.addr()) T(t1, t2, t3, t4);
-        constructed = true;
-    }
-
-    T *addr() {
-        JS_ASSERT(constructed);
-        return &asT();
-    }
-
-    T &ref() {
-        JS_ASSERT(constructed);
-        return asT();
-    }
-
-    void destroy() {
-        ref().~T();
-        constructed = false;
-    }
-
-    void destroyIfConstructed() {
-        if (!empty())
-            destroy();
-    }
-};
-
 template <class T>
 class AlignedPtrAndFlag
 {
     uintptr_t bits;
 
   public:
     AlignedPtrAndFlag(T *t, bool flag) {
         JS_ASSERT((uintptr_t(t) & 1) == 0);
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -41,16 +41,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /* state and methods used while laying out a single line of a block frame */
 
 #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
 #include "plarena.h"
 
+#include "mozilla/Util.h"
 #include "nsCOMPtr.h"
 #include "nsLineLayout.h"
 #include "nsBlockFrame.h"
 #include "nsInlineFrame.h"
 #include "nsStyleConsts.h"
 #include "nsHTMLContainerFrame.h"
 #include "nsFloatManager.h"
 #include "nsStyleContext.h"
@@ -61,17 +62,16 @@
 #include "nsIDocument.h"
 #include "nsIHTMLDocument.h"
 #include "nsIContent.h"
 #include "nsTextFragment.h"
 #include "nsBidiUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsTextFrame.h"
 #include "nsCSSRendering.h"
-#include "jstl.h"
 
 #ifdef DEBUG
 #undef  NOISY_HORIZONTAL_ALIGN
 #undef  NOISY_VERTICAL_ALIGN
 #undef  REALLY_NOISY_VERTICAL_ALIGN
 #undef  NOISY_REFLOW
 #undef  REALLY_NOISY_REFLOW
 #undef  NOISY_PUSHING
@@ -79,16 +79,18 @@
 #undef  DEBUG_ADD_TEXT
 #undef  NOISY_MAX_ELEMENT_SIZE
 #undef  REALLY_NOISY_MAX_ELEMENT_SIZE
 #undef  NOISY_CAN_PLACE_FRAME
 #undef  NOISY_TRIM
 #undef  REALLY_NOISY_TRIM
 #endif
 
+using namespace mozilla;
+
 //----------------------------------------------------------------------
 
 #define FIX_BUG_50257
 
 nsLineLayout::nsLineLayout(nsPresContext* aPresContext,
                            nsFloatManager* aFloatManager,
                            const nsHTMLReflowState* aOuterReflowState,
                            const nsLineList::iterator* aLine)
@@ -781,17 +783,17 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra
   // reflects the space left on the line.
   NS_WARN_IF_FALSE(psd->mRightEdge != NS_UNCONSTRAINEDSIZE,
                    "have unconstrained width; this should only result from "
                    "very large sizes, not attempts at intrinsic width "
                    "calculation");
   nscoord availableSpaceOnLine = psd->mRightEdge - psd->mX;
 
   // Setup reflow state for reflowing the frame
-  js::Maybe<nsHTMLReflowState> reflowStateHolder;
+  Maybe<nsHTMLReflowState> reflowStateHolder;
   if (!isText) {
     reflowStateHolder.construct(mPresContext, *psd->mReflowState,
                                 aFrame, availSize);
     nsHTMLReflowState& reflowState = reflowStateHolder.ref();
     reflowState.mLineLayout = this;
     reflowState.mFlags.mIsTopOfPage = GetFlag(LL_ISTOPOFPAGE);
     if (reflowState.ComputedWidth() == NS_UNCONSTRAINEDSIZE)
       reflowState.availableWidth = availableSpaceOnLine;
new file mode 100644
--- /dev/null
+++ b/mfbt/Types.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=99 ft=cpp:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Code.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyrigght (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * NB: This header must be both valid C and C++.  It must be
+ * include-able by code embedding SpiderMonkey *and* Gecko.
+ */
+
+#ifndef mozilla_Types_h_
+#define mozilla_Types_h_
+
+/* 
+ * mfbt is logically "lower level" than js/src, but needs basic
+ * definitions of numerical types and macros for compiler/linker
+ * directives.  js/src already goes through some pain to provide them
+ * on numerous platforms, so instead of moving all that goop here,
+ * this header makes use of the fact that for the foreseeable future
+ * mfbt code will be part and parcel with libmozjs, static or not.
+ *
+ * For now, the policy is to use jstypes definitions but add a layer
+ * of indirection on top of them in case a Great Refactoring ever
+ * happens.
+ */
+#include "jstypes.h"
+
+/*
+ * The numerical types provided by jstypes.h that are allowed within
+ * mfbt code are
+ *
+ *   stddef types:  size_t, ptrdiff_t, etc.
+ *   stdin [sic] types:  int8, uint32, etc.
+ *
+ * stdint types (int8_t etc.), are available for use here, but doing
+ * so would change SpiderMonkey's and Gecko's contracts with
+ * embedders: stdint types have not yet appeared in public APIs.
+ */
+
+#define MOZ_EXPORT_API(type_)  JS_EXPORT_API(type_)
+#define MOZ_IMPORT_API(type_)  JS_IMPORT_API(type_)
+
+/*
+ * mfbt definitions need to see export declarations when built, but
+ * other code needs to see import declarations when using mfbt.
+ */
+#if defined(IMPL_MFBT)
+# define MFBT_API(type_)       MOZ_EXPORT_API(type_)
+#else
+# define MFBT_API(type_)       MOZ_IMPORT_API(type_)
+#endif
+
+
+#define MOZ_BEGIN_EXTERN_C     JS_BEGIN_EXTERN_C
+#define MOZ_END_EXTERN_C       JS_END_EXTERN_C
+
+#endif  // mozilla_Types_h_
--- a/mfbt/Util.h
+++ b/mfbt/Util.h
@@ -35,16 +35,52 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_Util_h_
 #define mozilla_Util_h_
 
+#include "mozilla/Types.h"
+
+/*
+ * XXX: we're cheating here in order to avoid creating object files
+ * for mfbt /just/ to provide a function like FatalError() to be used
+ * by MOZ_ASSERT().  (It'll happen eventually, but for just ASSERT()
+ * it isn't worth the pain.)  JS_Assert(), although unfortunately
+ * named, is part of SpiderMonkey's stable, external API, so this
+ * isn't quite as bad as it seems.
+ *
+ * Once mfbt needs object files, this unholy union with JS_Assert()
+ * will be broken.
+ */
+MOZ_BEGIN_EXTERN_C
+
+extern MFBT_API(void)
+JS_Assert(const char *s, const char *file, JSIntn ln);
+
+MOZ_END_EXTERN_C
+
+/*
+ * MOZ_ASSERT() is a "strong" assertion of state, like libc's
+ * assert().  If a MOZ_ASSERT() fails in a debug build, the process in
+ * which it fails will stop running in a loud and dramatic way.
+ */
+#ifdef DEBUG
+
+# define MOZ_ASSERT(expr_)                                      \
+    ((expr_) ? (void)0 : JS_Assert(#expr_, __FILE__, __LINE__))
+
+#else
+
+# define MOZ_ASSERT(expr_) ((void)0)
+
+#endif  // DEBUG
+
 #ifdef __cplusplus
 
 namespace mozilla {
 
 /**
  * DebugOnly contains a value of type T, but only in debug builds.  In
  * release builds, it does not contain a value.  This helper is
  * intended to be used along with ASSERT()-style macros, allowing one
@@ -86,13 +122,131 @@ struct DebugOnly
     /*
      * DebugOnly must always have a destructor or else it will
      * generate "unused variable" warnings, exactly what it's intended
      * to avoid!
      */
     ~DebugOnly() {}
 };
 
+
+/*
+ * This utility pales in comparison to Boost's aligned_storage. The utility
+ * simply assumes that JSUint64 is enough alignment for anyone. This may need
+ * to be extended one day...
+ *
+ * As an important side effect, pulling the storage into this template is
+ * enough obfuscation to confuse gcc's strict-aliasing analysis into not giving
+ * false negatives when we cast from the char buffer to whatever type we've
+ * constructed using the bytes.
+ */
+template <size_t nbytes>
+struct AlignedStorage
+{
+    union U {
+        char bytes[nbytes];
+        uint64 _;
+    } u;
+
+    const void *addr() const { return u.bytes; }
+    void *addr() { return u.bytes; }
+};
+
+template <class T>
+struct AlignedStorage2
+{
+    union U {
+        char bytes[sizeof(T)];
+        uint64 _;
+    } u;
+
+    const T *addr() const { return (const T *)u.bytes; }
+    T *addr() { return (T *)u.bytes; }
+};
+
+/*
+ * Small utility for lazily constructing objects without using dynamic storage.
+ * When a Maybe<T> is constructed, it is |empty()|, i.e., no value of T has
+ * been constructed and no T destructor will be called when the Maybe<T> is
+ * destroyed. Upon calling |construct|, a T object will be constructed with the
+ * given arguments and that object will be destroyed when the owning Maybe<T>
+ * is destroyed.
+ *
+ * N.B. GCC seems to miss some optimizations with Maybe and may generate extra
+ * branches/loads/stores. Use with caution on hot paths.
+ */
+template <class T>
+class Maybe
+{
+    AlignedStorage2<T> storage;
+    bool constructed;
+
+    T &asT() { return *storage.addr(); }
+
+    explicit Maybe(const Maybe &other);
+    const Maybe &operator=(const Maybe &other);
+
+  public:
+    Maybe() { constructed = false; }
+    ~Maybe() { if (constructed) asT().~T(); }
+
+    bool empty() const { return !constructed; }
+
+    void construct() {
+        MOZ_ASSERT(!constructed);
+        new(storage.addr()) T();
+        constructed = true;
+    }
+
+    template <class T1>
+    void construct(const T1 &t1) {
+        MOZ_ASSERT(!constructed);
+        new(storage.addr()) T(t1);
+        constructed = true;
+    }
+
+    template <class T1, class T2>
+    void construct(const T1 &t1, const T2 &t2) {
+        MOZ_ASSERT(!constructed);
+        new(storage.addr()) T(t1, t2);
+        constructed = true;
+    }
+
+    template <class T1, class T2, class T3>
+    void construct(const T1 &t1, const T2 &t2, const T3 &t3) {
+        MOZ_ASSERT(!constructed);
+        new(storage.addr()) T(t1, t2, t3);
+        constructed = true;
+    }
+
+    template <class T1, class T2, class T3, class T4>
+    void construct(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4) {
+        MOZ_ASSERT(!constructed);
+        new(storage.addr()) T(t1, t2, t3, t4);
+        constructed = true;
+    }
+
+    T *addr() {
+        MOZ_ASSERT(constructed);
+        return &asT();
+    }
+
+    T &ref() {
+        MOZ_ASSERT(constructed);
+        return asT();
+    }
+
+    void destroy() {
+        ref().~T();
+        constructed = false;
+    }
+
+    void destroyIfConstructed() {
+        if (!empty())
+            destroy();
+    }
+};
+
 } /* namespace mozilla */
 
 #endif /* __cplusplus */
 
 #endif  /* mozilla_Util_h_ */