Commit 79634a3f authored by neis's avatar neis Committed by Commit bot

[es6] Partially implement Reflect.preventExtensions.

Ignore proxies for now.

R=rossberg
BUG=v8:3931
LOG=n

Review URL: https://codereview.chromium.org/1397853005

Cr-Commit-Position: refs/heads/master@{#31431}
parent 3e2e2062
...@@ -701,7 +701,8 @@ Handle<JSFunction> Genesis::GetThrowTypeErrorIntrinsic( ...@@ -701,7 +701,8 @@ Handle<JSFunction> Genesis::GetThrowTypeErrorIntrinsic(
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY)) static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY))
.Assert(); .Assert();
JSObject::PreventExtensions(function).Assert(); if (JSObject::PreventExtensions(function, THROW_ON_ERROR).IsNothing())
DCHECK(false);
return function; return function;
} }
...@@ -2197,6 +2198,8 @@ void Genesis::InitializeGlobal_harmony_reflect() { ...@@ -2197,6 +2198,8 @@ void Genesis::InitializeGlobal_harmony_reflect() {
Builtins::kReflectHas, 2, true); Builtins::kReflectHas, 2, true);
SimpleInstallFunction(reflect, "isExtensible", SimpleInstallFunction(reflect, "isExtensible",
Builtins::kReflectIsExtensible, 1, true); Builtins::kReflectIsExtensible, 1, true);
SimpleInstallFunction(reflect, "preventExtensions",
Builtins::kReflectPreventExtensions, 1, true);
} }
......
...@@ -1517,10 +1517,10 @@ BUILTIN(ReflectHas) { ...@@ -1517,10 +1517,10 @@ BUILTIN(ReflectHas) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name, ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name,
Object::ToName(isolate, key)); Object::ToName(isolate, key));
Maybe<bool> maybe = Maybe<bool> result =
JSReceiver::HasProperty(Handle<JSReceiver>::cast(target), name); JSReceiver::HasProperty(Handle<JSReceiver>::cast(target), name);
if (!maybe.IsJust()) return isolate->heap()->exception(); return result.IsJust() ? *isolate->factory()->ToBoolean(result.FromJust())
return *isolate->factory()->ToBoolean(maybe.FromJust()); : isolate->heap()->exception();
} }
...@@ -1554,6 +1554,26 @@ BUILTIN(ReflectIsExtensible) { ...@@ -1554,6 +1554,26 @@ BUILTIN(ReflectIsExtensible) {
} }
// ES6 section 26.1.12 Reflect.preventExtensions
BUILTIN(ReflectPreventExtensions) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
Handle<Object> target = args.at<Object>(1);
if (!target->IsJSReceiver()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kCalledOnNonObject,
isolate->factory()->NewStringFromAsciiChecked(
"Reflect.preventExtensions")));
}
Maybe<bool> result = JSReceiver::PreventExtensions(
Handle<JSReceiver>::cast(target), DONT_THROW);
return result.IsJust() ? *isolate->factory()->ToBoolean(result.FromJust())
: isolate->heap()->exception();
}
// ES6 section 20.3.4.45 Date.prototype [ @@toPrimitive ] ( hint ) // ES6 section 20.3.4.45 Date.prototype [ @@toPrimitive ] ( hint )
BUILTIN(DateToPrimitive) { BUILTIN(DateToPrimitive) {
HandleScope scope(isolate); HandleScope scope(isolate);
......
...@@ -63,6 +63,7 @@ enum BuiltinExtraArguments { ...@@ -63,6 +63,7 @@ enum BuiltinExtraArguments {
V(ReflectGet, NO_EXTRA_ARGUMENTS) \ V(ReflectGet, NO_EXTRA_ARGUMENTS) \
V(ReflectHas, NO_EXTRA_ARGUMENTS) \ V(ReflectHas, NO_EXTRA_ARGUMENTS) \
V(ReflectIsExtensible, NO_EXTRA_ARGUMENTS) \ V(ReflectIsExtensible, NO_EXTRA_ARGUMENTS) \
V(ReflectPreventExtensions, NO_EXTRA_ARGUMENTS) \
\ \
V(SymbolConstructor, NO_EXTRA_ARGUMENTS) \ V(SymbolConstructor, NO_EXTRA_ARGUMENTS) \
V(SymbolConstructor_ConstructStub, NO_EXTRA_ARGUMENTS) \ V(SymbolConstructor_ConstructStub, NO_EXTRA_ARGUMENTS) \
......
...@@ -6722,13 +6722,34 @@ bool JSObject::ReferencesObject(Object* obj) { ...@@ -6722,13 +6722,34 @@ bool JSObject::ReferencesObject(Object* obj) {
} }
Maybe<bool> JSObject::PreventExtensionsInternal(Handle<JSObject> object) { #define RETURN_FAILURE(isolate, should_throw, call) \
do { \
if ((should_throw) == DONT_THROW) { \
return Just(false); \
} else { \
isolate->Throw(*isolate->factory()->call); \
return Nothing<bool>(); \
} \
} while (false)
Maybe<bool> JSReceiver::PreventExtensions(Handle<JSReceiver> object,
ShouldThrow should_throw) {
if (!object->IsJSObject()) return Just(false);
// TODO(neis): Deal with proxies.
return JSObject::PreventExtensions(Handle<JSObject>::cast(object),
should_throw);
}
Maybe<bool> JSObject::PreventExtensions(Handle<JSObject> object,
ShouldThrow should_throw) {
Isolate* isolate = object->GetIsolate(); Isolate* isolate = object->GetIsolate();
if (!object->map()->is_extensible()) return Just(true); if (!object->map()->is_extensible()) return Just(true);
if (!object->HasSloppyArgumentsElements() && !object->map()->is_observed()) { if (!object->HasSloppyArgumentsElements() && !object->map()->is_observed()) {
return PreventExtensionsWithTransition<NONE>(object); return PreventExtensionsWithTransition<NONE>(object, should_throw);
} }
if (object->IsAccessCheckNeeded() && if (object->IsAccessCheckNeeded() &&
...@@ -6736,15 +6757,16 @@ Maybe<bool> JSObject::PreventExtensionsInternal(Handle<JSObject> object) { ...@@ -6736,15 +6757,16 @@ Maybe<bool> JSObject::PreventExtensionsInternal(Handle<JSObject> object) {
isolate->ReportFailedAccessCheck(object); isolate->ReportFailedAccessCheck(object);
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
UNREACHABLE(); UNREACHABLE();
return Just(false); RETURN_FAILURE(isolate, should_throw,
NewTypeError(MessageTemplate::kNoAccess));
} }
if (object->IsJSGlobalProxy()) { if (object->IsJSGlobalProxy()) {
PrototypeIterator iter(isolate, object); PrototypeIterator iter(isolate, object);
if (iter.IsAtEnd()) return Just(true); if (iter.IsAtEnd()) return Just(true);
DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
return PreventExtensionsInternal( return PreventExtensions(PrototypeIterator::GetCurrent<JSObject>(iter),
PrototypeIterator::GetCurrent<JSObject>(iter)); should_throw);
} }
// It's not possible to seal objects with external array elements // It's not possible to seal objects with external array elements
...@@ -6781,21 +6803,6 @@ Maybe<bool> JSObject::PreventExtensionsInternal(Handle<JSObject> object) { ...@@ -6781,21 +6803,6 @@ Maybe<bool> JSObject::PreventExtensionsInternal(Handle<JSObject> object) {
} }
static MaybeHandle<Object> ReturnObjectOrThrowTypeError(
Handle<JSObject> object, Maybe<bool> maybe, MessageTemplate::Template msg) {
if (!maybe.IsJust()) return MaybeHandle<Object>();
if (maybe.FromJust()) return object;
Isolate* isolate = object->GetIsolate();
THROW_NEW_ERROR(isolate, NewTypeError(msg), Object);
}
MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) {
return ReturnObjectOrThrowTypeError(object, PreventExtensionsInternal(object),
MessageTemplate::kCannotPreventExt);
}
bool JSObject::IsExtensible(Handle<JSObject> object) { bool JSObject::IsExtensible(Handle<JSObject> object) {
Isolate* isolate = object->GetIsolate(); Isolate* isolate = object->GetIsolate();
if (object->IsAccessCheckNeeded() && if (object->IsAccessCheckNeeded() &&
...@@ -6837,7 +6844,8 @@ static void ApplyAttributesToDictionary(Dictionary* dictionary, ...@@ -6837,7 +6844,8 @@ static void ApplyAttributesToDictionary(Dictionary* dictionary,
template <PropertyAttributes attrs> template <PropertyAttributes attrs>
Maybe<bool> JSObject::PreventExtensionsWithTransition(Handle<JSObject> object) { Maybe<bool> JSObject::PreventExtensionsWithTransition(
Handle<JSObject> object, ShouldThrow should_throw) {
STATIC_ASSERT(attrs == NONE || attrs == SEALED || attrs == FROZEN); STATIC_ASSERT(attrs == NONE || attrs == SEALED || attrs == FROZEN);
// Sealing/freezing sloppy arguments should be handled elsewhere. // Sealing/freezing sloppy arguments should be handled elsewhere.
...@@ -6850,6 +6858,8 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(Handle<JSObject> object) { ...@@ -6850,6 +6858,8 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(Handle<JSObject> object) {
isolate->ReportFailedAccessCheck(object); isolate->ReportFailedAccessCheck(object);
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
UNREACHABLE(); UNREACHABLE();
RETURN_FAILURE(isolate, should_throw,
NewTypeError(MessageTemplate::kNoAccess));
} }
if (object->IsJSGlobalProxy()) { if (object->IsJSGlobalProxy()) {
...@@ -6857,7 +6867,7 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(Handle<JSObject> object) { ...@@ -6857,7 +6867,7 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(Handle<JSObject> object) {
if (iter.IsAtEnd()) return Just(true); if (iter.IsAtEnd()) return Just(true);
DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
return PreventExtensionsWithTransition<attrs>( return PreventExtensionsWithTransition<attrs>(
PrototypeIterator::GetCurrent<JSObject>(iter)); PrototypeIterator::GetCurrent<JSObject>(iter), should_throw);
} }
// It's not possible to seal or freeze objects with external array elements // It's not possible to seal or freeze objects with external array elements
...@@ -6944,16 +6954,18 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(Handle<JSObject> object) { ...@@ -6944,16 +6954,18 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(Handle<JSObject> object) {
MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) { MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) {
return ReturnObjectOrThrowTypeError( return PreventExtensionsWithTransition<FROZEN>(object, THROW_ON_ERROR)
object, PreventExtensionsWithTransition<FROZEN>(object), .IsJust()
MessageTemplate::kCannotPreventExt); ? object
: MaybeHandle<Object>();
} }
MaybeHandle<Object> JSObject::Seal(Handle<JSObject> object) { MaybeHandle<Object> JSObject::Seal(Handle<JSObject> object) {
return ReturnObjectOrThrowTypeError( return PreventExtensionsWithTransition<SEALED>(object, THROW_ON_ERROR)
object, PreventExtensionsWithTransition<SEALED>(object), .IsJust()
MessageTemplate::kCannotPreventExt); ? object
: MaybeHandle<Object>();
} }
......
...@@ -1865,6 +1865,11 @@ class JSReceiver: public HeapObject { ...@@ -1865,6 +1865,11 @@ class JSReceiver: public HeapObject {
static bool GetOwnPropertyDescriptor(LookupIterator* it, static bool GetOwnPropertyDescriptor(LookupIterator* it,
PropertyDescriptor* desc); PropertyDescriptor* desc);
// Disallow further properties to be added to the object. This is
// ES6's [[PreventExtensions]] when passed DONT_THROW.
MUST_USE_RESULT static Maybe<bool> PreventExtensions(
Handle<JSReceiver> object, ShouldThrow should_throw);
// Tests for the fast common case for property enumeration. // Tests for the fast common case for property enumeration.
bool IsSimpleEnum(); bool IsSimpleEnum();
...@@ -2324,11 +2329,8 @@ class JSObject: public JSReceiver { ...@@ -2324,11 +2329,8 @@ class JSObject: public JSReceiver {
// Check whether this object references another object // Check whether this object references another object
bool ReferencesObject(Object* obj); bool ReferencesObject(Object* obj);
// Disallow further properties to be added to the oject. MUST_USE_RESULT static Maybe<bool> PreventExtensions(
MUST_USE_RESULT static Maybe<bool> PreventExtensionsInternal( Handle<JSObject> object, ShouldThrow should_throw);
Handle<JSObject> object); // ES [[PreventExtensions]]
MUST_USE_RESULT static MaybeHandle<Object> PreventExtensions(
Handle<JSObject> object); // ES Object.preventExtensions
static bool IsExtensible(Handle<JSObject> object); static bool IsExtensible(Handle<JSObject> object);
...@@ -2517,7 +2519,7 @@ class JSObject: public JSReceiver { ...@@ -2517,7 +2519,7 @@ class JSObject: public JSReceiver {
// attrs is one of NONE, SEALED, or FROZEN (depending on the operation). // attrs is one of NONE, SEALED, or FROZEN (depending on the operation).
template <PropertyAttributes attrs> template <PropertyAttributes attrs>
MUST_USE_RESULT static Maybe<bool> PreventExtensionsWithTransition( MUST_USE_RESULT static Maybe<bool> PreventExtensionsWithTransition(
Handle<JSObject> object); Handle<JSObject> object, ShouldThrow should_throw);
DISALLOW_IMPLICIT_CONSTRUCTORS(JSObject); DISALLOW_IMPLICIT_CONSTRUCTORS(JSObject);
}; };
......
...@@ -304,11 +304,10 @@ RUNTIME_FUNCTION(Runtime_GetOwnProperty) { ...@@ -304,11 +304,10 @@ RUNTIME_FUNCTION(Runtime_GetOwnProperty) {
RUNTIME_FUNCTION(Runtime_PreventExtensions) { RUNTIME_FUNCTION(Runtime_PreventExtensions) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 1); DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0); CONVERT_ARG_HANDLE_CHECKED(JSReceiver, obj, 0);
Handle<Object> result; if (JSReceiver::PreventExtensions(obj, THROW_ON_ERROR).IsNothing())
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, return isolate->heap()->exception();
JSObject::PreventExtensions(obj)); return *obj;
return *result;
} }
......
...@@ -864,7 +864,8 @@ RUNTIME_FUNCTION(Runtime_DeclareModules) { ...@@ -864,7 +864,8 @@ RUNTIME_FUNCTION(Runtime_DeclareModules) {
} }
} }
JSObject::PreventExtensions(module).Assert(); if (JSObject::PreventExtensions(module, THROW_ON_ERROR).IsNothing())
DCHECK(false);
} }
DCHECK(!isolate->has_pending_exception()); DCHECK(!isolate->has_pending_exception());
......
// Copyright 2010-2015 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Tests the Reflect.preventExtensions method - ES6 26.1.12.
// This is adapted from object-prevent-extensions.js.
// Flags: --allow-natives-syntax --harmony-reflect
var obj1 = {};
// Extensible defaults to true.
assertTrue(Object.isExtensible(obj1));
assertTrue(Reflect.preventExtensions(obj1));
// Make sure the is_extensible flag is set.
assertFalse(Object.isExtensible(obj1));
obj1.x = 42;
assertEquals(undefined, obj1.x);
// Try adding a new element.
obj1[1] = 42;
assertEquals(undefined, obj1[1]);
// Try when the object has an existing property.
var obj2 = {};
assertTrue(Object.isExtensible(obj2));
obj2.x = 42;
assertEquals(42, obj2.x);
assertTrue(Object.isExtensible(obj2));
assertTrue(Reflect.preventExtensions(obj2));
assertEquals(42, obj2.x);
obj2.y = 42;
// obj2.y should still be undefined.
assertEquals(undefined, obj2.y);
// Make sure we can still write values to obj.x.
obj2.x = 43;
assertEquals(43, obj2.x)
obj2.y = new function() { return 42; };
// obj2.y should still be undefined.
assertEquals(undefined, obj2.y);
assertEquals(43, obj2.x)
try {
Object.defineProperty(obj2, "y", {value: 42});
} catch (e) {
assertTrue(/object is not extensible/.test(e));
}
// obj2.y should still be undefined.
assertEquals(undefined, obj2.y);
assertEquals(43, obj2.x);
obj2[1] = 42;
assertEquals(undefined, obj2[1]);
var arr = new Array();
arr[1] = 10;
assertTrue(Reflect.preventExtensions(arr));
arr[2] = 42;
assertEquals(10, arr[1]);
// We should still be able to change existing elements.
arr[1]= 42;
assertEquals(42, arr[1]);
// Test the the extensible flag is not inherited.
var parent = {};
parent.x = 42;
assertTrue(Reflect.preventExtensions(parent));
var child = Object.create(parent);
// We should be able to add new properties to the child object.
child.y = 42;
// This should have no influence on the parent class.
parent.y = 29;
// Test that attributes on functions are also handled correctly.
function foo() {
return 42;
}
assertTrue(Reflect.preventExtensions(foo));
foo.x = 29;
assertEquals(undefined, foo.x);
// when Object.isExtensible(o) === false
// assignment should return right hand side value
var o = {};
assertTrue(Reflect.preventExtensions(o));
var v = o.v = 50;
assertEquals(undefined, o.v);
assertEquals(50, v);
// test same behavior as above, but for integer properties
var n = o[0] = 100;
assertEquals(undefined, o[0]);
assertEquals(100, n);
// Fast properties should remain fast
obj = { x: 42, y: 'foo' };
assertTrue(%HasFastProperties(obj));
assertTrue(Reflect.preventExtensions(obj));
assertFalse(Object.isExtensible(obj));
assertFalse(Object.isSealed(obj));
assertTrue(%HasFastProperties(obj));
// Non-extensible objects should share maps where possible
obj = { prop1: 1, prop2: 2 };
obj2 = { prop1: 3, prop2: 4 };
assertTrue(%HaveSameMap(obj, obj2));
assertTrue(Reflect.preventExtensions(obj));
assertTrue(Reflect.preventExtensions(obj2));
assertFalse(Object.isExtensible(obj));
assertFalse(Object.isExtensible(obj2));
assertFalse(Object.isSealed(obj));
assertFalse(Object.isSealed(obj2));
assertTrue(%HaveSameMap(obj, obj2));
// Non-extensible objects should share maps even when they have elements
obj = { prop1: 1, prop2: 2, 75: 'foo' };
obj2 = { prop1: 3, prop2: 4, 150: 'bar' };
assertTrue(%HaveSameMap(obj, obj2));
assertTrue(Reflect.preventExtensions(obj));
assertTrue(Reflect.preventExtensions(obj2));
assertFalse(Object.isExtensible(obj));
assertFalse(Object.isExtensible(obj2));
assertFalse(Object.isSealed(obj));
assertFalse(Object.isSealed(obj2));
assertTrue(%HaveSameMap(obj, obj2));
...@@ -247,7 +247,7 @@ function prepare(tgt) { ...@@ -247,7 +247,7 @@ function prepare(tgt) {
(function testReflectIsExtensibleOnObject() { (function testReflectIsExtensibleOnObject() {
// This should be the last test as it modifies the objects irreversibly. // This should be the last test on [objects] as it modifies them irreversibly.
for (let tgt of objects) { for (let tgt of objects) {
prepare(tgt); prepare(tgt);
if (tgt instanceof Int32Array) continue; // issue v8:4460 if (tgt instanceof Int32Array) continue; // issue v8:4460
...@@ -276,3 +276,26 @@ function prepare(tgt) { ...@@ -276,3 +276,26 @@ function prepare(tgt) {
// See reflect-enumerate*.js for further tests. // See reflect-enumerate*.js for further tests.
////////////////////////////////////////////////////////////////////////////////
// Reflect.preventExtensions
(function testReflectPreventExtensionsArity() {
assertEquals(1, Reflect.preventExtensions.length);
})();
(function testReflectPreventExtensionsOnNonObject() {
assertThrows(function() { Reflect.preventExtensions(); }, TypeError);
assertThrows(function() { Reflect.preventExtensions(42); }, TypeError);
assertThrows(function() { Reflect.preventExtensions(null); }, TypeError);
})();
// See reflect-prevent-extensions.js for further tests.
// TODO(neis): Need proxies to test the situation where
// [[preventExtensions]] returns false.
...@@ -88,7 +88,7 @@ Object.preventExtensions(arr); ...@@ -88,7 +88,7 @@ Object.preventExtensions(arr);
arr[2] = 42; arr[2] = 42;
assertEquals(10, arr[1]); assertEquals(10, arr[1]);
// We should still be able to change exiting elements. // We should still be able to change existing elements.
arr[1]= 42; arr[1]= 42;
assertEquals(42, arr[1]); assertEquals(42, arr[1]);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment