Implement sealing, freezing, and related functions for proxies.
R=ager@chromium.org BUG=v8:1543 TEST= Review URL: http://codereview.chromium.org/7391001 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8673 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
ce75f236f4
commit
5e62e325ac
@ -892,6 +892,13 @@ Handle<JSProxy> Factory::NewJSProxy(Handle<Object> handler,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Factory::BecomeJSObject(Handle<JSProxy> object) {
|
||||||
|
CALL_HEAP_FUNCTION_VOID(
|
||||||
|
isolate(),
|
||||||
|
isolate()->heap()->ReinitializeJSProxyAsJSObject(*object));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo(
|
Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo(
|
||||||
Handle<String> name,
|
Handle<String> name,
|
||||||
int number_of_literals,
|
int number_of_literals,
|
||||||
|
@ -253,6 +253,9 @@ class Factory {
|
|||||||
|
|
||||||
Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object> prototype);
|
Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object> prototype);
|
||||||
|
|
||||||
|
// Change the type of the argument into a regular JS object and reinitialize.
|
||||||
|
void BecomeJSObject(Handle<JSProxy> object);
|
||||||
|
|
||||||
Handle<JSFunction> NewFunction(Handle<String> name,
|
Handle<JSFunction> NewFunction(Handle<String> name,
|
||||||
Handle<Object> prototype);
|
Handle<Object> prototype);
|
||||||
|
|
||||||
|
33
src/heap.cc
33
src/heap.cc
@ -3267,14 +3267,13 @@ MaybeObject* Heap::AllocateJSProxy(Object* handler, Object* prototype) {
|
|||||||
MaybeObject* maybe_map_obj = AllocateMap(JS_PROXY_TYPE, JSProxy::kSize);
|
MaybeObject* maybe_map_obj = AllocateMap(JS_PROXY_TYPE, JSProxy::kSize);
|
||||||
if (!maybe_map_obj->To<Map>(&map)) return maybe_map_obj;
|
if (!maybe_map_obj->To<Map>(&map)) return maybe_map_obj;
|
||||||
map->set_prototype(prototype);
|
map->set_prototype(prototype);
|
||||||
map->set_pre_allocated_property_fields(1);
|
|
||||||
map->set_inobject_properties(1);
|
|
||||||
|
|
||||||
// Allocate the proxy object.
|
// Allocate the proxy object.
|
||||||
Object* result;
|
Object* result;
|
||||||
MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
|
MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
|
||||||
if (!maybe_result->ToObject(&result)) return maybe_result;
|
if (!maybe_result->ToObject(&result)) return maybe_result;
|
||||||
JSProxy::cast(result)->set_handler(handler);
|
JSProxy::cast(result)->set_handler(handler);
|
||||||
|
JSProxy::cast(result)->set_padding(Smi::FromInt(0));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3414,6 +3413,36 @@ MaybeObject* Heap::CopyJSObject(JSObject* source) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MaybeObject* Heap::ReinitializeJSProxyAsJSObject(JSProxy* object) {
|
||||||
|
// Allocate fresh map.
|
||||||
|
// TODO(rossberg): Once we optimize proxies, cache these maps.
|
||||||
|
Map* map;
|
||||||
|
MaybeObject* maybe_map_obj =
|
||||||
|
AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
|
||||||
|
if (!maybe_map_obj->To<Map>(&map)) return maybe_map_obj;
|
||||||
|
|
||||||
|
// Check that the receiver has the same size as a fresh object.
|
||||||
|
ASSERT(map->instance_size() == object->map()->instance_size());
|
||||||
|
|
||||||
|
map->set_prototype(object->map()->prototype());
|
||||||
|
|
||||||
|
// Allocate the backing storage for the properties.
|
||||||
|
int prop_size = map->unused_property_fields() - map->inobject_properties();
|
||||||
|
Object* properties;
|
||||||
|
{ MaybeObject* maybe_properties = AllocateFixedArray(prop_size, TENURED);
|
||||||
|
if (!maybe_properties->ToObject(&properties)) return maybe_properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the map for the object.
|
||||||
|
object->set_map(map);
|
||||||
|
|
||||||
|
// Reinitialize the object from the constructor map.
|
||||||
|
InitializeJSObjectFromMap(JSObject::cast(object),
|
||||||
|
FixedArray::cast(properties), map);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
MaybeObject* Heap::ReinitializeJSGlobalProxy(JSFunction* constructor,
|
MaybeObject* Heap::ReinitializeJSGlobalProxy(JSFunction* constructor,
|
||||||
JSGlobalProxy* object) {
|
JSGlobalProxy* object) {
|
||||||
ASSERT(constructor->has_initial_map());
|
ASSERT(constructor->has_initial_map());
|
||||||
|
@ -441,6 +441,11 @@ class Heap {
|
|||||||
MUST_USE_RESULT MaybeObject* AllocateJSProxy(Object* handler,
|
MUST_USE_RESULT MaybeObject* AllocateJSProxy(Object* handler,
|
||||||
Object* prototype);
|
Object* prototype);
|
||||||
|
|
||||||
|
// Reinitialize a JSProxy into an (empty) JSObject. The receiver
|
||||||
|
// must have the same size as an empty object. The object is reinitialized
|
||||||
|
// and behaves as an object that has been freshly allocated.
|
||||||
|
MUST_USE_RESULT MaybeObject* ReinitializeJSProxyAsJSObject(JSProxy* object);
|
||||||
|
|
||||||
// Reinitialize an JSGlobalProxy based on a constructor. The object
|
// Reinitialize an JSGlobalProxy based on a constructor. The object
|
||||||
// must have the same size as objects allocated using the
|
// must have the same size as objects allocated using the
|
||||||
// constructor. The object is reinitialized and behaves as an
|
// constructor. The object is reinitialized and behaves as an
|
||||||
|
@ -195,7 +195,8 @@ function FormatMessage(message) {
|
|||||||
non_extensible_proto: ["%0", " is not extensible"],
|
non_extensible_proto: ["%0", " is not extensible"],
|
||||||
handler_non_object: ["Proxy.", "%0", " called with non-object as handler"],
|
handler_non_object: ["Proxy.", "%0", " called with non-object as handler"],
|
||||||
handler_trap_missing: ["Proxy handler ", "%0", " has no '", "%1", "' trap"],
|
handler_trap_missing: ["Proxy handler ", "%0", " has no '", "%1", "' trap"],
|
||||||
handler_failed: ["Proxy handler ", "%0", " returned false for '", "%1", "' trap"],
|
handler_returned_false: ["Proxy handler ", "%0", " returned false for '", "%1", "' trap"],
|
||||||
|
handler_returned_undefined: ["Proxy handler ", "%0", " returned undefined for '", "%1", "' trap"],
|
||||||
proxy_prop_not_configurable: ["Trap ", "%1", " of proxy handler ", "%0", " returned non-configurable descriptor for property ", "%2"],
|
proxy_prop_not_configurable: ["Trap ", "%1", " of proxy handler ", "%0", " returned non-configurable descriptor for property ", "%2"],
|
||||||
proxy_non_object_prop_names: ["Trap ", "%1", " returned non-object ", "%0"],
|
proxy_non_object_prop_names: ["Trap ", "%1", " returned non-object ", "%0"],
|
||||||
proxy_repeated_prop_name: ["Trap ", "%1", " returned repeated property name ", "%2"],
|
proxy_repeated_prop_name: ["Trap ", "%1", " returned repeated property name ", "%2"],
|
||||||
|
@ -3761,6 +3761,7 @@ void JSBuiltinsObject::set_javascript_builtin_code(Builtins::JavaScript id,
|
|||||||
|
|
||||||
|
|
||||||
ACCESSORS(JSProxy, handler, Object, kHandlerOffset)
|
ACCESSORS(JSProxy, handler, Object, kHandlerOffset)
|
||||||
|
ACCESSORS(JSProxy, padding, Object, kPaddingOffset)
|
||||||
|
|
||||||
|
|
||||||
Address Foreign::address() {
|
Address Foreign::address() {
|
||||||
|
@ -2301,6 +2301,18 @@ MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JSProxy::Fix() {
|
||||||
|
Isolate* isolate = GetIsolate();
|
||||||
|
HandleScope scope(isolate);
|
||||||
|
Handle<JSProxy> self(this);
|
||||||
|
|
||||||
|
isolate->factory()->BecomeJSObject(self);
|
||||||
|
ASSERT(IsJSObject());
|
||||||
|
// TODO(rossberg): recognize function proxies.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
||||||
String* name,
|
String* name,
|
||||||
Object* value,
|
Object* value,
|
||||||
|
@ -6475,6 +6475,9 @@ class JSProxy: public JSReceiver {
|
|||||||
// [handler]: The handler property.
|
// [handler]: The handler property.
|
||||||
DECL_ACCESSORS(handler, Object)
|
DECL_ACCESSORS(handler, Object)
|
||||||
|
|
||||||
|
// [padding]: The padding slot (unused, see below).
|
||||||
|
DECL_ACCESSORS(padding, Object)
|
||||||
|
|
||||||
// Casting.
|
// Casting.
|
||||||
static inline JSProxy* cast(Object* obj);
|
static inline JSProxy* cast(Object* obj);
|
||||||
|
|
||||||
@ -6493,6 +6496,9 @@ class JSProxy: public JSReceiver {
|
|||||||
String* name,
|
String* name,
|
||||||
bool* has_exception);
|
bool* has_exception);
|
||||||
|
|
||||||
|
// Turn this into an (empty) JSObject.
|
||||||
|
void Fix();
|
||||||
|
|
||||||
// Dispatched behavior.
|
// Dispatched behavior.
|
||||||
#ifdef OBJECT_PRINT
|
#ifdef OBJECT_PRINT
|
||||||
inline void JSProxyPrint() {
|
inline void JSProxyPrint() {
|
||||||
@ -6504,9 +6510,14 @@ class JSProxy: public JSReceiver {
|
|||||||
void JSProxyVerify();
|
void JSProxyVerify();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Layout description.
|
// Layout description. We add padding so that a proxy has the same
|
||||||
|
// size as a virgin JSObject. This is essential for becoming a JSObject
|
||||||
|
// upon freeze.
|
||||||
static const int kHandlerOffset = HeapObject::kHeaderSize;
|
static const int kHandlerOffset = HeapObject::kHeaderSize;
|
||||||
static const int kSize = kHandlerOffset + kPointerSize;
|
static const int kPaddingOffset = kHandlerOffset + kPointerSize;
|
||||||
|
static const int kSize = kPaddingOffset + kPointerSize;
|
||||||
|
|
||||||
|
STATIC_CHECK(kSize == JSObject::kHeaderSize);
|
||||||
|
|
||||||
typedef FixedBodyDescriptor<kHandlerOffset,
|
typedef FixedBodyDescriptor<kHandlerOffset,
|
||||||
kHandlerOffset + kPointerSize,
|
kHandlerOffset + kPointerSize,
|
||||||
|
@ -615,6 +615,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetHandler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RUNTIME_FUNCTION(MaybeObject*, Runtime_Fix) {
|
||||||
|
ASSERT(args.length() == 1);
|
||||||
|
CONVERT_CHECKED(JSProxy, proxy, args[0]);
|
||||||
|
proxy->Fix();
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_ClassOf) {
|
RUNTIME_FUNCTION(MaybeObject*, Runtime_ClassOf) {
|
||||||
NoHandleAllocation ha;
|
NoHandleAllocation ha;
|
||||||
ASSERT(args.length() == 1);
|
ASSERT(args.length() == 1);
|
||||||
|
@ -284,6 +284,7 @@ namespace internal {
|
|||||||
F(CreateJSProxy, 2, 1) \
|
F(CreateJSProxy, 2, 1) \
|
||||||
F(IsJSProxy, 1, 1) \
|
F(IsJSProxy, 1, 1) \
|
||||||
F(GetHandler, 1, 1) \
|
F(GetHandler, 1, 1) \
|
||||||
|
F(Fix, 1, 1) \
|
||||||
\
|
\
|
||||||
/* Statements */ \
|
/* Statements */ \
|
||||||
F(NewClosure, 3, 1) \
|
F(NewClosure, 3, 1) \
|
||||||
|
@ -663,7 +663,8 @@ function DefineProxyProperty(obj, p, attributes, should_throw) {
|
|||||||
var result = %_CallFunction(handler, p, attributes, defineProperty);
|
var result = %_CallFunction(handler, p, attributes, defineProperty);
|
||||||
if (!ToBoolean(result)) {
|
if (!ToBoolean(result)) {
|
||||||
if (should_throw) {
|
if (should_throw) {
|
||||||
throw MakeTypeError("handler_failed", [handler, "defineProperty"]);
|
throw MakeTypeError("handler_returned_false",
|
||||||
|
[handler, "defineProperty"]);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1020,11 +1021,30 @@ function ObjectDefineProperties(obj, properties) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Harmony proxies.
|
||||||
|
function ProxyFix(obj) {
|
||||||
|
var handler = %GetHandler(obj);
|
||||||
|
var fix = handler.fix;
|
||||||
|
if (IS_UNDEFINED(fix)) {
|
||||||
|
throw MakeTypeError("handler_trap_missing", [handler, "fix"]);
|
||||||
|
}
|
||||||
|
var props = %_CallFunction(handler, fix);
|
||||||
|
if (IS_UNDEFINED(props)) {
|
||||||
|
throw MakeTypeError("handler_returned_undefined", [handler, "fix"]);
|
||||||
|
}
|
||||||
|
%Fix(obj);
|
||||||
|
ObjectDefineProperties(obj, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ES5 section 15.2.3.8.
|
// ES5 section 15.2.3.8.
|
||||||
function ObjectSeal(obj) {
|
function ObjectSeal(obj) {
|
||||||
if (!IS_SPEC_OBJECT(obj)) {
|
if (!IS_SPEC_OBJECT(obj)) {
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["seal"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["seal"]);
|
||||||
}
|
}
|
||||||
|
if (%IsJSProxy(obj)) {
|
||||||
|
ProxyFix(obj);
|
||||||
|
}
|
||||||
var names = ObjectGetOwnPropertyNames(obj);
|
var names = ObjectGetOwnPropertyNames(obj);
|
||||||
for (var i = 0; i < names.length; i++) {
|
for (var i = 0; i < names.length; i++) {
|
||||||
var name = names[i];
|
var name = names[i];
|
||||||
@ -1034,7 +1054,8 @@ function ObjectSeal(obj) {
|
|||||||
DefineOwnProperty(obj, name, desc, true);
|
DefineOwnProperty(obj, name, desc, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ObjectPreventExtension(obj);
|
%PreventExtensions(obj);
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1043,6 +1064,9 @@ function ObjectFreeze(obj) {
|
|||||||
if (!IS_SPEC_OBJECT(obj)) {
|
if (!IS_SPEC_OBJECT(obj)) {
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["freeze"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["freeze"]);
|
||||||
}
|
}
|
||||||
|
if (%IsJSProxy(obj)) {
|
||||||
|
ProxyFix(obj);
|
||||||
|
}
|
||||||
var names = ObjectGetOwnPropertyNames(obj);
|
var names = ObjectGetOwnPropertyNames(obj);
|
||||||
for (var i = 0; i < names.length; i++) {
|
for (var i = 0; i < names.length; i++) {
|
||||||
var name = names[i];
|
var name = names[i];
|
||||||
@ -1053,7 +1077,8 @@ function ObjectFreeze(obj) {
|
|||||||
DefineOwnProperty(obj, name, desc, true);
|
DefineOwnProperty(obj, name, desc, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ObjectPreventExtension(obj);
|
%PreventExtensions(obj);
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1062,6 +1087,9 @@ function ObjectPreventExtension(obj) {
|
|||||||
if (!IS_SPEC_OBJECT(obj)) {
|
if (!IS_SPEC_OBJECT(obj)) {
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
|
||||||
}
|
}
|
||||||
|
if (%IsJSProxy(obj)) {
|
||||||
|
ProxyFix(obj);
|
||||||
|
}
|
||||||
%PreventExtensions(obj);
|
%PreventExtensions(obj);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@ -1072,6 +1100,9 @@ function ObjectIsSealed(obj) {
|
|||||||
if (!IS_SPEC_OBJECT(obj)) {
|
if (!IS_SPEC_OBJECT(obj)) {
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["isSealed"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["isSealed"]);
|
||||||
}
|
}
|
||||||
|
if (%IsJSProxy(obj)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
var names = ObjectGetOwnPropertyNames(obj);
|
var names = ObjectGetOwnPropertyNames(obj);
|
||||||
for (var i = 0; i < names.length; i++) {
|
for (var i = 0; i < names.length; i++) {
|
||||||
var name = names[i];
|
var name = names[i];
|
||||||
@ -1090,6 +1121,9 @@ function ObjectIsFrozen(obj) {
|
|||||||
if (!IS_SPEC_OBJECT(obj)) {
|
if (!IS_SPEC_OBJECT(obj)) {
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["isFrozen"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["isFrozen"]);
|
||||||
}
|
}
|
||||||
|
if (%IsJSProxy(obj)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
var names = ObjectGetOwnPropertyNames(obj);
|
var names = ObjectGetOwnPropertyNames(obj);
|
||||||
for (var i = 0; i < names.length; i++) {
|
for (var i = 0; i < names.length; i++) {
|
||||||
var name = names[i];
|
var name = names[i];
|
||||||
@ -1109,6 +1143,9 @@ function ObjectIsExtensible(obj) {
|
|||||||
if (!IS_SPEC_OBJECT(obj)) {
|
if (!IS_SPEC_OBJECT(obj)) {
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["isExtensible"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["isExtensible"]);
|
||||||
}
|
}
|
||||||
|
if (%IsJSProxy(obj)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return %IsExtensible(obj);
|
return %IsExtensible(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,3 +529,84 @@ TestKeys([], {
|
|||||||
},
|
},
|
||||||
getOwnPropertyDescriptor: function(k) { return {} }
|
getOwnPropertyDescriptor: function(k) { return {} }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Fixing (Object.freeze, Object.seal, Object.preventExtensions,
|
||||||
|
// Object.isFrozen, Object.isSealed, Object.isExtensible)
|
||||||
|
|
||||||
|
function TestFix(names, handler) {
|
||||||
|
var proto = {p: 77}
|
||||||
|
var assertFixing = function(o, s, f, e) {
|
||||||
|
assertEquals(s, Object.isSealed(o))
|
||||||
|
assertEquals(f, Object.isFrozen(o))
|
||||||
|
assertEquals(e, Object.isExtensible(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
var o1 = Proxy.create(handler, proto)
|
||||||
|
assertFixing(o1, false, false, true)
|
||||||
|
Object.seal(o1)
|
||||||
|
assertFixing(o1, true, names.length === 0, false)
|
||||||
|
assertArrayEquals(names.sort(), Object.getOwnPropertyNames(o1).sort())
|
||||||
|
assertArrayEquals(names.filter(function(x) {return x < "z"}).sort(),
|
||||||
|
Object.keys(o1).sort())
|
||||||
|
assertEquals(proto, Object.getPrototypeOf(o1))
|
||||||
|
assertEquals(77, o1.p)
|
||||||
|
for (var n in o1) {
|
||||||
|
var desc = Object.getOwnPropertyDescriptor(o1, n)
|
||||||
|
if (desc !== undefined) assertFalse(desc.configurable)
|
||||||
|
}
|
||||||
|
|
||||||
|
var o2 = Proxy.create(handler, proto)
|
||||||
|
assertFixing(o2, false, false, true)
|
||||||
|
Object.freeze(o2)
|
||||||
|
assertFixing(o2, true, true, false)
|
||||||
|
assertArrayEquals(names.sort(), Object.getOwnPropertyNames(o2).sort())
|
||||||
|
assertArrayEquals(names.filter(function(x) {return x < "z"}).sort(),
|
||||||
|
Object.keys(o2).sort())
|
||||||
|
assertEquals(proto, Object.getPrototypeOf(o2))
|
||||||
|
assertEquals(77, o2.p)
|
||||||
|
for (var n in o2) {
|
||||||
|
var desc = Object.getOwnPropertyDescriptor(o2, n)
|
||||||
|
if (desc !== undefined) assertFalse(desc.writable)
|
||||||
|
if (desc !== undefined) assertFalse(desc.configurable)
|
||||||
|
}
|
||||||
|
|
||||||
|
var o3 = Proxy.create(handler, proto)
|
||||||
|
assertFixing(o3, false, false, true)
|
||||||
|
Object.preventExtensions(o3)
|
||||||
|
assertFixing(o3, names.length === 0, names.length === 0, false)
|
||||||
|
assertArrayEquals(names.sort(), Object.getOwnPropertyNames(o3).sort())
|
||||||
|
assertArrayEquals(names.filter(function(x) {return x < "z"}).sort(),
|
||||||
|
Object.keys(o3).sort())
|
||||||
|
assertEquals(proto, Object.getPrototypeOf(o3))
|
||||||
|
assertEquals(77, o3.p)
|
||||||
|
}
|
||||||
|
|
||||||
|
TestFix([], {
|
||||||
|
fix: function() { return {} }
|
||||||
|
})
|
||||||
|
TestFix(["a", "b", "c", "d", "zz"], {
|
||||||
|
fix: function() {
|
||||||
|
return {
|
||||||
|
a: {value: "a", writable: true, configurable: false, enumerable: true},
|
||||||
|
b: {value: 33, writable: false, configurable: false, enumerable: true},
|
||||||
|
c: {value: 0, writable: true, configurable: true, enumerable: true},
|
||||||
|
d: {value: true, writable: false, configurable: true, enumerable: true},
|
||||||
|
zz: {value: 0, enumerable: false}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
TestFix(["a"], {
|
||||||
|
fix: function() { return this.fix2() },
|
||||||
|
fix2: function() {
|
||||||
|
return {a: {value: 4, writable: true, configurable: true, enumerable: true}}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
TestFix(["b"], {
|
||||||
|
get fix() {
|
||||||
|
return function() {
|
||||||
|
return {b: {configurable: true, writable: true, enumerable: true}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user