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
-~----------~----~----~----~------~----~------~--~---

Reply via email to