зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1411774 - Optimize Object.assign with unboxed objects. r=jandem
This commit is contained in:
Родитель
d223c72b0e
Коммит
2864da25ae
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "vm/NativeObject-inl.h"
|
||||
#include "vm/Shape-inl.h"
|
||||
#include "vm/UnboxedObject-inl.h"
|
||||
|
||||
#ifdef FUZZING
|
||||
#include "builtin/TestingFunctions.h"
|
||||
|
@ -790,6 +791,59 @@ TryAssignNative(JSContext* cx, HandleObject to, HandleObject from, bool* optimiz
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
TryAssignFromUnboxed(JSContext* cx, HandleObject to, HandleObject from, bool* optimized)
|
||||
{
|
||||
*optimized = false;
|
||||
|
||||
if (!from->is<UnboxedPlainObject>() || !to->isNative())
|
||||
return true;
|
||||
|
||||
// Don't use the fast path for unboxed objects with expandos.
|
||||
UnboxedPlainObject* fromUnboxed = &from->as<UnboxedPlainObject>();
|
||||
if (fromUnboxed->maybeExpando())
|
||||
return true;
|
||||
|
||||
*optimized = true;
|
||||
|
||||
RootedObjectGroup fromGroup(cx, from->group());
|
||||
|
||||
RootedValue propValue(cx);
|
||||
RootedId nextKey(cx);
|
||||
RootedValue toReceiver(cx, ObjectValue(*to));
|
||||
|
||||
const UnboxedLayout& layout = fromUnboxed->layout();
|
||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
||||
const UnboxedLayout::Property& property = layout.properties()[i];
|
||||
nextKey = NameToId(property.name);
|
||||
|
||||
// All unboxed properties are enumerable.
|
||||
// Guard on the group to ensure that the object stays unboxed.
|
||||
// We can ignore expando properties added after the loop starts.
|
||||
if (MOZ_LIKELY(from->group() == fromGroup)) {
|
||||
propValue = from->as<UnboxedPlainObject>().getValue(property);
|
||||
} else {
|
||||
// |from| changed so we have to do the slower enumerability check
|
||||
// and GetProp.
|
||||
bool enumerable;
|
||||
if (!PropertyIsEnumerable(cx, from, nextKey, &enumerable))
|
||||
return false;
|
||||
if (!enumerable)
|
||||
continue;
|
||||
if (!GetProperty(cx, from, from, nextKey, &propValue))
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectOpResult result;
|
||||
if (MOZ_UNLIKELY(!SetProperty(cx, to, nextKey, propValue, toReceiver, result)))
|
||||
return false;
|
||||
if (MOZ_UNLIKELY(!result.checkStrict(cx, to, nextKey)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
AssignSlow(JSContext* cx, HandleObject to, HandleObject from)
|
||||
{
|
||||
|
@ -857,6 +911,11 @@ obj_assign(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (optimized)
|
||||
continue;
|
||||
|
||||
if (!TryAssignFromUnboxed(cx, to, from, &optimized))
|
||||
return false;
|
||||
if (optimized)
|
||||
continue;
|
||||
|
||||
if (!AssignSlow(cx, to, from))
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
load(libdir + "asserts.js");
|
||||
|
||||
function Unboxed() {
|
||||
this.a = 0;
|
||||
this.b = true;
|
||||
}
|
||||
|
||||
function tryCreateUnboxedObject() {
|
||||
var obj;
|
||||
for (var i = 0; i < 1000; ++i) {
|
||||
obj = new Unboxed();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function basic() {
|
||||
var unboxed = tryCreateUnboxedObject();
|
||||
|
||||
var target = {};
|
||||
Object.assign(target, unboxed);
|
||||
assertDeepEq(target, {a: 0, b: true});
|
||||
|
||||
target = {a: 1, c: 3};
|
||||
Object.assign(target, unboxed);
|
||||
assertDeepEq(target, {a: 0, c: 3, b: true});
|
||||
}
|
||||
|
||||
function expando() {
|
||||
var unboxed = tryCreateUnboxedObject();
|
||||
unboxed.c = 3.5;
|
||||
|
||||
var target = {};
|
||||
Object.assign(target, unboxed);
|
||||
assertDeepEq(target, {a: 0, b: true, c: 3.5});
|
||||
|
||||
target = {a: 1, d: 3};
|
||||
Object.assign(target, unboxed);
|
||||
assertDeepEq(target, {a: 0, d: 3, b: true, c: 3.5});
|
||||
}
|
||||
|
||||
function addExpando() {
|
||||
var unboxed = tryCreateUnboxedObject();
|
||||
|
||||
function setA(value) {
|
||||
assertEq(value, 0);
|
||||
unboxed.c = 3.5;
|
||||
}
|
||||
|
||||
var target = {};
|
||||
Object.defineProperty(target, "a", {set: setA});
|
||||
|
||||
var reference = {};
|
||||
Object.defineProperty(reference, "a", {set: setA});
|
||||
Object.defineProperty(reference, "b", {value: true, enumerable: true, configurable: true, writable: true});
|
||||
|
||||
Object.assign(target, unboxed);
|
||||
assertDeepEq(target, reference);
|
||||
}
|
||||
|
||||
function makeNative() {
|
||||
var unboxed = tryCreateUnboxedObject();
|
||||
|
||||
function setA(value) {
|
||||
assertEq(value, 0);
|
||||
Object.defineProperty(unboxed, "a", {writable: false});
|
||||
}
|
||||
|
||||
var target = {};
|
||||
Object.defineProperty(target, "a", {set: setA});
|
||||
|
||||
var reference = {};
|
||||
Object.defineProperty(reference, "a", {set: setA});
|
||||
Object.defineProperty(reference, "b", {value: true, enumerable: true, configurable: true, writable: true});
|
||||
|
||||
Object.assign(target, unboxed);
|
||||
assertDeepEq(target, reference);
|
||||
}
|
||||
|
||||
|
||||
basic();
|
||||
expando();
|
||||
addExpando();
|
||||
makeNative();
|
Загрузка…
Ссылка в новой задаче