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:
parent
72b802f27a
commit
ce1e10f5fc
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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()) {
|
||||
|
@ -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_;
|
||||
|
@ -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);"
|
||||
|
@ -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;
|
||||
|
@ -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' },
|
||||
]);
|
||||
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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)));
|
||||
|
31
test/mjsunit/regress/regress-2441.js
Normal file
31
test/mjsunit/regress/regress-2441.js
Normal 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);
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user