Make __proto__ a foreign callback on Object.prototype.

This moves the __proto__ property to Object.prototype and turns it into
a callback property actually present in the descriptor array as opposed
to a hack in the properties lookup. For now it still is a "magic" data
property using foreign callbacks and not an accessor property visible to
JavaScript.

The second effect of this change is that JSON.parse() no longer treats
the __proto__ property specially, it will be defined as any other data
property. Note that object literals still have their special handling.

R=rossberg@chromium.org
BUG=v8:621,v8:1949,v8:2441
TEST=mjsunit,cctest,test262

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13728 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
mstarzinger@chromium.org 2013-02-26 10:46:00 +00:00
parent 72b802f27a
commit ce1e10f5fc
13 changed files with 85 additions and 59 deletions

View File

@ -485,10 +485,26 @@ Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) {
native_context()->set_object_function(*object_fun);
// Allocate a new prototype for the object function.
Handle<JSObject> prototype = factory->NewJSObject(
isolate->object_function(),
TENURED);
Handle<Map> object_prototype_map =
factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
Handle<DescriptorArray> prototype_descriptors(
factory->NewDescriptorArray(0, 1));
DescriptorArray::WhitenessWitness witness(*prototype_descriptors);
Handle<Foreign> object_prototype(
factory->NewForeign(&Accessors::ObjectPrototype));
PropertyAttributes attribs = static_cast<PropertyAttributes>(
DONT_ENUM | DONT_DELETE);
object_prototype_map->set_instance_descriptors(*prototype_descriptors);
{ // Add __proto__.
CallbacksDescriptor d(heap->Proto_symbol(), *object_prototype, attribs);
object_prototype_map->AppendDescriptor(&d, witness);
}
Handle<JSObject> prototype = factory->NewJSObjectFromMap(
object_prototype_map,
TENURED);
native_context()->set_initial_object_prototype(*prototype);
SetPrototype(object_fun, prototype);
}

View File

@ -950,10 +950,11 @@ Handle<GlobalObject> Factory::NewGlobalObject(
Handle<JSObject> Factory::NewJSObjectFromMap(Handle<Map> map) {
Handle<JSObject> Factory::NewJSObjectFromMap(Handle<Map> map,
PretenureFlag pretenure) {
CALL_HEAP_FUNCTION(
isolate(),
isolate()->heap()->AllocateJSObjectFromMap(*map, NOT_TENURED),
isolate()->heap()->AllocateJSObjectFromMap(*map, pretenure),
JSObject);
}

View File

@ -278,7 +278,8 @@ class Factory {
// JS objects are pretenured when allocated by the bootstrapper and
// runtime.
Handle<JSObject> NewJSObjectFromMap(Handle<Map> map);
Handle<JSObject> NewJSObjectFromMap(Handle<Map> map,
PretenureFlag pretenure = NOT_TENURED);
// JS modules are pretenured.
Handle<JSModule> NewJSModule(Handle<Context> context,

View File

@ -291,7 +291,6 @@ Handle<Object> JsonParser<seq_ascii>::ParseJsonValue() {
// Parse a JSON object. Position must be right at '{'.
template <bool seq_ascii>
Handle<Object> JsonParser<seq_ascii>::ParseJsonObject() {
Handle<Object> prototype;
Handle<JSObject> json_object =
factory()->NewJSObject(object_constructor(), pretenure_);
ASSERT_EQ(c0_, '{');
@ -346,22 +345,17 @@ Handle<Object> JsonParser<seq_ascii>::ParseJsonObject() {
Handle<Object> value = ParseJsonValue();
if (value.is_null()) return ReportUnexpectedCharacter();
if (key->Equals(isolate()->heap()->Proto_symbol())) {
prototype = value;
if (JSObject::TryTransitionToField(json_object, key)) {
int index = json_object->LastAddedFieldIndex();
json_object->FastPropertyAtPut(index, *value);
} else {
if (JSObject::TryTransitionToField(json_object, key)) {
int index = json_object->LastAddedFieldIndex();
json_object->FastPropertyAtPut(index, *value);
} else {
JSObject::SetLocalPropertyIgnoreAttributes(
json_object, key, value, NONE);
}
JSObject::SetLocalPropertyIgnoreAttributes(
json_object, key, value, NONE);
}
} while (MatchSkipWhiteSpace(','));
if (c0_ != '}') {
return ReportUnexpectedCharacter();
}
if (!prototype.is_null()) SetPrototype(json_object, prototype);
}
AdvanceSkipWhitespace();
return json_object;

View File

@ -4602,12 +4602,6 @@ void JSReceiver::LocalLookup(
JSObject* js_object = JSObject::cast(this);
// Check __proto__ before interceptor.
if (name->Equals(heap->Proto_symbol()) && !IsJSContextExtensionObject()) {
result->ConstantResult(js_object);
return;
}
// Check for lookup interceptor except when bootstrapping.
if (js_object->HasNamedInterceptor() &&
!heap->isolate()->bootstrapper()->IsActive()) {

View File

@ -201,16 +201,6 @@ class LookupResult BASE_EMBEDDED {
number_ = number;
}
void ConstantResult(JSObject* holder) {
lookup_type_ = CONSTANT_TYPE;
holder_ = holder;
details_ =
PropertyDetails(static_cast<PropertyAttributes>(DONT_ENUM |
DONT_DELETE),
CALLBACKS);
number_ = -1;
}
void DictionaryResult(JSObject* holder, int entry) {
lookup_type_ = DICTIONARY_TYPE;
holder_ = holder;
@ -427,10 +417,7 @@ class LookupResult BASE_EMBEDDED {
}
Object* GetCallbackObject() {
if (lookup_type_ == CONSTANT_TYPE) {
return HEAP->prototype_accessors();
}
ASSERT(!IsTransition());
ASSERT(type() == CALLBACKS && !IsTransition());
return GetValue();
}
@ -466,8 +453,7 @@ class LookupResult BASE_EMBEDDED {
TRANSITION_TYPE,
DICTIONARY_TYPE,
HANDLER_TYPE,
INTERCEPTOR_TYPE,
CONSTANT_TYPE
INTERCEPTOR_TYPE
} lookup_type_;
JSReceiver* holder_;

View File

@ -1849,8 +1849,8 @@ THREADED_TEST(PropertyHandlerInPrototype) {
Local<v8::Object> top = templ->GetFunction()->NewInstance();
Local<v8::Object> middle = templ->GetFunction()->NewInstance();
bottom->Set(v8_str("__proto__"), middle);
middle->Set(v8_str("__proto__"), top);
bottom->SetPrototype(middle);
middle->SetPrototype(top);
env->Global()->Set(v8_str("obj"), bottom);
// Indexed and named get.
@ -9670,7 +9670,7 @@ THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
call_ic_function4 =
v8_compile("function f(x) { return x - 1; }; f")->Run();
v8::Handle<Value> value = CompileRun(
"o.__proto__.x = function(x) { return x + 1; };"
"Object.getPrototypeOf(o).x = function(x) { return x + 1; };"
"var result = 0;"
"for (var i = 0; i < 1000; i++) {"
" result = o.x(42);"

View File

@ -54,7 +54,7 @@ function checkConstructor(func, name) {
assertFalse(proto_desc.writable, name);
assertFalse(proto_desc.configurable, name);
var prototype = proto_desc.value;
assertEquals(null, prototype.__proto__, name);
assertEquals(null, Object.getPrototypeOf(prototype), name);
for (var i = 0; i < propNames.length; i++) {
var propName = propNames[i];
if (propName == "constructor") continue;

View File

@ -898,7 +898,7 @@ var q = {bar: 'no'};
obj.__proto__ = p;
obj.__proto__ = p; // ignored
obj.__proto__ = null;
obj.__proto__ = q;
obj.__proto__ = q; // the __proto__ accessor is gone
// TODO(adamk): Add tests for objects with hidden prototypes
// once we support observing the global object.
Object.deliverChangeRecords(observer.callback);
@ -906,7 +906,7 @@ observer.assertCallbackRecords([
{ object: obj, name: '__proto__', type: 'prototype',
oldValue: Object.prototype },
{ object: obj, name: '__proto__', type: 'prototype', oldValue: p },
{ object: obj, name: '__proto__', type: 'prototype', oldValue: null },
{ object: obj, name: '__proto__', type: 'new' },
]);

View File

@ -2294,7 +2294,6 @@ function TestConstructorWithProxyPrototype2(create, handler) {
C.prototype = create(handler);
var o = new C;
assertSame(C.prototype, o.__proto__);
assertSame(C.prototype, Object.getPrototypeOf(o));
}

View File

@ -453,16 +453,23 @@ falseNum.__proto__ = Number.prototype;
falseNum.toString = function() { return 42; };
assertEquals('"42"', JSON.stringify(falseNum));
// We don't currently allow plain properties called __proto__ in JSON
// objects in JSON.parse. Instead we read them as we would JS object
// literals. If we change that, this test should change with it.
//
// Parse a non-object value as __proto__. This must not create a
// __proto__ property different from the original, and should not
// change the original.
var o = JSON.parse('{"__proto__":5}');
assertEquals(Object.prototype, o.__proto__); // __proto__ isn't changed.
assertEquals(0, Object.keys(o).length); // __proto__ isn't added as enumerable.
// Parse an object value as __proto__.
var o1 = JSON.parse('{"__proto__":[]}');
assertEquals([], o1.__proto__);
assertEquals(["__proto__"], Object.keys(o1));
assertEquals([], Object.getOwnPropertyDescriptor(o1, "__proto__").value);
assertEquals(["__proto__"], Object.getOwnPropertyNames(o1));
assertTrue(o1.hasOwnProperty("__proto__"));
assertTrue(Object.prototype.isPrototypeOf(o1));
// Parse a non-object value as __proto__.
var o2 = JSON.parse('{"__proto__":5}');
assertEquals(5, o2.__proto__);
assertEquals(["__proto__"], Object.keys(o2));
assertEquals(5, Object.getOwnPropertyDescriptor(o2, "__proto__").value);
assertEquals(["__proto__"], Object.getOwnPropertyNames(o2));
assertTrue(o2.hasOwnProperty("__proto__"));
assertTrue(Object.prototype.isPrototypeOf(o2));
var json = '{"stuff before slash\\\\stuff after slash":"whatever"}';
assertEquals(json, JSON.stringify(JSON.parse(json)));

View File

@ -0,0 +1,31 @@
// Copyright 2013 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.
var o = {};
Object.preventExtensions(o);
assertThrows("Object.defineProperty(o, 'foobarloo', {value:{}});", TypeError);
assertThrows("Object.defineProperty(o, '__proto__', {value:{}});", TypeError);

View File

@ -30,9 +30,6 @@ def FAIL_OK = FAIL, OKAY
############################### BUGS ###################################
# '__proto__' should be treated as a normal property in JSON.
S15.12.2_A1: FAIL
# Sequencing of getter side effects on receiver and argument properties
# is wrong. The receiver callback should be called before any arguments
# are evaluated.