Bug 1393089 - Part 7: Don't force source buffer allocation when copying TypedArrays. r=jandem
authorAndré Bargull <andre.bargull@gmail.com>
Sat, 09 Sep 2017 13:11:58 +0200
changeset 429839 453483778b6e1f09d3fddd9b89beb42abfaac200
parent 429838 4acbcf36de474be12c30a07a913236d8041d5319
child 429840 2d0d78a70953c589f5c62f527f7f95881492d3a6
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1393089
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1393089 - Part 7: Don't force source buffer allocation when copying TypedArrays. r=jandem
js/src/jsobj.cpp
js/src/jsobj.h
js/src/vm/TypedArrayObject.cpp
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2455,16 +2455,31 @@ js::GetPropertyPure(JSContext* cx, JSObj
     if (!prop) {
         vp->setUndefined();
         return true;
     }
 
     return pobj->isNative() && NativeGetPureInline(&pobj->as<NativeObject>(), id, prop, vp);
 }
 
+bool
+js::GetOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp)
+{
+    PropertyResult prop;
+    if (!LookupOwnPropertyPure(cx, obj, id, &prop))
+        return false;
+
+    if (!prop) {
+        vp->setUndefined();
+        return true;
+    }
+
+    return obj->isNative() && NativeGetPureInline(&obj->as<NativeObject>(), id, prop, vp);
+}
+
 static inline bool
 NativeGetGetterPureInline(PropertyResult prop, JSFunction** fp)
 {
     if (!prop.isDenseOrTypedArrayElement() && prop.shape()->hasGetterObject()) {
         Shape* shape = prop.shape();
         if (shape->getterObject()->is<JSFunction>()) {
             *fp = &shape->getterObject()->as<JSFunction>();
             return true;
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1232,16 +1232,19 @@ LookupPropertyPure(JSContext* cx, JSObje
 bool
 LookupOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, PropertyResult* propp,
                       bool* isTypedArrayOutOfRange = nullptr);
 
 bool
 GetPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp);
 
 bool
+GetOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp);
+
+bool
 GetGetterPure(JSContext* cx, JSObject* obj, jsid id, JSFunction** fp);
 
 bool
 GetOwnGetterPure(JSContext* cx, JSObject* obj, jsid id, JSFunction** fp);
 
 bool
 GetOwnNativeGetterPure(JSContext* cx, JSObject* obj, jsid id, JSNative* native);
 
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -1070,32 +1070,64 @@ TypedArrayObjectTemplate<T>::AllocateArr
 
 static bool
 IsArrayBufferSpecies(JSContext* cx, JSFunction* species)
 {
     return IsSelfHostedFunctionWithName(species, cx->names().ArrayBufferSpecies);
 }
 
 static JSObject*
-GetSpeciesConstructor(JSContext* cx, HandleObject obj, bool isWrapped,
-                      SpeciesConstructorOverride override)
+GetBufferSpeciesConstructor(JSContext* cx, Handle<TypedArrayObject*> typedArray,
+                            bool isWrapped, SpeciesConstructorOverride override)
 {
     RootedObject defaultCtor(cx, GlobalObject::getOrCreateArrayBufferConstructor(cx, cx->global()));
     if (!defaultCtor)
         return nullptr;
 
     // Use the current global's ArrayBuffer if the override is set.
     if (override == SpeciesConstructorOverride::ArrayBuffer)
         return defaultCtor;
 
-    RootedObject wrappedObj(cx, obj);
-    if (isWrapped && !cx->compartment()->wrap(cx, &wrappedObj))
-        return nullptr;
+    RootedObject obj(cx, typedArray->bufferObject());
+    if (!obj) {
+        MOZ_ASSERT(!isWrapped);
+
+        // The buffer was never exposed to content code, so if
+        // 1. %ArrayBufferPrototype%.constructor == %ArrayBuffer%, and
+        // 2. %ArrayBuffer%[@@species] == ArrayBufferSpecies
+        // we don't have to reify the buffer object and can simply return the
+        // default arrray buffer constructor.
+
+        JSObject* proto = GlobalObject::getOrCreateArrayBufferPrototype(cx, cx->global());
+        if (!proto)
+            return nullptr;
 
-    return SpeciesConstructor(cx, wrappedObj, defaultCtor, IsArrayBufferSpecies);
+        Value ctor;
+        if (GetOwnPropertyPure(cx, proto, NameToId(cx->names().constructor), &ctor) &&
+            ctor.isObject() && &ctor.toObject() == defaultCtor)
+        {
+            jsid speciesId = SYMBOL_TO_JSID(cx->wellKnownSymbols().species);
+            JSFunction* getter;
+            if (GetOwnGetterPure(cx, defaultCtor, speciesId, &getter) && getter &&
+                IsArrayBufferSpecies(cx, getter))
+            {
+                return defaultCtor;
+            }
+        }
+
+        if (!TypedArrayObject::ensureHasBuffer(cx, typedArray))
+            return nullptr;
+
+        obj.set(typedArray->bufferObject());
+    } else {
+        if (isWrapped && !cx->compartment()->wrap(cx, &obj))
+            return nullptr;
+    }
+
+    return SpeciesConstructor(cx, obj, defaultCtor, IsArrayBufferSpecies);
 }
 
 template<typename T>
 /* static */ JSObject*
 TypedArrayObjectTemplate<T>::fromArray(JSContext* cx, HandleObject other,
                                        HandleObject proto /* = nullptr */)
 {
     // Allow nullptr proto for FriendAPI methods, which don't care about
@@ -1125,37 +1157,37 @@ TypedArrayObjectTemplate<T>::fromTypedAr
     // Step 2 (Already performed in caller).
 
     // Steps 3-4 (Allocation deferred until later).
 
     // Step 5.
     Rooted<TypedArrayObject*> srcArray(cx);
     if (!isWrapped) {
         srcArray = &other->as<TypedArrayObject>();
-        if (!TypedArrayObject::ensureHasBuffer(cx, srcArray))
-            return nullptr;
     } else {
         RootedObject unwrapped(cx, CheckedUnwrap(other));
         if (!unwrapped) {
             ReportAccessDenied(cx);
             return nullptr;
         }
 
         JSAutoCompartment ac(cx, unwrapped);
 
         srcArray = &unwrapped->as<TypedArrayObject>();
+
+        // To keep things simpler, we always reify the array buffer for
+        // wrapped typed arrays.
         if (!TypedArrayObject::ensureHasBuffer(cx, srcArray))
             return nullptr;
     }
 
-    // Step 6.
-    Rooted<ArrayBufferObjectMaybeShared*> srcData(cx, srcArray->bufferEither());
+    // Step 6 (skipped).
 
     // Step 7.
-    if (srcData->isDetached()) {
+    if (srcArray->hasDetachedBuffer()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
         return nullptr;
     }
 
     // Step 9.
     uint32_t elementLength = srcArray->length();
 
     // Steps 10-11.
@@ -1163,25 +1195,25 @@ TypedArrayObjectTemplate<T>::fromTypedAr
 
     // Steps 12-13 (skipped).
 
     // Steps 16-17.
     bool isShared = srcArray->isSharedMemory();
     SpeciesConstructorOverride override = isShared ? SpeciesConstructorOverride::ArrayBuffer
                                                    : SpeciesConstructorOverride::None;
 
-    RootedObject bufferCtor(cx, GetSpeciesConstructor(cx, srcData, isWrapped, override));
+    RootedObject bufferCtor(cx, GetBufferSpeciesConstructor(cx, srcArray, isWrapped, override));
     if (!bufferCtor)
         return nullptr;
 
     // Steps 8, 18-19.
     Rooted<ArrayBufferObject*> buffer(cx);
     if (ArrayTypeID() == srcType) {
         // Step 18.a.
-        if (srcData->isDetached()) {
+        if (srcArray->hasDetachedBuffer()) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
             return nullptr;
         }
 
         // Step 15.
         uint32_t byteLength = srcArray->byteLength();
 
         // Step 18.b.
@@ -1190,17 +1222,17 @@ TypedArrayObjectTemplate<T>::fromTypedAr
             return nullptr;
     } else {
         // Steps 14-15, 19.a.
         if (!AllocateArrayBuffer(cx, bufferCtor, elementLength, BYTES_PER_ELEMENT, &buffer))
             return nullptr;
     }
 
     // Step 19.b or 24.1.1.4 step 4.
-    if (srcData->isDetached()) {
+    if (srcArray->hasDetachedBuffer()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
         return nullptr;
     }
 
     // Steps 3-4 (remaining part), 20-23.
     Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, elementLength, proto));
     if (!obj)
         return nullptr;