Author: erik.co...@gmail.com Date: Wed May 6 05:54:57 2009 New Revision: 1882
Added: branches/bleeding_edge/test/mjsunit/undeletable-functions.js Modified: branches/bleeding_edge/src/array.js branches/bleeding_edge/src/bootstrapper.cc branches/bleeding_edge/src/date-delay.js branches/bleeding_edge/src/math.js branches/bleeding_edge/src/runtime.cc branches/bleeding_edge/src/runtime.h branches/bleeding_edge/src/string.js Log: Fix Chromium bug 1717 by emulating JSCs somewhat strange hidden prototypes on some built in types. Review URL: http://codereview.chromium.org/109004 Modified: branches/bleeding_edge/src/array.js ============================================================================== --- branches/bleeding_edge/src/array.js (original) +++ branches/bleeding_edge/src/array.js Wed May 6 05:54:57 2009 @@ -1005,7 +1005,6 @@ // ------------------------------------------------------------------- - function SetupArray() { // Setup non-enumerable constructor property on the Array.prototype // object. @@ -1013,7 +1012,7 @@ // Setup non-enumerable functions of the Array.prototype object and // set their names. - InstallFunctions($Array.prototype, DONT_ENUM, $Array( + InstallFunctionsOnHiddenPrototype($Array.prototype, DONT_ENUM, $Array( "toString", ArrayToString, "toLocaleString", ArrayToLocaleString, "join", ArrayJoin, @@ -1034,8 +1033,7 @@ "indexOf", ArrayIndexOf, "lastIndexOf", ArrayLastIndexOf, "reduce", ArrayReduce, - "reduceRight", ArrayReduceRight - )); + "reduceRight", ArrayReduceRight)); // Manipulate the length of some of the functions to meet // expectations set by ECMA-262 or Mozilla. Modified: branches/bleeding_edge/src/bootstrapper.cc ============================================================================== --- branches/bleeding_edge/src/bootstrapper.cc (original) +++ branches/bleeding_edge/src/bootstrapper.cc Wed May 6 05:54:57 2009 @@ -1481,11 +1481,15 @@ Handle<JSFunction> function = Handle<JSFunction>( JSFunction::cast(global->GetProperty(Heap::Array_symbol()))); - Handle<JSObject> prototype = + Handle<JSObject> hidden_prototype = Handle<JSObject>(JSObject::cast(function->prototype())); - AddSpecialFunction(prototype, "pop", + ASSERT(hidden_prototype->map()->is_hidden_prototype()); + // Remember to skip the hidden prototype: + Handle<JSObject> real_prototype = + Handle<JSObject>(JSObject::cast(hidden_prototype->GetPrototype())); + AddSpecialFunction(real_prototype, "pop", Handle<Code>(Builtins::builtin(Builtins::ArrayPop))); - AddSpecialFunction(prototype, "push", + AddSpecialFunction(real_prototype, "push", Handle<Code>(Builtins::builtin(Builtins::ArrayPush))); } Modified: branches/bleeding_edge/src/date-delay.js ============================================================================== --- branches/bleeding_edge/src/date-delay.js (original) +++ branches/bleeding_edge/src/date-delay.js Wed May 6 05:54:57 2009 @@ -1019,7 +1019,7 @@ // Setup non-enumerable functions of the Date prototype object and // set their names. - InstallFunctions($Date.prototype, DONT_ENUM, $Array( + InstallFunctionsOnHiddenPrototype($Date.prototype, DONT_ENUM, $Array( "toString", DateToString, "toDateString", DateToDateString, "toTimeString", DateToTimeString, Modified: branches/bleeding_edge/src/math.js ============================================================================== --- branches/bleeding_edge/src/math.js (original) +++ branches/bleeding_edge/src/math.js Wed May 6 05:54:57 2009 @@ -164,7 +164,7 @@ // Setup non-enumerable functions of the Math object and // set their names. - InstallFunctions($Math, DONT_ENUM, $Array( + InstallFunctionsOnHiddenPrototype($Math, DONT_ENUM, $Array( "random", MathRandom, "abs", MathAbs, "acos", MathAcos, Modified: branches/bleeding_edge/src/runtime.cc ============================================================================== --- branches/bleeding_edge/src/runtime.cc (original) +++ branches/bleeding_edge/src/runtime.cc Wed May 6 05:54:57 2009 @@ -474,6 +474,42 @@ } +// Inserts an object as the hidden prototype of another object. +static Object* Runtime_SetHiddenPrototype(Arguments args) { + NoHandleAllocation ha; + ASSERT(args.length() == 2); + CONVERT_CHECKED(JSObject, jsobject, args[0]); + CONVERT_CHECKED(JSObject, proto, args[1]); + + // Sanity checks. The old prototype (that we are replacing) could + // theoretically be null, but if it is not null then check that we + // didn't already install a hidden prototype here. + RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() || + !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype()); + RUNTIME_ASSERT(!proto->map()->is_hidden_prototype()); + + // Allocate up front before we start altering state in case we get a GC. + Object* map_or_failure = proto->map()->CopyDropTransitions(); + if (map_or_failure->IsFailure()) return map_or_failure; + Map* new_proto_map = Map::cast(map_or_failure); + + map_or_failure = jsobject->map()->CopyDropTransitions(); + if (map_or_failure->IsFailure()) return map_or_failure; + Map* new_map = Map::cast(map_or_failure); + + // Set proto's prototype to be the old prototype of the object. + new_proto_map->set_prototype(jsobject->GetPrototype()); + proto->set_map(new_proto_map); + new_proto_map->set_is_hidden_prototype(); + + // Set the object's prototype to proto. + new_map->set_prototype(proto); + jsobject->set_map(new_map); + + return Heap::undefined_value(); +} + + static Object* Runtime_IsConstructCall(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 0); @@ -2796,25 +2832,38 @@ } -static Object* Runtime_HasLocalProperty(Arguments args) { - NoHandleAllocation ha; - ASSERT(args.length() == 2); - CONVERT_CHECKED(String, key, args[1]); - +static Object* HasLocalPropertyImplementation(Object* obj, String* key) { // Only JS objects can have properties. - if (args[0]->IsJSObject()) { - JSObject* object = JSObject::cast(args[0]); + if (obj->IsJSObject()) { + JSObject* object = JSObject::cast(obj); if (object->HasLocalProperty(key)) return Heap::true_value(); - } else if (args[0]->IsString()) { + // Handle hidden prototypes. If there's a hidden prototype above this thing + // then we have to check it for properties, because they are supposed to + // look like they are on this object. + Object* proto = object->GetPrototype(); + if (proto->IsJSObject() && + JSObject::cast(proto)->map()->is_hidden_prototype()) { + return HasLocalPropertyImplementation(object->GetPrototype(), key); + } + } else if (obj->IsString()) { // Well, there is one exception: Handle [] on strings. uint32_t index; if (key->AsArrayIndex(&index)) { - String* string = String::cast(args[0]); + String* string = String::cast(obj); if (index < static_cast<uint32_t>(string->length())) return Heap::true_value(); } } return Heap::false_value(); +} + + +static Object* Runtime_HasLocalProperty(Arguments args) { + NoHandleAllocation ha; + ASSERT(args.length() == 2); + CONVERT_CHECKED(String, key, args[1]); + + return HasLocalPropertyImplementation(args[0], key); } Modified: branches/bleeding_edge/src/runtime.h ============================================================================== --- branches/bleeding_edge/src/runtime.h (original) +++ branches/bleeding_edge/src/runtime.h Wed May 6 05:54:57 2009 @@ -53,6 +53,7 @@ F(ToSlowProperties, 1) \ \ F(IsInPrototypeChain, 2) \ + F(SetHiddenPrototype, 2) \ \ F(IsConstructCall, 0) \ \ Modified: branches/bleeding_edge/src/string.js ============================================================================== --- branches/bleeding_edge/src/string.js (original) +++ branches/bleeding_edge/src/string.js Wed May 6 05:54:57 2009 @@ -831,7 +831,7 @@ // Setup the non-enumerable functions on the String prototype object. - InstallFunctions($String.prototype, DONT_ENUM, $Array( + InstallFunctionsOnHiddenPrototype($String.prototype, DONT_ENUM, $Array( "valueOf", StringValueOf, "toString", StringToString, "charAt", StringCharAt, Added: branches/bleeding_edge/test/mjsunit/undeletable-functions.js ============================================================================== --- (empty file) +++ branches/bleeding_edge/test/mjsunit/undeletable-functions.js Wed May 6 05:54:57 2009 @@ -0,0 +1,181 @@ +// Copyright 2009 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. + +// Test that we match JSC in making some functions undeletable. +// See http://code.google.com/p/chromium/issues/detail?id=1717 +// The functions on these prototypes are not just undeletable. It is +// possible to override them with new definitions, then get the old +// version back by deleting the new definition. + +var array; + +array = [ + "toString", "toLocaleString", "join", "pop", "push", "concat", "reverse", + "shift", "unshift", "slice", "splice", "sort", "filter", "forEach", "some", + "every", "map", "indexOf", "lastIndexOf", "reduce", "reduceRight"]; +CheckJSCSemantics(Array.prototype, array, "Array prototype"); + +array = [ + "toString", "toDateString", "toTimeString", "toLocaleString", + "toLocaleDateString", "toLocaleTimeString", "valueOf", "getTime", + "getFullYear", "getUTCFullYear", "getMonth", "getUTCMonth", "getDate", + "getUTCDate", "getDay", "getUTCDay", "getHours", "getUTCHours", "getMinutes", + "getUTCMinutes", "getSeconds", "getUTCSeconds", "getMilliseconds", + "getUTCMilliseconds", "getTimezoneOffset", "setTime", "setMilliseconds", + "setUTCMilliseconds", "setSeconds", "setUTCSeconds", "setMinutes", + "setUTCMinutes", "setHours", "setUTCHours", "setDate", "setUTCDate", + "setMonth", "setUTCMonth", "setFullYear", "setUTCFullYear", "toGMTString", + "toUTCString", "getYear", "setYear", "toISOString", "toJSON"]; +CheckJSCSemantics(Date.prototype, array, "Date prototype"); + +array = [ + "random", "abs", "acos", "asin", "atan", "ceil", "cos", "exp", "floor", "log", + "round", "sin", "sqrt", "tan", "atan2", "pow", "max", "min"]; +CheckJSCSemantics(Math, array, "Math1"); + +CheckEcmaSemantics(Date, ["UTC", "parse", "now"], "Date"); + +array = [ + "E", "LN10", "LN2", "LOG2E", "LOG10E", "PI", "SQRT1_2", "SQRT2"]; +CheckDontDelete(Math, array, "Math2"); + +array = [ + "escape", "unescape", "decodeURI", "decodeURIComponent", "encodeURI", + "encodeURIComponent", "isNaN", "isFinite", "parseInt", "parseFloat", "eval", + "execScript"]; +CheckEcmaSemantics(this, array, "Global"); +CheckReadOnlyAttr(this, "Infinity"); + +array = ["exec", "test", "toString", "compile"]; +CheckEcmaSemantics(RegExp.prototype, array, "RegExp prototype"); + +array = [ + "toString", "toLocaleString", "valueOf", "hasOwnProperty", + "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", + "__lookupGetter__", "__defineSetter__", "__lookupSetter__"]; +CheckEcmaSemantics(Object.prototype, array, "Object prototype"); + +array = [ + "toString", "valueOf", "toJSON"]; +CheckEcmaSemantics(Boolean.prototype, array, "Boolean prototype"); + +array = [ + "toString", "toLocaleString", "valueOf", "toFixed", "toExponential", + "toPrecision", "toJSON"]; +CheckEcmaSemantics(Number.prototype, array, "Number prototype"); + +CheckEcmaSemantics(Function.prototype, ["toString"], "Function prototype"); +CheckEcmaSemantics(Date.prototype, ["constructor"], "Date prototype constructor"); + +array = [ + "charAt", "charCodeAt", "concat", "indexOf", + "lastIndexOf", "localeCompare", "match", "replace", "search", "slice", + "split", "substring", "substr", "toLowerCase", "toLocaleLowerCase", + "toUpperCase", "toLocaleUpperCase", "link", "anchor", "fontcolor", "fontsize", + "big", "blink", "bold", "fixed", "italics", "small", "strike", "sub", "sup", + "toJSON", "toString", "valueOf"]; +CheckJSCSemantics(String.prototype, array, "String prototype"); +CheckEcmaSemantics(String, ["fromCharCode"], "String"); + + +function CheckEcmaSemantics(type, props, name) { + print(name); + for (var i = 0; i < props.length; i++) { + CheckDeletable(type, props[i]); + } +} + + +function CheckJSCSemantics(type, props, name) { + print(name); + for (var i = 0; i < props.length; i++) { + CheckNotDeletable(type, props[i]); + } +} + + +function CheckDontDelete(type, props, name) { + print(name); + for (var i = 0; i < props.length; i++) { + CheckDontDeleteAttr(type, props[i]); + } +} + + +function CheckDeletable(type, prop) { + var old = type[prop]; + var hasOwnProperty = Object.prototype.hasOwnProperty; + if (!type[prop]) return; + assertTrue(type.hasOwnProperty(prop), "inherited: " + prop); + var deleted = delete type[prop]; + assertTrue(deleted, "delete operator returned false: " + prop); + assertFalse(hasOwnProperty.call(type, prop), "still there after delete: " + prop); + type[prop] = "foo"; + assertEquals("foo", type[prop], "not overwritable: " + prop); + type[prop] = old; +} + + +function CheckNotDeletable(type, prop) { + var old = type[prop]; + if (!type[prop]) return; + assertTrue(type.hasOwnProperty(prop), "inherited: " + prop); + var deleted = delete type[prop]; + assertTrue(deleted, "delete operator returned false: " + prop); + assertTrue(type.hasOwnProperty(prop), "not there after delete: " + prop); + type[prop] = "foo"; + assertEquals("foo", type[prop], "not overwritable: " + prop); + deleted = delete type[prop]; + assertTrue(deleted, "delete operator returned false 2nd time: " + prop); + assertEquals(old.toString(), type[prop].toString(), "delete didn't restore the old value: " + prop); +} + + +function CheckDontDeleteAttr(type, prop) { + var old = type[prop]; + if (!type[prop]) return; + assertTrue(type.hasOwnProperty(prop), "inherited: " + prop); + var deleted = delete type[prop]; + assertFalse(deleted, "delete operator returned true: " + prop); + assertTrue(type.hasOwnProperty(prop), "not there after delete: " + prop); + type[prop] = "foo"; + assertFalse("foo" == type[prop], "overwritable: " + prop); +} + + +function CheckReadOnlyAttr(type, prop) { + var old = type[prop]; + if (!type[prop]) return; + assertTrue(type.hasOwnProperty(prop), "inherited: " + prop); + var deleted = delete type[prop]; + assertFalse(deleted, "delete operator returned true: " + prop); + assertTrue(type.hasOwnProperty(prop), "not there after delete: " + prop); + type[prop] = "foo"; + assertEquals("foo", type[prop], "overwritable: " + prop); +} + +print("OK"); --~--~---------~--~----~------------~-------~--~----~ v8-dev mailing list v8-dev@googlegroups.com http://groups.google.com/group/v8-dev -~----------~----~----~----~------~----~------~--~---