Move special __proto__ property to Object.prototype
authorbarraclough@apple.com <barraclough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 20 Feb 2012 21:14:48 +0000 (21:14 +0000)
committerbarraclough@apple.com <barraclough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 20 Feb 2012 21:14:48 +0000 (21:14 +0000)
https://bugs.webkit.org/show_bug.cgi?id=78409

Reviewed by Oliver Hunt.

Re-implement this as a regular accessor property.  This has three key benefits:
1) It makes it possible for objects to be given properties named __proto__.
2) Object.prototype.__proto__ can be deleted, preventing object prototypes from being changed.
3) This largely removes the magic used the implement __proto__, it can just be made a regular accessor property.

Source/JavaScriptCore:

* parser/Parser.cpp:
(JSC::::parseFunctionInfo):
    - No need to prohibit functions named __proto__.
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::reset):
    - Add __proto__ accessor to Object.prototype.
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::globalFuncProtoGetter):
(JSC::globalFuncProtoSetter):
    - Definition of the __proto__ accessor functions.
* runtime/JSGlobalObjectFunctions.h:
    - Declaration of the __proto__ accessor functions.
* runtime/JSObject.cpp:
(JSC::JSObject::put):
    - Remove the special handling for __proto__, there is still a check to allow for a fast guard for accessors excluding __proto__.
(JSC::JSObject::putDirectAccessor):
    - Track on the structure whether an object contains accessors other than one for __proto__.
(JSC::JSObject::defineOwnProperty):
    - No need to prohibit definition of own properties named __proto__.
* runtime/JSObject.h:
(JSC::JSObject::inlineGetOwnPropertySlot):
    - Remove the special handling for __proto__.
(JSC::JSValue::get):
    - Remove the special handling for __proto__.
* runtime/JSString.cpp:
(JSC::JSString::getOwnPropertySlot):
    - Remove the special handling for __proto__.
* runtime/JSValue.h:
(JSValue):
    - Made synthesizePrototype public (this may be needed by the __proto__ getter).
* runtime/ObjectConstructor.cpp:
(JSC::objectConstructorGetPrototypeOf):
    - Perform the security check & call prototype() directly.
* runtime/Structure.cpp:
(JSC::Structure::Structure):
    - Added 'ExcludingProto' variant of the 'hasGetterSetterProperties' state.
* runtime/Structure.h:
(JSC::Structure::hasGetterSetterPropertiesExcludingProto):
(JSC::Structure::setHasGetterSetterProperties):
(Structure):
    - Added 'ExcludingProto' variant of the 'hasGetterSetterProperties' state.

Source/WebCore:

* bindings/js/JSDOMWindowBase.cpp:
(WebCore::JSDOMWindowBase::allowsAccessFrom):
(WebCore):
    - expose allowsAccessFrom check to JSC.
* bindings/js/JSDOMWindowBase.h:
(JSDOMWindowBase):
    - expose allowsAccessFrom check to JSC.

LayoutTests:

* fast/js/Object-getOwnPropertyNames-expected.txt:
* fast/js/cyclic-prototypes-expected.txt:
* fast/js/parser-syntax-check-expected.txt:
* fast/js/preventExtensions-expected.txt:
* fast/js/prototypes-expected.txt:
    - Update results
* fast/js/script-tests/Object-getOwnPropertyNames.js:
    - __proto__ is now a property of Object Prototype.
* fast/js/script-tests/cyclic-prototypes.js:
    - setting an object's prototype to null removes __proto__ setter, future usage won't set prototype.
* fast/js/script-tests/parser-syntax-check.js:
    - Allow functions named __proto__
* fast/js/script-tests/preventExtensions.js:
    - Setting __proto__ should not throw.
* fast/js/script-tests/prototypes.js:
    - Objects may contained own properties named __proto__, add new test cases.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@108259 268f45cc-cd09-0410-ab3c-d52691b4dbfc

33 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/Window/script-tests/window-custom-prototype.js
LayoutTests/fast/dom/Window/window-custom-prototype-expected.txt
LayoutTests/fast/dom/wrapper-classes-expected.txt
LayoutTests/fast/js/Object-getOwnPropertyNames-expected.txt
LayoutTests/fast/js/cyclic-prototypes-expected.txt
LayoutTests/fast/js/parser-syntax-check-expected.txt
LayoutTests/fast/js/preventExtensions-expected.txt
LayoutTests/fast/js/prototypes-expected.txt
LayoutTests/fast/js/script-tests/Object-getOwnPropertyNames.js
LayoutTests/fast/js/script-tests/cyclic-prototypes.js
LayoutTests/fast/js/script-tests/parser-syntax-check.js
LayoutTests/fast/js/script-tests/preventExtensions.js
LayoutTests/fast/js/script-tests/prototypes.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def
Source/JavaScriptCore/interpreter/CallFrame.cpp
Source/JavaScriptCore/interpreter/CallFrame.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h
Source/JavaScriptCore/runtime/JSObject.cpp
Source/JavaScriptCore/runtime/JSObject.h
Source/JavaScriptCore/runtime/JSString.cpp
Source/JavaScriptCore/runtime/JSValue.h
Source/JavaScriptCore/runtime/ObjectConstructor.cpp
Source/JavaScriptCore/runtime/Structure.cpp
Source/JavaScriptCore/runtime/Structure.h
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSDOMWindowBase.cpp
Source/WebCore/bindings/js/JSDOMWindowBase.h

index 2cf1870..00ba3a8 100644 (file)
@@ -1,3 +1,32 @@
+2012-02-16  Gavin Barraclough  <barraclough@apple.com>
+
+        Move special __proto__ property to Object.prototype
+        https://bugs.webkit.org/show_bug.cgi?id=78409
+
+        Reviewed by Oliver Hunt.
+
+        Re-implement this as a regular accessor property.  This has three key benefits:
+        1) It makes it possible for objects to be given properties named __proto__.
+        2) Object.prototype.__proto__ can be deleted, preventing object prototypes from being changed.
+        3) This largely removes the magic used the implement __proto__, it can just be made a regular accessor property.
+
+        * fast/js/Object-getOwnPropertyNames-expected.txt:
+        * fast/js/cyclic-prototypes-expected.txt:
+        * fast/js/parser-syntax-check-expected.txt:
+        * fast/js/preventExtensions-expected.txt:
+        * fast/js/prototypes-expected.txt:
+            - Update results
+        * fast/js/script-tests/Object-getOwnPropertyNames.js:
+            - __proto__ is now a property of Object Prototype.
+        * fast/js/script-tests/cyclic-prototypes.js:
+            - setting an object's prototype to null removes __proto__ setter, future usage won't set prototype.
+        * fast/js/script-tests/parser-syntax-check.js:
+            - Allow functions named __proto__
+        * fast/js/script-tests/preventExtensions.js:
+            - Setting __proto__ should not throw.
+        * fast/js/script-tests/prototypes.js:
+            - Objects may contained own properties named __proto__, add new test cases.
+
 2012-02-20  Benjamin Poulain  <benjamin@webkit.org>
 
         Make JSCSSStyleDeclaration work directly with CSS Property ID
index d47e641..1ad8ea0 100644 (file)
@@ -10,9 +10,9 @@ shouldBe("__proto__ = 1; __proto__", "originalWindowPrototype");
 shouldBe("__proto__ = 'a string'; __proto__", "originalWindowPrototype");
 shouldBe("__proto__ = anotherObject; __proto__", "anotherObject");
 shouldThrow("anotherObject.__proto__ = window; __proto__", "'Error: cyclic __proto__ value'");
-shouldBe("__proto__ = null; __proto__", "null");
-shouldBe("__proto__ = 1; __proto__", "null");
-shouldBe("__proto__ = 'a string'; __proto__", "null");
+shouldBe("__proto__ = 1; __proto__", "anotherObject");
+shouldBe("__proto__ = 'a string'; __proto__", "anotherObject");
 shouldBe("__proto__ = anotherObject; __proto__", "anotherObject");
 shouldBe("__proto__ = originalWindowPrototype; __proto__", "originalWindowPrototype");
 shouldBe("anotherObject.__proto__ = window; anotherObject.__proto__", "window");
+shouldBe("__proto__ = null; __proto__", "null");
index 57a93b8..1cf4f5d 100644 (file)
@@ -9,12 +9,12 @@ PASS __proto__ = 1; __proto__ is originalWindowPrototype
 PASS __proto__ = 'a string'; __proto__ is originalWindowPrototype
 PASS __proto__ = anotherObject; __proto__ is anotherObject
 PASS anotherObject.__proto__ = window; __proto__ threw exception Error: cyclic __proto__ value.
-PASS __proto__ = null; __proto__ is null
-PASS __proto__ = 1; __proto__ is null
-PASS __proto__ = 'a string'; __proto__ is null
+PASS __proto__ = 1; __proto__ is anotherObject
+PASS __proto__ = 'a string'; __proto__ is anotherObject
 PASS __proto__ = anotherObject; __proto__ is anotherObject
 PASS __proto__ = originalWindowPrototype; __proto__ is originalWindowPrototype
 PASS anotherObject.__proto__ = window; anotherObject.__proto__ is window
+PASS __proto__ = null; __proto__ is null
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 18eb39d..f40a66a 100644 (file)
@@ -18,7 +18,7 @@ PASS jsWrapperClass(document.implementation) is 'DOMImplementation'
 PASS jsWrapperClass(document.implementation.__proto__) is 'DOMImplementationPrototype'
 PASS jsWrapperClass(document.implementation.constructor) is 'DOMImplementationConstructor'
 PASS jsWrapperClass(root.attributes) is 'NamedNodeMap'
-FAIL jsWrapperClass(root.attributes.__proto__) should be NamedNodeMapPrototype. Was Object.
+PASS jsWrapperClass(root.attributes.__proto__) is 'NamedNodeMapPrototype'
 FAIL jsWrapperClass(root.attributes.constructor) should be NamedNodeMapConstructor. Was Function.
 PASS jsWrapperClass(document.createNodeIterator(root, 0, null, false)) is 'NodeIterator'
 PASS jsWrapperClass(document.createNodeIterator(root, 0, null, false).__proto__) is 'NodeIteratorPrototype'
@@ -135,13 +135,13 @@ PASS jsWrapperClass(window.constructor) is 'DOMWindowConstructor'
 HTML DOM
 
 PASS jsWrapperClass(root.children) is 'HTMLCollection'
-FAIL jsWrapperClass(root.children.__proto__) should be HTMLCollectionPrototype. Was Object.
+PASS jsWrapperClass(root.children.__proto__) is 'HTMLCollectionPrototype'
 FAIL jsWrapperClass(root.children.constructor) should be HTMLCollectionConstructor. Was Function.
 PASS jsWrapperClass(document) is 'HTMLDocument'
 PASS jsWrapperClass(document.__proto__) is 'HTMLDocumentPrototype'
 PASS jsWrapperClass(document.constructor) is 'HTMLDocumentConstructor'
 PASS jsWrapperClass(document.createElement('select').options) is 'HTMLOptionsCollection'
-FAIL jsWrapperClass(document.createElement('select').options.__proto__) should be HTMLOptionsCollectionPrototype. Was HTMLCollectionPrototype.
+PASS jsWrapperClass(document.createElement('select').options.__proto__) is 'HTMLOptionsCollectionPrototype'
 PASS jsWrapperClass(document.createElement('select').options.constructor) is 'HTMLOptionsCollectionConstructor'
 
 HTML Elements
index a124e2e..892062b 100644 (file)
@@ -41,7 +41,7 @@ PASS getSortedOwnPropertyNames(decodeURIComponent) is ['length', 'name']
 PASS getSortedOwnPropertyNames(encodeURI) is ['length', 'name']
 PASS getSortedOwnPropertyNames(encodeURIComponent) is ['length', 'name']
 PASS getSortedOwnPropertyNames(Object) is ['create', 'defineProperties', 'defineProperty', 'freeze', 'getOwnPropertyDescriptor', 'getOwnPropertyNames', 'getPrototypeOf', 'isExtensible', 'isFrozen', 'isSealed', 'keys', 'length', 'name', 'preventExtensions', 'prototype', 'seal']
-PASS getSortedOwnPropertyNames(Object.prototype) is ['__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf']
+PASS getSortedOwnPropertyNames(Object.prototype) is ['__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', '__proto__', 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf']
 PASS getSortedOwnPropertyNames(Function) is ['length', 'name', 'prototype']
 PASS getSortedOwnPropertyNames(Function.prototype) is ['apply', 'bind', 'call', 'constructor', 'length', 'name', 'toString']
 PASS getSortedOwnPropertyNames(Array) is ['isArray', 'length', 'name', 'prototype']
index d1f9ac9..7953362 100644 (file)
@@ -3,7 +3,10 @@ This test makes sure we don't hang when setting cyclic prototype values: http://
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS o1.__proto__ = o3 threw exception Error: cyclic __proto__ value.
+PASS o1.__proto__ = o3; threw exception Error: cyclic __proto__ value.
+PASS ({}).hasOwnProperty.call(o1, '__proto__') is false
+PASS ({}).hasOwnProperty.call(o1, '__proto__') is true
+PASS Object.getPrototypeOf(o1) is null
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 2dd7626..ac7472f 100644 (file)
@@ -541,14 +541,14 @@ PASS Invalid: "for(var a,b 'this shouldn't be allowed' false ; ) ;"
 PASS Invalid: "function f() { for(var a,b 'this shouldn't be allowed' false ; ) ; }"
 PASS Invalid: "for(var a,b '"
 PASS Invalid: "function f() { for(var a,b ' }"
-PASS Invalid: "function __proto__(){}"
-PASS Invalid: "function f() { function __proto__(){} }"
-PASS Invalid: "(function __proto__(){})"
-PASS Invalid: "function f() { (function __proto__(){}) }"
-PASS Invalid: "'use strict'; function __proto__(){}"
-PASS Invalid: "function f() { 'use strict'; function __proto__(){} }"
-PASS Invalid: "'use strict'; (function __proto__(){})"
-PASS Invalid: "function f() { 'use strict'; (function __proto__(){}) }"
+PASS Valid:   "function __proto__(){}"
+PASS Valid:   "function f() { function __proto__(){} }"
+PASS Valid:   "(function __proto__(){})"
+PASS Valid:   "function f() { (function __proto__(){}) }"
+PASS Valid:   "'use strict'; function __proto__(){}"
+PASS Valid:   "function f() { 'use strict'; function __proto__(){} }"
+PASS Valid:   "'use strict'; (function __proto__(){})"
+PASS Valid:   "function f() { 'use strict'; (function __proto__(){}) }"
 PASS Valid:   "if (0) $foo; "
 PASS Valid:   "function f() { if (0) $foo;  }"
 PASS Valid:   "if (0) _foo; "
index 8295029..035672f 100644 (file)
@@ -11,8 +11,8 @@ PASS test(preventExtensions(obj())) is "(b:4)"
 PASS test(seal(obj())) is "(a:1)(b:4)S"
 PASS test(freeze(obj())) is "(a:1)(b:2)SF"
 PASS Object.preventExtensions(Math.sin) is Math.sin
-PASS var o = {}; Object.preventExtensions(o); o.__proto__ = { newProp: "Should not see this" }; o.newProp; is undefined.
-PASS "use strict"; var o = {}; Object.preventExtensions(o); o.__proto__ = { newProp: "Should not see this" }; threw exception TypeError: Attempted to assign to readonly property..
+PASS var o = {}; Object.preventExtensions(o); o.__proto__ = { newProp: "Should not see this" }; o.newProp; threw exception TypeError: Attempted to assign to readonly property..
+PASS "use strict"; var o = {}; Object.preventExtensions(o); o.__proto__ = { newProp: "Should not see this" }; o.newProp; threw exception TypeError: Attempted to assign to readonly property..
 PASS Object.preventExtensions(Math); Math.sqrt(4) is 2
 PASS var arr = Object.preventExtensions([]); arr[0] = 42; arr[0] is undefined.
 PASS var arr = Object.preventExtensions([]); arr[0] = 42; arr.length is 0
index 781b0be..0f1e440 100644 (file)
@@ -53,10 +53,14 @@ PASS Object.__proto__.isPrototypeOf(Array) is true
 PASS Object.__proto__.isPrototypeOf(Date) is true
 PASS Object.__proto__.isPrototypeOf(Number) is true
 PASS Object.__proto__.isPrototypeOf(String) is true
-PASS var wasSet = false; var o = { }; o.__defineGetter__("__proto__", function() { wasSet = true }); o.__proto__; wasSet; is false
-PASS var wasSet = false; var o = { }; o.__defineSetter__("__proto__", function() { wasSet = true }); o.__proto__ = {}; wasSet; is false
-PASS var wasSet = false; var o = { }; Object.defineProperty(o, "__proto__", { "get": function() { wasSet = true } }); o.__proto__; wasSet; is false
+PASS var wasSet = false; var o = { }; o.__defineGetter__("__proto__", function() { wasSet = true }); o.__proto__; wasSet; is true
+PASS var wasSet = false; var o = { }; o.__defineSetter__("__proto__", function() { wasSet = true }); o.__proto__ = {}; wasSet; is true
+PASS var wasSet = false; var o = { }; Object.defineProperty(o, "__proto__", { "get": function() { wasSet = true } }); o.__proto__; wasSet; is true
 PASS var wasSet = false; var o = { }; Object.defineProperty(o, "__proto__", { "__proto__": function(x) { wasSet = true } }); o.__proto__ = {}; wasSet; is false
+PASS var o = {}; o.__proto__ = { x:true }; o.x is true
+PASS var o = {}; o.__proto__ = { x:true }; o.hasOwnProperty('__proto__') is false
+PASS var o = {}; o.__proto__ = { x:true }; o.x is undefined.
+PASS var o = {}; o.__proto__ = { x:true }; o.hasOwnProperty('__proto__') is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 3ec75cb..daea97c 100644 (file)
@@ -49,7 +49,7 @@ var expectedPropertyNamesSet = {
     "encodeURIComponent": "['length', 'name']",
 // Built-in ECMA objects
     "Object": "['create', 'defineProperties', 'defineProperty', 'freeze', 'getOwnPropertyDescriptor', 'getOwnPropertyNames', 'getPrototypeOf', 'isExtensible', 'isFrozen', 'isSealed', 'keys', 'length', 'name', 'preventExtensions', 'prototype', 'seal']",
-    "Object.prototype": "['__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf']",
+    "Object.prototype": "['__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', '__proto__', 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf']",
     "Function": "['length', 'name', 'prototype']",
     "Function.prototype": "['apply', 'bind', 'call', 'constructor', 'length', 'name', 'toString']",
     "Array": "['isArray', 'length', 'name', 'prototype']",
index a80e69e..041356e 100644 (file)
@@ -6,6 +6,13 @@ o2.__proto__ = o1;
 var o3 = { p3: 3 };
 o3.__proto__ = o2;
 
-o1.__proto__ = null;  // just for sanity's sake
+// Try to create a cyclical prototype chain.
+shouldThrow("o1.__proto__ = o3;");
 
-shouldThrow("o1.__proto__ = o3");
+// This changes behaviour, since __proto__ is an accessor on Object.prototype.
+o1.__proto__ = null;
+
+shouldBeFalse("({}).hasOwnProperty.call(o1, '__proto__')");
+o1.__proto__ = o3;
+shouldBeTrue("({}).hasOwnProperty.call(o1, '__proto__')");
+shouldBe("Object.getPrototypeOf(o1)", "null");
index 5fd6937..e225baa 100644 (file)
@@ -347,10 +347,10 @@ invalid("L: L1: L2: L3: L4: L: ;");
 invalid("for(var a,b 'this shouldn\'t be allowed' false ; ) ;");
 invalid("for(var a,b '");
 
-invalid("function __proto__(){}")
-invalid("(function __proto__(){})")
-invalid("'use strict'; function __proto__(){}")
-invalid("'use strict'; (function __proto__(){})")
+valid("function __proto__(){}")
+valid("(function __proto__(){})")
+valid("'use strict'; function __proto__(){}")
+valid("'use strict'; (function __proto__(){})")
 
 valid("if (0) $foo; ")
 valid("if (0) _foo; ")
index ce48f0e..fccf403 100644 (file)
@@ -69,8 +69,8 @@ shouldBe('test(freeze(obj()))', '"(a:1)(b:2)SF"'); // sealed and frozen, CANNOT
 // check that we can preventExtensions on a host function.
 shouldBe('Object.preventExtensions(Math.sin)', 'Math.sin');
 
-shouldBeUndefined('var o = {}; Object.preventExtensions(o); o.__proto__ = { newProp: "Should not see this" }; o.newProp;');
-shouldThrow('"use strict"; var o = {}; Object.preventExtensions(o); o.__proto__ = { newProp: "Should not see this" };');
+shouldThrow('var o = {}; Object.preventExtensions(o); o.__proto__ = { newProp: "Should not see this" }; o.newProp;');
+shouldThrow('"use strict"; var o = {}; Object.preventExtensions(o); o.__proto__ = { newProp: "Should not see this" }; o.newProp;');
 
 // check that we can still access static properties on an object after calling preventExtensions.
 shouldBe('Object.preventExtensions(Math); Math.sqrt(4)', '2');
index 7bbe1ad..4be3fa6 100644 (file)
@@ -55,7 +55,14 @@ shouldBeTrue("Object.__proto__.isPrototypeOf(Date)");
 shouldBeTrue("Object.__proto__.isPrototypeOf(Number)");
 shouldBeTrue("Object.__proto__.isPrototypeOf(String)");
 
-shouldBeFalse("var wasSet = false; var o = { }; o.__defineGetter__(\"__proto__\", function() { wasSet = true }); o.__proto__; wasSet;");
-shouldBeFalse("var wasSet = false; var o = { }; o.__defineSetter__(\"__proto__\", function() { wasSet = true }); o.__proto__ = {}; wasSet;");
-shouldBeFalse("var wasSet = false; var o = { }; Object.defineProperty(o, \"__proto__\", { \"get\": function() { wasSet = true } }); o.__proto__; wasSet;");
+shouldBeTrue("var wasSet = false; var o = { }; o.__defineGetter__(\"__proto__\", function() { wasSet = true }); o.__proto__; wasSet;");
+shouldBeTrue("var wasSet = false; var o = { }; o.__defineSetter__(\"__proto__\", function() { wasSet = true }); o.__proto__ = {}; wasSet;");
+shouldBeTrue("var wasSet = false; var o = { }; Object.defineProperty(o, \"__proto__\", { \"get\": function() { wasSet = true } }); o.__proto__; wasSet;");
 shouldBeFalse("var wasSet = false; var o = { }; Object.defineProperty(o, \"__proto__\", { \"__proto__\": function(x) { wasSet = true } }); o.__proto__ = {}; wasSet;");
+
+// Deleting Object.prototype.__proto__ removes the ability to set the object's prototype.
+shouldBeTrue("var o = {}; o.__proto__ = { x:true }; o.x");
+shouldBeFalse("var o = {}; o.__proto__ = { x:true }; o.hasOwnProperty('__proto__')");
+delete Object.prototype.__proto__;
+shouldBeUndefined("var o = {}; o.__proto__ = { x:true }; o.x");
+shouldBeTrue("var o = {}; o.__proto__ = { x:true }; o.hasOwnProperty('__proto__')");
index a08300a..758dc2b 100644 (file)
@@ -1,3 +1,57 @@
+2012-02-16  Gavin Barraclough  <barraclough@apple.com>
+
+        Move special __proto__ property to Object.prototype
+        https://bugs.webkit.org/show_bug.cgi?id=78409
+
+        Reviewed by Oliver Hunt.
+
+        Re-implement this as a regular accessor property.  This has three key benefits:
+        1) It makes it possible for objects to be given properties named __proto__.
+        2) Object.prototype.__proto__ can be deleted, preventing object prototypes from being changed.
+        3) This largely removes the magic used the implement __proto__, it can just be made a regular accessor property.
+
+        * parser/Parser.cpp:
+        (JSC::::parseFunctionInfo):
+            - No need to prohibit functions named __proto__.
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::reset):
+            - Add __proto__ accessor to Object.prototype.
+        * runtime/JSGlobalObjectFunctions.cpp:
+        (JSC::globalFuncProtoGetter):
+        (JSC::globalFuncProtoSetter):
+            - Definition of the __proto__ accessor functions.
+        * runtime/JSGlobalObjectFunctions.h:
+            - Declaration of the __proto__ accessor functions.
+        * runtime/JSObject.cpp:
+        (JSC::JSObject::put):
+            - Remove the special handling for __proto__, there is still a check to allow for a fast guard for accessors excluding __proto__.
+        (JSC::JSObject::putDirectAccessor):
+            - Track on the structure whether an object contains accessors other than one for __proto__.
+        (JSC::JSObject::defineOwnProperty):
+            - No need to prohibit definition of own properties named __proto__.
+        * runtime/JSObject.h:
+        (JSC::JSObject::inlineGetOwnPropertySlot):
+            - Remove the special handling for __proto__.
+        (JSC::JSValue::get):
+            - Remove the special handling for __proto__.
+        * runtime/JSString.cpp:
+        (JSC::JSString::getOwnPropertySlot):
+            - Remove the special handling for __proto__.
+        * runtime/JSValue.h:
+        (JSValue):
+            - Made synthesizePrototype public (this may be needed by the __proto__ getter).
+        * runtime/ObjectConstructor.cpp:
+        (JSC::objectConstructorGetPrototypeOf):
+            - Perform the security check & call prototype() directly.
+        * runtime/Structure.cpp:
+        (JSC::Structure::Structure):
+            - Added 'ExcludingProto' variant of the 'hasGetterSetterProperties' state.
+        * runtime/Structure.h:
+        (JSC::Structure::hasGetterSetterPropertiesExcludingProto):
+        (JSC::Structure::setHasGetterSetterProperties):
+        (Structure):
+            - Added 'ExcludingProto' variant of the 'hasGetterSetterProperties' state.
+
 2012-02-20  Michael Saboff  <msaboff@apple.com>
 
         Update toLower and toUpper tests for Unicode 6.1 changes
index fd2beb7..dc2efa5 100644 (file)
@@ -327,7 +327,6 @@ EXPORTS
     ?stopSampling@JSGlobalData@JSC@@QAEXXZ
     ?substringSharingImpl@UString@JSC@@QBE?AV12@II@Z
     ?symbolTableGet@JSVariableObject@JSC@@IAE_NABVIdentifier@2@AAVPropertyDescriptor@2@@Z
-    ?synthesizePrototype@JSValue@JSC@@ABEPAVJSObject@2@PAVExecState@2@@Z
     ?thisObject@DebuggerCallFrame@JSC@@QBEPAVJSObject@2@XZ
     ?throwError@JSC@@YA?AVJSValue@1@PAVExecState@1@V21@@Z
     ?throwError@JSC@@YAPAVJSObject@1@PAVExecState@1@PAV21@@Z
index 74d2e04..bad6424 100644 (file)
@@ -140,10 +140,10 @@ CallFrame* CallFrame::trueCallerFrame()
     //
     // machineCaller -> The caller according to the machine, which may be zero or
     //    more frames above the true caller due to inlining.
-    
+
     // Am I an inline call frame? If so, we're done.
-    if (isInlineCallFrame())
-        return callerFrame();
+    if (isInlineCallFrame() || !hasReturnPC())
+        return callerFrame()->removeHostCallFrameFlag();
     
     // I am a machine call frame, so the question is: is my caller a machine call frame
     // that has inlines or a machine call frame that doesn't?
@@ -154,9 +154,9 @@ CallFrame* CallFrame::trueCallerFrame()
     
     // Figure out how we want to get the current code location.
     if (hasHostCallFrameFlag() || returnAddressIsInCtiTrampoline(returnPC()))
-        return machineCaller->trueCallFrameFromVMCode();
+        return machineCaller->trueCallFrameFromVMCode()->removeHostCallFrameFlag();
     
-    return machineCaller->trueCallFrame(returnPC());
+    return machineCaller->trueCallFrame(returnPC())->removeHostCallFrameFlag();
 }
 #endif
 
index 97934de..9ef41c9 100644 (file)
@@ -103,6 +103,7 @@ namespace JSC  {
 
         CallFrame* callerFrame() const { return this[RegisterFile::CallerFrame].callFrame(); }
 #if ENABLE(JIT)
+        bool hasReturnPC() const { return this[RegisterFile::ReturnPC].vPC(); }
         ReturnAddressPtr returnPC() const { return ReturnAddressPtr(this[RegisterFile::ReturnPC].vPC()); }
 #endif
         AbstractPC abstractReturnPC(JSGlobalData& globalData) { return AbstractPC(globalData, this); }
index 58e9e19..939d269 100644 (file)
@@ -774,7 +774,6 @@ template <FunctionRequirements requirements, bool nameIsInContainingScope, class
     functionScope->setIsFunction();
     if (match(IDENT)) {
         name = m_token.m_data.ident;
-        failIfTrueWithMessage(*name == m_globalData->propertyNames->underscoreProto, "Cannot name a function __proto__");
         next();
         if (!nameIsInContainingScope)
             failIfFalseIfStrict(functionScope->declareVariable(name));
index e648fbe..8d39758 100644 (file)
@@ -78,7 +78,7 @@ namespace JSC {
 
 const ClassInfo JSGlobalObject::s_info = { "GlobalObject", &JSVariableObject::s_info, 0, ExecState::globalObjectTable, CREATE_METHOD_TABLE(JSGlobalObject) };
 
-const GlobalObjectMethodTable JSGlobalObject::s_globalObjectMethodTable = { &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript };
+const GlobalObjectMethodTable JSGlobalObject::s_globalObjectMethodTable = { &allowsAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript };
 
 /* Source for JSGlobalObject.lut.h
 @begin globalObjectTable
@@ -205,6 +205,10 @@ void JSGlobalObject::reset(JSValue prototype)
     m_callFunction.set(exec->globalData(), this, callFunction);
     m_applyFunction.set(exec->globalData(), this, applyFunction);
     m_objectPrototype.set(exec->globalData(), this, ObjectPrototype::create(exec, this, ObjectPrototype::createStructure(exec->globalData(), this, jsNull())));
+    GetterSetter* protoAccessor = GetterSetter::create(exec);
+    protoAccessor->setGetter(exec->globalData(), JSFunction::create(exec, this, 0, Identifier(), globalFuncProtoGetter));
+    protoAccessor->setSetter(exec->globalData(), JSFunction::create(exec, this, 0, Identifier(), globalFuncProtoSetter));
+    m_objectPrototype->putDirectAccessor(exec->globalData(), exec->propertyNames().underscoreProto, protoAccessor, Accessor | DontEnum);
     m_functionPrototype->structure()->setPrototypeWithoutTransition(exec->globalData(), m_objectPrototype.get());
 
     m_emptyObjectStructure.set(exec->globalData(), this, m_objectPrototype->inheritorID(exec->globalData()));
index b67ccb7..781a840 100644 (file)
@@ -56,6 +56,9 @@ namespace JSC {
     typedef Vector<ExecState*, 16> ExecStateStack;
     
     struct GlobalObjectMethodTable {
+        typedef bool (*AllowsAccessFromFunctionPtr)(const JSGlobalObject*, ExecState*);
+        AllowsAccessFromFunctionPtr allowsAccessFrom;
+
         typedef bool (*SupportsProfilingFunctionPtr)(const JSGlobalObject*); 
         SupportsProfilingFunctionPtr supportsProfiling;
 
@@ -279,6 +282,7 @@ namespace JSC {
 
         const GlobalObjectMethodTable* globalObjectMethodTable() const { return m_globalObjectMethodTable; }
 
+        static bool allowsAccessFrom(const JSGlobalObject*, ExecState*) { return true; }
         static bool supportsProfiling(const JSGlobalObject*) { return false; }
         static bool supportsRichSourceInfo(const JSGlobalObject*) { return true; }
 
index 204144c..db8ee1d 100644 (file)
@@ -714,4 +714,40 @@ EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeError(ExecState* exec)
     return throwVMTypeError(exec);
 }
 
+EncodedJSValue JSC_HOST_CALL globalFuncProtoGetter(ExecState* exec)
+{
+    if (!exec->thisValue().isObject())
+        return JSValue::encode(exec->thisValue().synthesizePrototype(exec));
+
+    JSObject* thisObject = asObject(exec->thisValue());
+    if (!thisObject->allowsAccessFrom(exec->trueCallerFrame()))
+        return JSValue::encode(jsUndefined());
+
+    return JSValue::encode(thisObject->prototype());
+}
+
+EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(ExecState* exec)
+{
+    JSValue value = exec->argument(0);
+
+    // Setting __proto__ of a primitive should have no effect.
+    if (!exec->thisValue().isObject())
+        return JSValue::encode(jsUndefined());
+
+    JSObject* thisObject = asObject(exec->thisValue());
+    if (!thisObject->allowsAccessFrom(exec->trueCallerFrame()))
+        return JSValue::encode(jsUndefined());
+
+    // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
+    if (!value.isObject() && !value.isNull())
+        return JSValue::encode(jsUndefined());
+
+    if (!thisObject->isExtensible())
+        return throwVMError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError));
+
+    if (!thisObject->setPrototypeWithCycleCheck(exec->globalData(), value))
+        throwError(exec, createError(exec, "cyclic __proto__ value"));
+    return JSValue::encode(jsUndefined());
+}
+
 } // namespace JSC
index 1183dfa..8833bf6 100644 (file)
@@ -48,6 +48,8 @@ namespace JSC {
     EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState*);
     EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState*);
     EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeError(ExecState*);
+    EncodedJSValue JSC_HOST_CALL globalFuncProtoGetter(ExecState*);
+    EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(ExecState*);
 
     static const double mantissaOverflowLowerBound = 9007199254740992.0;
     double parseIntOverflow(const LChar*, int length, int radix);
index 25a62ca..7fbe5db 100644 (file)
@@ -132,33 +132,19 @@ void JSObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName
     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject));
     JSGlobalData& globalData = exec->globalData();
 
-    if (propertyName == exec->propertyNames().underscoreProto) {
-        // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
-        if (!value.isObject() && !value.isNull())
-            return;
-
-        if (!thisObject->isExtensible()) {
-            if (slot.isStrictMode())
-                throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
-            return;
-        }
-            
-        if (!thisObject->setPrototypeWithCycleCheck(globalData, value))
-            throwError(exec, createError(exec, "cyclic __proto__ value"));
-        return;
-    }
-
     // Check if there are any setters or getters in the prototype chain
     JSValue prototype;
-    for (JSObject* obj = thisObject; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
-        prototype = obj->prototype();
-        if (prototype.isNull()) {
-            if (!thisObject->putDirectInternal<PutModePut>(globalData, propertyName, value, 0, slot, getJSFunction(value)) && slot.isStrictMode())
-                throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
-            return;
+    if (propertyName != exec->propertyNames().underscoreProto) {
+        for (JSObject* obj = thisObject; !obj->structure()->hasGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) {
+            prototype = obj->prototype();
+            if (prototype.isNull()) {
+                if (!thisObject->putDirectInternal<PutModePut>(globalData, propertyName, value, 0, slot, getJSFunction(value)) && slot.isStrictMode())
+                    throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
+                return;
+            }
         }
     }
-    
+
     unsigned attributes;
     JSCell* specificValue;
     if ((thisObject->structure()->get(globalData, propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly) {
@@ -216,10 +202,31 @@ void JSObject::putDirectVirtual(JSObject* object, ExecState* exec, const Identif
     object->putDirectInternal<PutModeDefineOwnProperty>(exec->globalData(), propertyName, value, attributes, slot, getJSFunction(value));
 }
 
+bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prototype)
+{
+    JSValue checkFor = this;
+    if (this->isGlobalObject())
+        checkFor = static_cast<JSGlobalObject*>(this)->globalExec()->thisValue();
+
+    JSValue nextPrototype = prototype;
+    while (nextPrototype && nextPrototype.isObject()) {
+        if (nextPrototype == checkFor)
+            return false;
+        nextPrototype = asObject(nextPrototype)->prototype();
+    }
+    setPrototype(globalData, prototype);
+    return true;
+}
+
+bool JSObject::allowsAccessFrom(ExecState* exec)
+{
+    JSGlobalObject* globalObject = isGlobalThis() ? static_cast<JSGlobalThis*>(this)->unwrappedObject() : this->globalObject();
+    return globalObject->globalObjectMethodTable()->allowsAccessFrom(globalObject, exec);
+}
+
 void JSObject::putDirectAccessor(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
 {
     ASSERT(value.isGetterSetter() && (attributes & Accessor));
-    ASSERT(propertyName != globalData.propertyNames->underscoreProto);
 
     PutPropertySlot slot;
     putDirectInternal<PutModeDefineOwnProperty>(globalData, propertyName, value, attributes, slot, getJSFunction(value));
@@ -230,7 +237,7 @@ void JSObject::putDirectAccessor(JSGlobalData& globalData, const Identifier& pro
     if (slot.type() != PutPropertySlot::NewProperty)
         setStructure(globalData, Structure::attributeChangeTransition(globalData, structure(), propertyName, attributes));
 
-    structure()->setHasGetterSetterProperties(true);
+    structure()->setHasGetterSetterProperties(propertyName == globalData.propertyNames->underscoreProto);
 }
 
 bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
@@ -632,11 +639,6 @@ static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& p
 
 bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
 {
-    // __proto__ is magic; we don't currently support setting it as a regular property.
-    // Silent filter out calls to set __proto__ at an early stage; pretend all is okay.
-    if (propertyName == exec->propertyNames().underscoreProto)
-        return true;
-
     // If we have a new property we can just put it on normally
     PropertyDescriptor current;
     if (!object->methodTable()->getOwnPropertyDescriptor(object, exec, propertyName, current)) {
index 9b5516c..65130db 100644 (file)
@@ -105,6 +105,8 @@ namespace JSC {
         JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
         JS_EXPORT_PRIVATE static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&);
 
+        bool allowsAccessFrom(ExecState*);
+
         JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&);
         JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue);
 
@@ -486,19 +488,6 @@ inline JSValue JSObject::prototype() const
     return structure()->storedPrototype();
 }
 
-inline bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prototype)
-{
-    JSValue nextPrototypeValue = prototype;
-    while (nextPrototypeValue && nextPrototypeValue.isObject()) {
-        JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
-        if (nextPrototype == this)
-            return false;
-        nextPrototypeValue = nextPrototype->prototype();
-    }
-    setPrototype(globalData, prototype);
-    return true;
-}
-
 inline void JSObject::setPrototype(JSGlobalData& globalData, JSValue prototype)
 {
     ASSERT(prototype);
@@ -550,12 +539,6 @@ ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Ide
         return true;
     }
 
-    // non-standard Netscape extension
-    if (propertyName == exec->propertyNames().underscoreProto) {
-        slot.setValue(prototype());
-        return true;
-    }
-
     return false;
 }
 
@@ -803,8 +786,6 @@ inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, Pro
 {
     if (UNLIKELY(!isCell())) {
         JSObject* prototype = synthesizePrototype(exec);
-        if (propertyName == exec->propertyNames().underscoreProto)
-            return prototype;
         if (!prototype->getPropertySlot(exec, propertyName, slot))
             return jsUndefined();
         return slot.getValue(exec, propertyName);
index 4e98f9d..3fa2901 100644 (file)
@@ -253,10 +253,6 @@ bool JSString::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifie
     // This function should only be called by JSValue::get.
     if (thisObject->getStringPropertySlot(exec, propertyName, slot))
         return true;
-    if (propertyName == exec->propertyNames().underscoreProto) {
-        slot.setValue(exec->lexicalGlobalObject()->stringPrototype());
-        return true;
-    }
     slot.setBase(thisObject);
     JSObject* object;
     for (JSValue prototype = exec->lexicalGlobalObject()->stringPrototype(); !prototype.isNull(); prototype = object->prototype()) {
index b18c181..80acbb1 100644 (file)
@@ -234,6 +234,8 @@ namespace JSC {
 
         char* description();
 
+        JS_EXPORT_PRIVATE JSObject* synthesizePrototype(ExecState*) const;
+
     private:
         template <class T> JSValue(WriteBarrierBase<T>);
 
@@ -246,7 +248,6 @@ namespace JSC {
         JS_EXPORT_PRIVATE JSObject* toObjectSlowCase(ExecState*, JSGlobalObject*) const;
         JS_EXPORT_PRIVATE JSObject* toThisObjectSlowCase(ExecState*) const;
 
-        JS_EXPORT_PRIVATE JSObject* synthesizePrototype(ExecState*) const;
         JSObject* synthesizeObject(ExecState*) const;
 
 #if USE(JSVALUE32_64)
index bb0303e..a7fff0f 100644 (file)
@@ -138,11 +138,10 @@ EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec)
 {
     if (!exec->argument(0).isObject())
         return throwVMError(exec, createTypeError(exec, "Requested prototype of a value that is not an object."));
-        
-    // This uses JSValue::get() instead of directly accessing the prototype from the object
-    // (using JSObject::prototype()) in order to allow objects to override the behavior, such
-    // as returning jsUndefined() for cross-origin access.
-    return JSValue::encode(exec->argument(0).get(exec, exec->propertyNames().underscoreProto));
+    JSObject* object = asObject(exec->argument(0));
+    if (!object->allowsAccessFrom(exec->trueCallerFrame()))
+        return JSValue::encode(jsUndefined());
+    return JSValue::encode(object->prototype());
 }
 
 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec)
index a9688f0..2366afd 100644 (file)
@@ -167,6 +167,7 @@ Structure::Structure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSV
     , m_dictionaryKind(NoneDictionaryKind)
     , m_isPinnedPropertyTable(false)
     , m_hasGetterSetterProperties(false)
+    , m_hasGetterSetterPropertiesExcludingProto(false)
     , m_hasNonEnumerableProperties(false)
     , m_attributesInPrevious(0)
     , m_specificFunctionThrashCount(0)
@@ -188,6 +189,7 @@ Structure::Structure(JSGlobalData& globalData)
     , m_dictionaryKind(NoneDictionaryKind)
     , m_isPinnedPropertyTable(false)
     , m_hasGetterSetterProperties(false)
+    , m_hasGetterSetterPropertiesExcludingProto(false)
     , m_hasNonEnumerableProperties(false)
     , m_attributesInPrevious(0)
     , m_specificFunctionThrashCount(0)
@@ -207,6 +209,7 @@ Structure::Structure(JSGlobalData& globalData, const Structure* previous)
     , m_dictionaryKind(previous->m_dictionaryKind)
     , m_isPinnedPropertyTable(false)
     , m_hasGetterSetterProperties(previous->m_hasGetterSetterProperties)
+    , m_hasGetterSetterPropertiesExcludingProto(previous->m_hasGetterSetterPropertiesExcludingProto)
     , m_hasNonEnumerableProperties(previous->m_hasNonEnumerableProperties)
     , m_attributesInPrevious(0)
     , m_specificFunctionThrashCount(previous->m_specificFunctionThrashCount)
index ced2968..f4207f5 100644 (file)
@@ -145,7 +145,13 @@ namespace JSC {
         }
 
         bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; }
-        void setHasGetterSetterProperties(bool hasGetterSetterProperties) { m_hasGetterSetterProperties = hasGetterSetterProperties; }
+        bool hasGetterSetterPropertiesExcludingProto() const { return m_hasGetterSetterPropertiesExcludingProto; }
+        void setHasGetterSetterProperties(bool is__proto__)
+        {
+            m_hasGetterSetterProperties = true;
+            if (!is__proto__)
+                m_hasGetterSetterPropertiesExcludingProto = true;
+        }
 
         bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; }
         
@@ -282,6 +288,7 @@ namespace JSC {
         unsigned m_dictionaryKind : 2;
         bool m_isPinnedPropertyTable : 1;
         bool m_hasGetterSetterProperties : 1;
+        bool m_hasGetterSetterPropertiesExcludingProto : 1;
         bool m_hasNonEnumerableProperties : 1;
         unsigned m_attributesInPrevious : 7;
         unsigned m_specificFunctionThrashCount : 2;
index 10f2809..54326b3 100644 (file)
@@ -1,3 +1,23 @@
+2012-02-16  Gavin Barraclough  <barraclough@apple.com>
+
+        Move special __proto__ property to Object.prototype
+        https://bugs.webkit.org/show_bug.cgi?id=78409
+
+        Reviewed by Oliver Hunt.
+
+        Re-implement this as a regular accessor property.  This has three key benefits:
+        1) It makes it possible for objects to be given properties named __proto__.
+        2) Object.prototype.__proto__ can be deleted, preventing object prototypes from being changed.
+        3) This largely removes the magic used the implement __proto__, it can just be made a regular accessor property.
+
+        * bindings/js/JSDOMWindowBase.cpp:
+        (WebCore::JSDOMWindowBase::allowsAccessFrom):
+        (WebCore):
+            - expose allowsAccessFrom check to JSC.
+        * bindings/js/JSDOMWindowBase.h:
+        (JSDOMWindowBase):
+            - expose allowsAccessFrom check to JSC.
+
 2012-02-20  Benjamin Poulain  <benjamin@webkit.org>
 
         Make JSCSSStyleDeclaration work directly with CSS Property ID
index 8de432e..2702fc5 100644 (file)
@@ -43,7 +43,7 @@ namespace WebCore {
 
 const ClassInfo JSDOMWindowBase::s_info = { "Window", &JSDOMGlobalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSDOMWindowBase) };
 
-const GlobalObjectMethodTable JSDOMWindowBase::s_globalObjectMethodTable = { &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript };
+const GlobalObjectMethodTable JSDOMWindowBase::s_globalObjectMethodTable = { &allowsAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript };
 
 JSDOMWindowBase::JSDOMWindowBase(JSGlobalData& globalData, Structure* structure, PassRefPtr<DOMWindow> window, JSDOMWindowShell* shell)
     : JSDOMGlobalObject(globalData, structure, shell->world(), &s_globalObjectMethodTable)
@@ -92,6 +92,34 @@ void JSDOMWindowBase::printErrorMessage(const String& message) const
     printErrorMessageForFrame(impl()->frame(), message);
 }
 
+// This method checks whether accesss to *this* global object is permitted from
+// the given context; this differs from allowsAccessFromPrivate, since that
+// method checks whether the given context is permitted to access the current
+// window the shell is referencing (which may come from a different security
+// origin to this global object).
+bool JSDOMWindowBase::allowsAccessFrom(const JSGlobalObject* thisObject, ExecState* exec)
+{
+    JSGlobalObject* otherObject = exec->lexicalGlobalObject();
+
+    const JSDOMWindow* originWindow = asJSDOMWindow(otherObject);
+    const JSDOMWindow* targetWindow = asJSDOMWindow(thisObject);
+
+    if (originWindow == targetWindow)
+        return true;
+
+    const SecurityOrigin* originSecurityOrigin = originWindow->impl()->securityOrigin();
+    const SecurityOrigin* targetSecurityOrigin = targetWindow->impl()->securityOrigin();
+
+    if (originSecurityOrigin->canAccess(targetSecurityOrigin))
+        return true;
+
+    targetWindow->printErrorMessage(targetWindow->crossDomainAccessErrorMessage(otherObject));
+    return false;
+
+
+    return static_cast<const JSDOMWindowBase*>(thisObject)->allowsAccessFrom(exec);
+}
+
 bool JSDOMWindowBase::supportsProfiling(const JSGlobalObject* object)
 {
 #if !ENABLE(JAVASCRIPT_DEBUGGER) || !ENABLE(INSPECTOR)
index 84c1862..51e0f89 100644 (file)
@@ -63,7 +63,8 @@ namespace WebCore {
         static bool supportsProfiling(const JSC::JSGlobalObject*);
         static bool supportsRichSourceInfo(const JSC::JSGlobalObject*);
         static bool shouldInterruptScript(const JSC::JSGlobalObject*);
-
+        static bool allowsAccessFrom(const JSC::JSGlobalObject*, JSC::ExecState*);
+        
         bool allowsAccessFrom(JSC::ExecState*) const;
         bool allowsAccessFromNoErrorMessage(JSC::ExecState*) const;
         bool allowsAccessFrom(JSC::ExecState*, String& message) const;