Bug 1494752. r=jonco, a=abillings DEVEDITION_64_0b8_BUILD1 DEVEDITION_64_0b8_RELEASE FIREFOX_64_0b8_BUILD1 FIREFOX_64_0b8_RELEASE
authorJeff Walden <jwalden@mit.edu>
Thu, 11 Oct 2018 13:54:41 -0700
changeset 501119 222184270b1be4dd8be3be13e9ec3dd38da8d9e2
parent 501118 fad6b5da71c665e77afc316e6ec07a3442da7bf5
child 501120 fa5ee669847dcda6261349b4940728c88f8fa14e
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco, abillings
bugs1494752
milestone64.0
Bug 1494752. r=jonco, a=abillings
js/src/frontend/Parser.cpp
js/src/vm/Scope.cpp
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -14,16 +14,18 @@
  * syntax trees, see Parser.h).  After tree construction, it rewrites trees to
  * fold constants and evaluate compile-time expressions.
  *
  * This parser attempts no error recovery.
  */
 
 #include "frontend/Parser.h"
 
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Casting.h"
 #include "mozilla/Range.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Utf8.h"
 
 #include <memory>
 #include <new>
@@ -47,20 +49,22 @@
 #include "vm/StringType.h"
 #include "wasm/AsmJS.h"
 
 #include "frontend/ParseContext-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 
 using namespace js;
 
+using mozilla::AssertedCast;
 using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::PodCopy;
 using mozilla::PodZero;
+using mozilla::PointerRangeSize;
 using mozilla::Some;
 using mozilla::Unused;
 using mozilla::Utf8Unit;
 
 using JS::AutoGCRooter;
 using JS::ReadOnlyCompileOptions;
 
 namespace js {
@@ -1940,24 +1944,67 @@ NewEmptyBindingData(JSContext* cx, LifoA
     size_t allocSize = SizeOfData<typename Scope::Data>(numBindings);
     auto* bindings = alloc.allocInSize<Data>(allocSize, numBindings);
     if (!bindings) {
         ReportOutOfMemory(cx);
     }
     return bindings;
 }
 
-/**
- * Copy-construct |BindingName|s from |bindings| into |cursor|, then return
- * the location one past the newly-constructed |BindingName|s.
- */
-static MOZ_MUST_USE BindingName*
-FreshlyInitializeBindings(BindingName* cursor, const BindingNameVector& bindings)
-{
-    return std::uninitialized_copy(bindings.begin(), bindings.end(), cursor);
+namespace detail {
+
+template<class Data>
+static MOZ_ALWAYS_INLINE BindingName*
+InitializeIndexedBindings(Data* data, BindingName* start, BindingName* cursor)
+{
+    return cursor;
+}
+
+template<class Data, typename UnsignedInteger, typename... Step>
+static MOZ_ALWAYS_INLINE BindingName*
+InitializeIndexedBindings(Data* data, BindingName* start, BindingName* cursor,
+                          UnsignedInteger Data::* field, const BindingNameVector& bindings,
+                          Step&&... step)
+{
+    data->*field = AssertedCast<UnsignedInteger>(PointerRangeSize(start, cursor));
+
+    BindingName* newCursor = std::uninitialized_copy(bindings.begin(), bindings.end(), cursor);
+
+    return InitializeIndexedBindings(data, start, newCursor, std::forward<Step>(step)...);
+}
+
+} // namespace detail
+
+// Initialize |data->trailingNames| bindings, then set |data->length| to the
+// count of bindings added (which must equal |count|).
+//
+// First, |firstBindings| are added to |data->trailingNames|.  Then any "steps"
+// present are performed first to last.  Each step is 1) a pointer to a member
+// of |data| to be set to the current number of bindings added, and 2) a vector
+// of |BindingName|s to then copy into |data->trailingNames|.  (Thus each
+// |data| member field indicates where the corresponding vector's names start.)
+template<class Data, typename... Step>
+static MOZ_ALWAYS_INLINE void
+InitializeBindingData(Data* data, uint32_t count,
+                      const BindingNameVector& firstBindings,
+                      Step&&... step)
+{
+    MOZ_ASSERT(data->length == 0, "data shouldn't be filled yet");
+
+    BindingName* start = data->trailingNames.start();
+    BindingName* cursor =
+        std::uninitialized_copy(firstBindings.begin(), firstBindings.end(), start);
+
+#ifdef DEBUG
+    BindingName* end =
+#endif
+        detail::InitializeIndexedBindings(data, start, cursor, std::forward<Step>(step)...);
+
+    MOZ_ASSERT(PointerRangeSize(start, end) == count);
+    data->length = count;
 }
 
 Maybe<GlobalScope::Data*>
 NewGlobalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc)
 {
     BindingNameVector vars(context);
     BindingNameVector lets(context);
     BindingNameVector consts(context);
@@ -1999,28 +2046,20 @@ NewGlobalScopeData(JSContext* context, P
 
     if (numBindings > 0) {
         bindings = NewEmptyBindingData<GlobalScope>(context, alloc, numBindings);
         if (!bindings) {
             return Nothing();
         }
 
         // The ordering here is important. See comments in GlobalScope.
-        BindingName* start = bindings->trailingNames.start();
-        BindingName* cursor = start;
-
-        cursor = FreshlyInitializeBindings(cursor, vars);
-
-        bindings->letStart = cursor - start;
-        cursor = FreshlyInitializeBindings(cursor, lets);
-
-        bindings->constStart = cursor - start;
-        Unused << FreshlyInitializeBindings(cursor, consts);
-
-        bindings->length = numBindings;
+        InitializeBindingData(bindings, numBindings,
+                              vars,
+                              &GlobalScope::Data::letStart, lets,
+                              &GlobalScope::Data::constStart, consts);
     }
 
     return Some(bindings);
 }
 
 Maybe<GlobalScope::Data*>
 ParserBase::newGlobalScopeData(ParseContext::Scope& scope)
 {
@@ -2071,31 +2110,21 @@ NewModuleScopeData(JSContext* context, P
 
     if (numBindings > 0) {
         bindings = NewEmptyBindingData<ModuleScope>(context, alloc, numBindings);
         if (!bindings) {
             return Nothing();
         }
 
         // The ordering here is important. See comments in ModuleScope.
-        BindingName* start = bindings->trailingNames.start();
-        BindingName* cursor = start;
-
-        cursor = FreshlyInitializeBindings(cursor, imports);
-
-        bindings->varStart = cursor - start;
-        cursor = FreshlyInitializeBindings(cursor, vars);
-
-        bindings->letStart = cursor - start;
-        cursor = FreshlyInitializeBindings(cursor, lets);
-
-        bindings->constStart = cursor - start;
-        Unused << FreshlyInitializeBindings(cursor, consts);
-
-        bindings->length = numBindings;
+        InitializeBindingData(bindings, numBindings,
+                              imports,
+                              &ModuleScope::Data::varStart, vars,
+                              &ModuleScope::Data::letStart, lets,
+                              &ModuleScope::Data::constStart, consts);
     }
 
     return Some(bindings);
 }
 
 Maybe<ModuleScope::Data*>
 ParserBase::newModuleScopeData(ParseContext::Scope& scope)
 {
@@ -2122,22 +2151,17 @@ NewEvalScopeData(JSContext* context, Par
     uint32_t numBindings = vars.length();
 
     if (numBindings > 0) {
         bindings = NewEmptyBindingData<EvalScope>(context, alloc, numBindings);
         if (!bindings) {
             return Nothing();
         }
 
-        BindingName* start = bindings->trailingNames.start();
-        BindingName* cursor = start;
-
-        Unused << FreshlyInitializeBindings(cursor, vars);
-
-        bindings->length = numBindings;
+        InitializeBindingData(bindings, numBindings, vars);
     }
 
     return Some(bindings);
 }
 
 Maybe<EvalScope::Data*>
 ParserBase::newEvalScopeData(ParseContext::Scope& scope)
 {
@@ -2219,28 +2243,20 @@ NewFunctionScopeData(JSContext* context,
 
     if (numBindings > 0) {
         bindings = NewEmptyBindingData<FunctionScope>(context, alloc, numBindings);
         if (!bindings) {
             return Nothing();
         }
 
         // The ordering here is important. See comments in FunctionScope.
-        BindingName* start = bindings->trailingNames.start();
-        BindingName* cursor = start;
-
-        cursor = FreshlyInitializeBindings(cursor, positionalFormals);
-
-        bindings->nonPositionalFormalStart = cursor - start;
-        cursor = FreshlyInitializeBindings(cursor, formals);
-
-        bindings->varStart = cursor - start;
-        Unused << FreshlyInitializeBindings(cursor, vars);
-
-        bindings->length = numBindings;
+        InitializeBindingData(bindings, numBindings,
+                              positionalFormals,
+                              &FunctionScope::Data::nonPositionalFormalStart, formals,
+                              &FunctionScope::Data::varStart, vars);
     }
 
     return Some(bindings);
 }
 
 Maybe<FunctionScope::Data*>
 ParserBase::newFunctionScopeData(ParseContext::Scope& scope, bool hasParameterExprs)
 {
@@ -2267,23 +2283,17 @@ NewVarScopeData(JSContext* context, Pars
     uint32_t numBindings = vars.length();
 
     if (numBindings > 0) {
         bindings = NewEmptyBindingData<VarScope>(context, alloc, numBindings);
         if (!bindings) {
             return Nothing();
         }
 
-        // The ordering here is important. See comments in FunctionScope.
-        BindingName* start = bindings->trailingNames.start();
-        BindingName* cursor = start;
-
-        Unused << FreshlyInitializeBindings(cursor, vars);
-
-        bindings->length = numBindings;
+        InitializeBindingData(bindings, numBindings, vars);
     }
 
     return Some(bindings);
 }
 
 Maybe<VarScope::Data*>
 ParserBase::newVarScopeData(ParseContext::Scope& scope)
 {
@@ -2325,25 +2335,19 @@ NewLexicalScopeData(JSContext* context, 
 
     if (numBindings > 0) {
         bindings = NewEmptyBindingData<LexicalScope>(context, alloc, numBindings);
         if (!bindings) {
             return Nothing();
         }
 
         // The ordering here is important. See comments in LexicalScope.
-        BindingName* cursor = bindings->trailingNames.start();
-        BindingName* start = cursor;
-
-        cursor = FreshlyInitializeBindings(cursor, lets);
-
-        bindings->constStart = cursor - start;
-        Unused << FreshlyInitializeBindings(cursor, consts);
-
-        bindings->length = numBindings;
+        InitializeBindingData(bindings, numBindings,
+                              lets,
+                              &LexicalScope::Data::constStart, consts);
     }
 
     return Some(bindings);
 }
 
 Maybe<LexicalScope::Data*>
 ParserBase::newLexicalScopeData(ParseContext::Scope& scope)
 {
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -213,17 +213,17 @@ NewEmptyScopeData(JSContext* cx, uint32_
     }
     return UniquePtr<typename ConcreteScope::Data>(data);
 }
 
 static constexpr size_t HasAtomMask = 1;
 static constexpr size_t HasAtomShift = 1;
 
 static XDRResult
-XDRBindingName(XDRState<XDR_ENCODE>* xdr, BindingName* bindingName)
+XDRTrailingName(XDRState<XDR_ENCODE>* xdr, BindingName* bindingName, const uint32_t* length)
 {
     JSContext* cx = xdr->cx();
 
     RootedAtom atom(cx, bindingName->name());
     bool hasAtom = !!atom;
 
     uint8_t flags = bindingName->flagsForXDR();
     MOZ_ASSERT(((flags << HasAtomShift) >> HasAtomShift) == flags);
@@ -233,31 +233,32 @@ XDRBindingName(XDRState<XDR_ENCODE>* xdr
     if (hasAtom) {
         MOZ_TRY(XDRAtom(xdr, &atom));
     }
 
     return Ok();
 }
 
 static XDRResult
-XDRBindingName(XDRState<XDR_DECODE>* xdr, BindingName* bindingName)
+XDRTrailingName(XDRState<XDR_DECODE>* xdr, void* bindingName, uint32_t* length)
 {
     JSContext* cx = xdr->cx();
 
     uint8_t u8;
     MOZ_TRY(xdr->codeUint8(&u8));
 
     bool hasAtom = u8 & HasAtomMask;
     RootedAtom atom(cx);
     if (hasAtom) {
         MOZ_TRY(XDRAtom(xdr, &atom));
     }
 
     uint8_t flags = u8 >> HasAtomShift;
-    *bindingName = BindingName::fromXDR(atom, flags);
+    new (bindingName) BindingName(BindingName::fromXDR(atom, flags));
+    ++*length;
 
     return Ok();
 }
 
 template <typename ConcreteScopeData>
 static void
 DeleteScopeData(ConcreteScopeData* data)
 {
@@ -283,29 +284,32 @@ Scope::XDRSizedBindingNames(XDRState<mod
 
     if (mode == XDR_ENCODE) {
         data.set(&scope->data());
     } else {
         data.set(NewEmptyScopeData<ConcreteScope>(cx, length).release());
         if (!data) {
             return xdr->fail(JS::TranscodeResult_Throw);
         }
-        data->length = length;
     }
 
     auto dataGuard = mozilla::MakeScopeExit([&] () {
         if (mode == XDR_DECODE) {
             DeleteScopeData(data.get());
             data.set(nullptr);
         }
     });
 
     for (uint32_t i = 0; i < length; i++) {
-        MOZ_TRY(XDRBindingName(xdr, &data->trailingNames[i]));
+        if (mode == XDR_DECODE) {
+            MOZ_ASSERT(i == data->length, "must be decoding at the end");
+        }
+        MOZ_TRY(XDRTrailingName(xdr, &data->trailingNames[i], &data->length));
     }
+    MOZ_ASSERT(data->length == length);
 
     dataGuard.release();
     return Ok();
 }
 
 /* static */ Scope*
 Scope::create(JSContext* cx, ScopeKind kind, HandleScope enclosing, HandleShape envShape)
 {
@@ -1243,16 +1247,31 @@ GenerateWasmName(JSContext* cx, const ch
     }
     if (!NumberValueToStringBuffer(cx, Int32Value(index), sb)) {
         return nullptr;
     }
 
     return sb.finishAtom();
 }
 
+static void
+InitializeTrailingName(TrailingNamesArray& trailingNames, size_t i, JSAtom* name)
+{
+    void* trailingName = &trailingNames[i];
+    new (trailingName) BindingName(name, false);
+}
+
+template<class Data>
+static void
+InitializeNextTrailingName(const Rooted<UniquePtr<Data>>& data, JSAtom* name)
+{
+    InitializeTrailingName(data->trailingNames, data->length, name);
+    data->length++;
+}
+
 /* static */ WasmInstanceScope*
 WasmInstanceScope::create(JSContext* cx, WasmInstanceObject* instance)
 {
     // WasmInstanceScope::Data has GCManagedDeletePolicy because it contains a
     // GCPtr. Destruction of |data| below may trigger calls into the GC.
 
     size_t namesCount = 0;
     if (instance->instance().memory()) {
@@ -1262,40 +1281,39 @@ WasmInstanceScope::create(JSContext* cx,
     size_t globalsCount = instance->instance().metadata().globals.length();
     namesCount += globalsCount;
 
     Rooted<UniquePtr<Data>> data(cx, NewEmptyScopeData<WasmInstanceScope>(cx, namesCount));
     if (!data) {
         return nullptr;
     }
 
-    size_t nameIndex = 0;
-    RootedAtom name(cx);
     if (instance->instance().memory()) {
-        name = GenerateWasmName(cx, "memory", /* index = */ 0);
-        if (!name) {
+        JSAtom* wasmName = GenerateWasmName(cx, "memory", /* index = */ 0);
+        if (!wasmName) {
             return nullptr;
         }
-        new (&data->trailingNames[nameIndex]) BindingName(name, false);
-        nameIndex++;
+
+        InitializeNextTrailingName(data, wasmName);
     }
+
     for (size_t i = 0; i < globalsCount; i++) {
-        name = GenerateWasmName(cx, "global", i);
-        if (!name) {
+        JSAtom* wasmName = GenerateWasmName(cx, "global", i);
+        if (!wasmName) {
             return nullptr;
         }
-        new (&data->trailingNames[nameIndex]) BindingName(name, false);
-        nameIndex++;
+
+        InitializeNextTrailingName(data, wasmName);
     }
-    MOZ_ASSERT(nameIndex == namesCount);
+
+    MOZ_ASSERT(data->length == namesCount);
 
     data->instance.init(instance);
     data->memoriesStart = 0;
     data->globalsStart = globalsStart;
-    data->length = namesCount;
 
     Rooted<Scope*> enclosingScope(cx, &cx->global()->emptyGlobalScope());
 
     return Scope::create<WasmInstanceScope>(cx, ScopeKind::WasmInstance, enclosingScope,
                                             /* envShape = */ nullptr, &data);
 }
 
 /* static */ Shape*
@@ -1327,26 +1345,27 @@ WasmFunctionScope::create(JSContext* cx,
     }
     uint32_t namesCount = locals.length();
 
     Rooted<UniquePtr<Data>> data(cx, NewEmptyScopeData<WasmFunctionScope>(cx, namesCount));
     if (!data) {
         return nullptr;
     }
 
-    data->funcIndex = funcIndex;
-    data->length = namesCount;
-    RootedAtom name(cx);
     for (size_t i = 0; i < namesCount; i++) {
-        name = GenerateWasmName(cx, "var", i);
-        if (!name) {
+        JSAtom* wasmName = GenerateWasmName(cx, "var", i);
+        if (!wasmName) {
             return nullptr;
         }
-        new (&data->trailingNames[i]) BindingName(name, false);
+
+        InitializeNextTrailingName(data, wasmName);
     }
+    MOZ_ASSERT(data->length == namesCount);
+
+    data->funcIndex = funcIndex;
 
     return Scope::create<WasmFunctionScope>(cx, ScopeKind::WasmFunction, enclosing,
                                             /* envShape = */ nullptr, &data);
 }
 
 /* static */ Shape*
 WasmFunctionScope::getEmptyEnvironmentShape(JSContext* cx)
 {