зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1448838: Add native version for CopyDataProperties. r=jandem
This commit is contained in:
Родитель
e7a7eb2bdf
Коммит
61f80eae24
|
@ -219,67 +219,78 @@ function GetInternalError(msg) {
|
|||
// To be used when a function is required but calling it shouldn't do anything.
|
||||
function NullFunction() {}
|
||||
|
||||
// Object Rest/Spread Properties proposal
|
||||
// Abstract operation: CopyDataProperties (target, source, excluded)
|
||||
function CopyDataProperties(target, source, excluded) {
|
||||
// ES2019 draft rev 4c2df13f4194057f09b920ee88712e5a70b1a556
|
||||
// 7.3.23 CopyDataProperties (target, source, excludedItems)
|
||||
function CopyDataProperties(target, source, excludedItems) {
|
||||
// Step 1.
|
||||
assert(IsObject(target), "target is an object");
|
||||
|
||||
// Step 2.
|
||||
assert(IsObject(excluded), "excluded is an object");
|
||||
assert(IsObject(excludedItems), "excludedItems is an object");
|
||||
|
||||
// Steps 3, 6.
|
||||
// Steps 3 and 7.
|
||||
if (source === undefined || source === null)
|
||||
return;
|
||||
|
||||
// Step 4.a.
|
||||
source = ToObject(source);
|
||||
|
||||
// Step 4.b.
|
||||
var keys = OwnPropertyKeys(source);
|
||||
// Step 4.
|
||||
var from = ToObject(source);
|
||||
|
||||
// Step 5.
|
||||
var keys = CopyDataPropertiesOrGetOwnKeys(target, from, excludedItems);
|
||||
|
||||
// Return if we copied all properties in native code.
|
||||
if (keys === null)
|
||||
return;
|
||||
|
||||
// Step 6.
|
||||
for (var index = 0; index < keys.length; index++) {
|
||||
var key = keys[index];
|
||||
|
||||
// We abbreviate this by calling propertyIsEnumerable which is faster
|
||||
// and returns false for not defined properties.
|
||||
if (!hasOwn(key, excluded) && callFunction(std_Object_propertyIsEnumerable, source, key))
|
||||
_DefineDataProperty(target, key, source[key]);
|
||||
if (!hasOwn(key, excludedItems) &&
|
||||
callFunction(std_Object_propertyIsEnumerable, from, key))
|
||||
{
|
||||
_DefineDataProperty(target, key, from[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6 (Return).
|
||||
// Step 7 (Return).
|
||||
}
|
||||
|
||||
// Object Rest/Spread Properties proposal
|
||||
// Abstract operation: CopyDataProperties (target, source, excluded)
|
||||
// ES2019 draft rev 4c2df13f4194057f09b920ee88712e5a70b1a556
|
||||
// 7.3.23 CopyDataProperties (target, source, excludedItems)
|
||||
function CopyDataPropertiesUnfiltered(target, source) {
|
||||
// Step 1.
|
||||
assert(IsObject(target), "target is an object");
|
||||
|
||||
// Step 2 (Not applicable).
|
||||
|
||||
// Steps 3, 6.
|
||||
// Steps 3 and 7.
|
||||
if (source === undefined || source === null)
|
||||
return;
|
||||
|
||||
// Step 4.a.
|
||||
source = ToObject(source);
|
||||
|
||||
// Step 4.b.
|
||||
var keys = OwnPropertyKeys(source);
|
||||
// Step 4.
|
||||
var from = ToObject(source);
|
||||
|
||||
// Step 5.
|
||||
var keys = CopyDataPropertiesOrGetOwnKeys(target, from, null);
|
||||
|
||||
// Return if we copied all properties in native code.
|
||||
if (keys === null)
|
||||
return;
|
||||
|
||||
// Step 6.
|
||||
for (var index = 0; index < keys.length; index++) {
|
||||
var key = keys[index];
|
||||
|
||||
// We abbreviate this by calling propertyIsEnumerable which is faster
|
||||
// and returns false for not defined properties.
|
||||
if (callFunction(std_Object_propertyIsEnumerable, source, key))
|
||||
_DefineDataProperty(target, key, source[key]);
|
||||
if (callFunction(std_Object_propertyIsEnumerable, from, key))
|
||||
_DefineDataProperty(target, key, from[key]);
|
||||
}
|
||||
|
||||
// Step 6 (Return).
|
||||
// Step 7 (Return).
|
||||
}
|
||||
|
||||
/*************************************** Testing functions ***************************************/
|
||||
|
|
|
@ -10,7 +10,8 @@ function tryCreateUnboxedObject() {
|
|||
for (var i = 0; i < 1000; ++i) {
|
||||
obj = new Unboxed();
|
||||
}
|
||||
|
||||
if (unboxedObjectsEnabled())
|
||||
assertEq(isUnboxedObject(obj), true);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
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();
|
||||
}
|
||||
if (unboxedObjectsEnabled())
|
||||
assertEq(isUnboxedObject(obj), true);
|
||||
return obj;
|
||||
}
|
||||
|
||||
function basic() {
|
||||
var unboxed = tryCreateUnboxedObject();
|
||||
|
||||
var {...target} = unboxed;
|
||||
assertDeepEq(target, {a: 0, b: true});
|
||||
|
||||
var {a, c, ...target} = unboxed;
|
||||
assertDeepEq(a, 0);
|
||||
assertDeepEq(c, undefined);
|
||||
assertDeepEq(target, {b: true});
|
||||
}
|
||||
|
||||
function expando() {
|
||||
var unboxed = tryCreateUnboxedObject();
|
||||
unboxed.c = 3.5;
|
||||
|
||||
var {...target} = unboxed;
|
||||
assertDeepEq(target, {a: 0, b: true, c: 3.5});
|
||||
|
||||
var {a, d, ...target} = unboxed;
|
||||
assertDeepEq(a, 0);
|
||||
assertDeepEq(d, undefined);
|
||||
assertDeepEq(target, {b: true, c: 3.5});
|
||||
}
|
||||
|
||||
basic();
|
||||
expando();
|
|
@ -0,0 +1,90 @@
|
|||
function test() {
|
||||
var from, to;
|
||||
|
||||
// From values.
|
||||
from = {x: 1, y: 2};
|
||||
({...to} = from);
|
||||
assertEq(to.y, 2);
|
||||
|
||||
var z;
|
||||
from = {x: 1, y: 2};
|
||||
({x: z, ...to} = from);
|
||||
assertEq(z, 1);
|
||||
assertEq(to.y, 2);
|
||||
|
||||
// From getter.
|
||||
var c = 7;
|
||||
from = {x: 1, get y() { return ++c; }};
|
||||
({...to} = from);
|
||||
assertEq(c, 8);
|
||||
assertEq(to.y, 8);
|
||||
|
||||
from = {x: 1, get y() { return ++c; }};
|
||||
({y: z, ...to} = from);
|
||||
assertEq(c, 9);
|
||||
assertEq(z, 9);
|
||||
assertEq(to.y, undefined);
|
||||
|
||||
// Array with dense elements.
|
||||
from = [1, 2, 3];
|
||||
({...to} = from);
|
||||
assertEq(to[2], 3);
|
||||
assertEq("length" in to, false);
|
||||
|
||||
from = [1, 2, 3];
|
||||
({2: z, ...to} = from);
|
||||
assertEq(z, 3);
|
||||
assertEq(to[2], undefined);
|
||||
assertEq(to[0], 1);
|
||||
assertEq("length" in to, false);
|
||||
|
||||
// Object with sparse elements and symbols.
|
||||
from = {x: 1, 1234567: 2, 1234560: 3, [Symbol.iterator]: 5, z: 3};
|
||||
({...to} = from);
|
||||
assertEq(to[1234567], 2);
|
||||
assertEq(Object.keys(to).toString(), "1234560,1234567,x,z");
|
||||
assertEq(to[Symbol.iterator], 5);
|
||||
|
||||
from = {x: 1, 1234567: 2, 1234560: 3, [Symbol.iterator]: 5, z: 3};
|
||||
({[Symbol.iterator]: z, ...to} = from);
|
||||
assertEq(to[1234567], 2);
|
||||
assertEq(Object.keys(to).toString(), "1234560,1234567,x,z");
|
||||
assertEq(to[Symbol.iterator], undefined);
|
||||
assertEq(z, 5);
|
||||
|
||||
// Typed array.
|
||||
from = new Int32Array([1, 2, 3]);
|
||||
({...to} = from);
|
||||
assertEq(to[1], 2);
|
||||
|
||||
from = new Int32Array([1, 2, 3]);
|
||||
({1: z, ...to} = from);
|
||||
assertEq(z, 2);
|
||||
assertEq(to[1], undefined);
|
||||
assertEq(to[2], 3);
|
||||
|
||||
// Primitive string.
|
||||
from = "foo";
|
||||
({...to} = from);
|
||||
assertEq(to[0], "f");
|
||||
|
||||
from = "foo";
|
||||
({0: z, ...to} = from);
|
||||
assertEq(z, "f");
|
||||
assertEq(to[0], undefined);
|
||||
assertEq(to[1], "o");
|
||||
|
||||
// String object.
|
||||
from = new String("bar");
|
||||
({...to} = from);
|
||||
assertEq(to[2], "r");
|
||||
|
||||
from = new String("bar");
|
||||
({1: z, ...to} = from);
|
||||
assertEq(z, "a");
|
||||
assertEq(to[1], undefined);
|
||||
assertEq(to[2], "r");
|
||||
}
|
||||
test();
|
||||
test();
|
||||
test();
|
|
@ -0,0 +1,40 @@
|
|||
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();
|
||||
}
|
||||
if (unboxedObjectsEnabled())
|
||||
assertEq(isUnboxedObject(obj), true);
|
||||
return obj;
|
||||
}
|
||||
|
||||
function basic() {
|
||||
var unboxed = tryCreateUnboxedObject();
|
||||
|
||||
var target = {...unboxed};
|
||||
assertDeepEq(target, {a: 0, b: true});
|
||||
|
||||
target = {a: 1, c: 3, ...unboxed};
|
||||
assertDeepEq(target, {a: 0, c: 3, b: true});
|
||||
}
|
||||
|
||||
function expando() {
|
||||
var unboxed = tryCreateUnboxedObject();
|
||||
unboxed.c = 3.5;
|
||||
|
||||
var target = {...unboxed};
|
||||
assertDeepEq(target, {a: 0, b: true, c: 3.5});
|
||||
|
||||
target = {a: 1, d: 3, ...unboxed};
|
||||
assertDeepEq(target, {a: 0, d: 3, b: true, c: 3.5});
|
||||
}
|
||||
|
||||
basic();
|
||||
expando();
|
|
@ -0,0 +1,49 @@
|
|||
function test() {
|
||||
var from, to;
|
||||
|
||||
// From values.
|
||||
from = {x: 1, y: 2};
|
||||
to = {...from};
|
||||
assertEq(to.y, 2);
|
||||
to = {...from, ...from};
|
||||
assertEq(to.y, 2);
|
||||
|
||||
// From getter.
|
||||
var c = 7;
|
||||
from = {x: 1, get y() { return ++c; }};
|
||||
to = {...from};
|
||||
assertEq(to.y, 8);
|
||||
to = {...from, ...from};
|
||||
assertEq(to.y, 10);
|
||||
|
||||
// Array with dense elements.
|
||||
from = [1, 2, 3];
|
||||
to = {...from};
|
||||
assertEq(to[2], 3);
|
||||
assertEq("length" in to, false);
|
||||
|
||||
// Object with sparse elements and symbols.
|
||||
from = {x: 1, 1234567: 2, 1234560: 3, [Symbol.iterator]: 5, z: 3};
|
||||
to = {...from};
|
||||
assertEq(to[1234567], 2);
|
||||
assertEq(Object.keys(to).toString(), "1234560,1234567,x,z");
|
||||
assertEq(to[Symbol.iterator], 5);
|
||||
|
||||
// Typed array.
|
||||
from = new Int32Array([1, 2, 3]);
|
||||
to = {...from};
|
||||
assertEq(to[1], 2);
|
||||
|
||||
// Primitive string.
|
||||
from = "foo";
|
||||
to = {...from};
|
||||
assertEq(to[0], "f");
|
||||
|
||||
// String object.
|
||||
from = new String("bar");
|
||||
to = {...from};
|
||||
assertEq(to[2], "r");
|
||||
}
|
||||
test();
|
||||
test();
|
||||
test();
|
|
@ -9,17 +9,20 @@
|
|||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
#include "gc/Marking.h"
|
||||
#include "js/Value.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
#include "vm/UnboxedObject.h"
|
||||
|
||||
#include "gc/Nursery-inl.h"
|
||||
#include "vm/ArrayObject-inl.h"
|
||||
#include "vm/EnvironmentObject-inl.h"
|
||||
#include "vm/JSObject-inl.h"
|
||||
#include "vm/Shape-inl.h"
|
||||
#include "vm/UnboxedObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
|
@ -1498,8 +1501,8 @@ AddOrChangeProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
|
|||
return CallAddPropertyHook(cx, obj, id, desc.value());
|
||||
}
|
||||
|
||||
// Version of AddOrChangeProperty optimized for adding a plain data property.
|
||||
// This function doesn't handle integer ids as we may have to store them in
|
||||
// Versions of AddOrChangeProperty optimized for adding a plain data property.
|
||||
// These function doesn't handle integer ids as we may have to store them in
|
||||
// dense elements.
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
AddDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v)
|
||||
|
@ -1518,6 +1521,24 @@ AddDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue
|
|||
return CallAddPropertyHook(cx, obj, id, v);
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
AddDataPropertyNonDelegate(JSContext* cx, HandlePlainObject obj, HandleId id, HandleValue v)
|
||||
{
|
||||
MOZ_ASSERT(!JSID_IS_INT(id));
|
||||
MOZ_ASSERT(!obj->isDelegate());
|
||||
|
||||
// If we know this is a new property we can call addProperty instead of
|
||||
// the slower putProperty.
|
||||
Shape* shape = NativeObject::addEnumerableDataProperty(cx, obj, id);
|
||||
if (!shape)
|
||||
return false;
|
||||
|
||||
UpdateShapeTypeAndValueForWritableDataProp(cx, obj, shape, id, v);
|
||||
|
||||
MOZ_ASSERT(!obj->getClass()->getAddProperty());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsConfigurable(unsigned attrs) { return (attrs & JSPROP_PERMANENT) == 0; }
|
||||
static bool IsEnumerable(unsigned attrs) { return (attrs & JSPROP_ENUMERATE) != 0; }
|
||||
static bool IsWritable(unsigned attrs) { return (attrs & JSPROP_READONLY) == 0; }
|
||||
|
@ -2895,3 +2916,143 @@ js::NativeDeleteProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
|
|||
|
||||
return SuppressDeletedProperty(cx, obj, id);
|
||||
}
|
||||
|
||||
bool
|
||||
js::CopyDataPropertiesNative(JSContext* cx, HandlePlainObject target, HandleNativeObject from,
|
||||
HandlePlainObject excludedItems, bool* optimized)
|
||||
{
|
||||
MOZ_ASSERT(!target->isDelegate(),
|
||||
"CopyDataPropertiesNative should only be called during object literal construction"
|
||||
"which precludes that |target| is the prototype of any other object");
|
||||
|
||||
*optimized = false;
|
||||
|
||||
// Don't use the fast path if |from| may have extra indexed or lazy
|
||||
// properties.
|
||||
if (from->getDenseInitializedLength() > 0 ||
|
||||
from->isIndexed() ||
|
||||
from->is<TypedArrayObject>() ||
|
||||
from->getClass()->getNewEnumerate() ||
|
||||
from->getClass()->getEnumerate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Collect all enumerable data properties.
|
||||
using ShapeVector = GCVector<Shape*, 8>;
|
||||
Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
|
||||
|
||||
RootedShape fromShape(cx, from->lastProperty());
|
||||
for (Shape::Range<NoGC> r(fromShape); !r.empty(); r.popFront()) {
|
||||
Shape* shape = &r.front();
|
||||
jsid id = shape->propid();
|
||||
MOZ_ASSERT(!JSID_IS_INT(id));
|
||||
|
||||
if (!shape->enumerable())
|
||||
continue;
|
||||
if (excludedItems && excludedItems->contains(cx, id))
|
||||
continue;
|
||||
|
||||
// Don't use the fast path if |from| contains non-data properties.
|
||||
//
|
||||
// This enables two optimizations:
|
||||
// 1. We don't need to handle the case when accessors modify |from|.
|
||||
// 2. String and symbol properties can be added in one go.
|
||||
if (!shape->isDataProperty())
|
||||
return true;
|
||||
|
||||
if (!shapes.append(shape))
|
||||
return false;
|
||||
}
|
||||
|
||||
*optimized = true;
|
||||
|
||||
// If |target| contains no own properties, we can directly call
|
||||
// addProperty instead of the slower putProperty.
|
||||
const bool targetHadNoOwnProperties = target->lastProperty()->isEmptyShape();
|
||||
|
||||
RootedId key(cx);
|
||||
RootedValue value(cx);
|
||||
for (size_t i = shapes.length(); i > 0; i--) {
|
||||
Shape* shape = shapes[i - 1];
|
||||
MOZ_ASSERT(shape->isDataProperty());
|
||||
MOZ_ASSERT(shape->enumerable());
|
||||
|
||||
key = shape->propid();
|
||||
MOZ_ASSERT(!JSID_IS_INT(key));
|
||||
|
||||
MOZ_ASSERT(from->isNative());
|
||||
MOZ_ASSERT(from->lastProperty() == fromShape);
|
||||
|
||||
value = from->getSlot(shape->slot());
|
||||
if (targetHadNoOwnProperties) {
|
||||
MOZ_ASSERT(!target->contains(cx, key),
|
||||
"didn't expect to find an existing property");
|
||||
|
||||
if (!AddDataPropertyNonDelegate(cx, target, key, value))
|
||||
return false;
|
||||
} else {
|
||||
if (!NativeDefineDataProperty(cx, target, key, value, JSPROP_ENUMERATE))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::CopyDataPropertiesNative(JSContext* cx, HandlePlainObject target,
|
||||
Handle<UnboxedPlainObject*> from, HandlePlainObject excludedItems,
|
||||
bool* optimized)
|
||||
{
|
||||
MOZ_ASSERT(!target->isDelegate(),
|
||||
"CopyDataPropertiesNative should only be called during object literal construction"
|
||||
"which precludes that |target| is the prototype of any other object");
|
||||
|
||||
*optimized = false;
|
||||
|
||||
// Don't use the fast path for unboxed objects with expandos.
|
||||
if (from->maybeExpando())
|
||||
return true;
|
||||
|
||||
*optimized = true;
|
||||
|
||||
// If |target| contains no own properties, we can directly call
|
||||
// addProperty instead of the slower putProperty.
|
||||
const bool targetHadNoOwnProperties = target->lastProperty()->isEmptyShape();
|
||||
|
||||
#ifdef DEBUG
|
||||
RootedObjectGroup fromGroup(cx, from->group());
|
||||
#endif
|
||||
|
||||
RootedId key(cx);
|
||||
RootedValue value(cx);
|
||||
const UnboxedLayout& layout = from->layout();
|
||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
||||
const UnboxedLayout::Property& property = layout.properties()[i];
|
||||
key = NameToId(property.name);
|
||||
MOZ_ASSERT(!JSID_IS_INT(key));
|
||||
|
||||
if (excludedItems && excludedItems->contains(cx, key))
|
||||
continue;
|
||||
|
||||
// Ensure the object stays unboxed.
|
||||
MOZ_ASSERT(from->group() == fromGroup);
|
||||
|
||||
// All unboxed properties are enumerable.
|
||||
value = from->getValue(property);
|
||||
|
||||
if (targetHadNoOwnProperties) {
|
||||
MOZ_ASSERT(!target->contains(cx, key),
|
||||
"didn't expect to find an existing property");
|
||||
|
||||
if (!AddDataPropertyNonDelegate(cx, target, key, value))
|
||||
return false;
|
||||
} else {
|
||||
if (!NativeDefineDataProperty(cx, target, key, value, JSPROP_ENUMERATE))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ namespace js {
|
|||
|
||||
class Shape;
|
||||
class TenuringTracer;
|
||||
class UnboxedPlainObject;
|
||||
|
||||
/*
|
||||
* To really poison a set of values, using 'magic' or 'undefined' isn't good
|
||||
|
@ -1607,6 +1608,16 @@ bool IsPackedArray(JSObject* obj);
|
|||
extern void
|
||||
AddPropertyTypesAfterProtoChange(JSContext* cx, NativeObject* obj, ObjectGroup* oldGroup);
|
||||
|
||||
// Specializations of 7.3.23 CopyDataProperties(...) for NativeObjects.
|
||||
extern bool
|
||||
CopyDataPropertiesNative(JSContext* cx, HandlePlainObject target, HandleNativeObject from,
|
||||
HandlePlainObject excludedItems, bool* optimized);
|
||||
|
||||
extern bool
|
||||
CopyDataPropertiesNative(JSContext* cx, HandlePlainObject target,
|
||||
Handle<UnboxedPlainObject*> from, HandlePlainObject excludedItems,
|
||||
bool* optimized);
|
||||
|
||||
} // namespace js
|
||||
|
||||
|
||||
|
|
|
@ -2159,6 +2159,59 @@ intrinsic_PromiseResolve(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
intrinsic_CopyDataPropertiesOrGetOwnKeys(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 3);
|
||||
MOZ_ASSERT(args[0].isObject());
|
||||
MOZ_ASSERT(args[1].isObject());
|
||||
MOZ_ASSERT(args[2].isObjectOrNull());
|
||||
|
||||
RootedObject target(cx, &args[0].toObject());
|
||||
RootedObject from(cx, &args[1].toObject());
|
||||
RootedObject excludedItems(cx, args[2].toObjectOrNull());
|
||||
|
||||
if (from->isNative() &&
|
||||
target->is<PlainObject>() &&
|
||||
(!excludedItems || excludedItems->is<PlainObject>()))
|
||||
{
|
||||
bool optimized;
|
||||
if (!CopyDataPropertiesNative(cx, target.as<PlainObject>(), from.as<NativeObject>(),
|
||||
(excludedItems ? excludedItems.as<PlainObject>() : nullptr),
|
||||
&optimized))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (optimized) {
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (from->is<UnboxedPlainObject>() &&
|
||||
target->is<PlainObject>() &&
|
||||
(!excludedItems || excludedItems->is<PlainObject>()))
|
||||
{
|
||||
bool optimized;
|
||||
if (!CopyDataPropertiesNative(cx, target.as<PlainObject>(), from.as<UnboxedPlainObject>(),
|
||||
(excludedItems ? excludedItems.as<PlainObject>() : nullptr),
|
||||
&optimized))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (optimized) {
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return GetOwnPropertyKeys(cx, from, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
|
||||
args.rval());
|
||||
}
|
||||
|
||||
// The self-hosting global isn't initialized with the normal set of builtins.
|
||||
// Instead, individual C++-implemented functions that're required by
|
||||
// self-hosted code are defined as global functions. Accessing these
|
||||
|
@ -2284,6 +2337,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
|||
JS_FN("AddContentTelemetry", intrinsic_AddContentTelemetry, 2,0),
|
||||
JS_FN("_DefineDataProperty", intrinsic_DefineDataProperty, 4,0),
|
||||
JS_FN("_DefineProperty", intrinsic_DefineProperty, 6,0),
|
||||
JS_FN("CopyDataPropertiesOrGetOwnKeys", intrinsic_CopyDataPropertiesOrGetOwnKeys, 3,0),
|
||||
|
||||
JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing, 0,0,
|
||||
IntrinsicIsConstructing),
|
||||
|
|
Загрузка…
Ссылка в новой задаче