Bug 1411774 - Optimize Object.assign with unboxed objects. r=jandem

This commit is contained in:
Tom Schuster 2017-10-31 18:34:05 +01:00
Родитель d223c72b0e
Коммит 2864da25ae
2 изменённых файлов: 143 добавлений и 0 удалений

Просмотреть файл

@ -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();