ES6: Implement Object.setPrototypeOf
http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.setprototypeof This just exposes the internal %SetPrototype and adds all the required type checks as specified. BUG=v8:2675 LOG=Y R=dslomov@chromium.org, rossberg@chromium.org Review URL: https://codereview.chromium.org/141913002 Patch from Erik Arvidsson <arv@chromium.org>. git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@18685 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
f93f8ded96
commit
1e3a14da44
@ -139,6 +139,10 @@ macro IS_SPEC_OBJECT(arg) = (%_IsSpecObject(arg));
|
||||
# we cannot handle those anyway.
|
||||
macro IS_SPEC_FUNCTION(arg) = (%_ClassOf(arg) === 'Function');
|
||||
|
||||
# Macro for ES6 CheckObjectCoercible
|
||||
# Will throw a TypeError of the form "[functionName] called on null or undefined".
|
||||
macro CHECK_OBJECT_COERCIBLE(arg, functionName) = if (IS_NULL_OR_UNDEFINED(arg) && !IS_UNDETECTABLE(arg)) throw MakeTypeError('called_on_null_or_undefined', [functionName]);
|
||||
|
||||
# Indices in bound function info retrieved by %BoundFunctionGetBindings(...).
|
||||
const kBoundFunctionIndex = 0;
|
||||
const kBoundThisIndex = 1;
|
||||
|
@ -78,7 +78,7 @@ var kMessages = {
|
||||
getter_must_be_callable: ["Getter must be a function: ", "%0"],
|
||||
setter_must_be_callable: ["Setter must be a function: ", "%0"],
|
||||
value_and_accessor: ["Invalid property. A property cannot both have accessors and be writable or have a value, ", "%0"],
|
||||
proto_object_or_null: ["Object prototype may only be an Object or null"],
|
||||
proto_object_or_null: ["Object prototype may only be an Object or null: ", "%0"],
|
||||
property_desc_object: ["Property description must be an object: ", "%0"],
|
||||
redefine_disallowed: ["Cannot redefine property: ", "%0"],
|
||||
define_disallowed: ["Cannot define property:", "%0", ", object is not extensible."],
|
||||
|
120
src/string.js
120
src/string.js
@ -61,10 +61,8 @@ function StringValueOf() {
|
||||
|
||||
// ECMA-262, section 15.5.4.4
|
||||
function StringCharAt(pos) {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.charAt"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.charAt");
|
||||
|
||||
var result = %_StringCharAt(this, pos);
|
||||
if (%_IsSmi(result)) {
|
||||
result = %_StringCharAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
|
||||
@ -75,10 +73,8 @@ function StringCharAt(pos) {
|
||||
|
||||
// ECMA-262 section 15.5.4.5
|
||||
function StringCharCodeAt(pos) {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.charCodeAt"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.charCodeAt");
|
||||
|
||||
var result = %_StringCharCodeAt(this, pos);
|
||||
if (!%_IsSmi(result)) {
|
||||
result = %_StringCharCodeAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
|
||||
@ -89,10 +85,8 @@ function StringCharCodeAt(pos) {
|
||||
|
||||
// ECMA-262, section 15.5.4.6
|
||||
function StringConcat() {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.concat"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.concat");
|
||||
|
||||
var len = %_ArgumentsLength();
|
||||
var this_as_string = TO_STRING_INLINE(this);
|
||||
if (len === 1) {
|
||||
@ -113,10 +107,8 @@ function StringConcat() {
|
||||
|
||||
// ECMA-262 section 15.5.4.7
|
||||
function StringIndexOf(pattern /* position */) { // length == 1
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.indexOf"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.indexOf");
|
||||
|
||||
var subject = TO_STRING_INLINE(this);
|
||||
pattern = TO_STRING_INLINE(pattern);
|
||||
var index = 0;
|
||||
@ -132,10 +124,8 @@ function StringIndexOf(pattern /* position */) { // length == 1
|
||||
|
||||
// ECMA-262 section 15.5.4.8
|
||||
function StringLastIndexOf(pat /* position */) { // length == 1
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.lastIndexOf"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.lastIndexOf");
|
||||
|
||||
var sub = TO_STRING_INLINE(this);
|
||||
var subLength = sub.length;
|
||||
var pat = TO_STRING_INLINE(pat);
|
||||
@ -165,10 +155,8 @@ function StringLastIndexOf(pat /* position */) { // length == 1
|
||||
// This function is implementation specific. For now, we do not
|
||||
// do anything locale specific.
|
||||
function StringLocaleCompare(other) {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.localeCompare"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.localeCompare");
|
||||
|
||||
return %StringLocaleCompare(TO_STRING_INLINE(this),
|
||||
TO_STRING_INLINE(other));
|
||||
}
|
||||
@ -176,10 +164,8 @@ function StringLocaleCompare(other) {
|
||||
|
||||
// ECMA-262 section 15.5.4.10
|
||||
function StringMatch(regexp) {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.match"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.match");
|
||||
|
||||
var subject = TO_STRING_INLINE(this);
|
||||
if (IS_REGEXP(regexp)) {
|
||||
// Emulate RegExp.prototype.exec's side effect in step 5, even though
|
||||
@ -210,10 +196,8 @@ var reusableMatchInfo = [2, "", "", -1, -1];
|
||||
|
||||
// ECMA-262, section 15.5.4.11
|
||||
function StringReplace(search, replace) {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.replace"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace");
|
||||
|
||||
var subject = TO_STRING_INLINE(this);
|
||||
|
||||
// Decision tree for dispatch
|
||||
@ -543,10 +527,8 @@ function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
|
||||
|
||||
// ECMA-262 section 15.5.4.12
|
||||
function StringSearch(re) {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.search"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.search");
|
||||
|
||||
var regexp;
|
||||
if (IS_STRING(re)) {
|
||||
regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re);
|
||||
@ -565,10 +547,8 @@ function StringSearch(re) {
|
||||
|
||||
// ECMA-262 section 15.5.4.13
|
||||
function StringSlice(start, end) {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.slice"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.slice");
|
||||
|
||||
var s = TO_STRING_INLINE(this);
|
||||
var s_len = s.length;
|
||||
var start_i = TO_INTEGER(start);
|
||||
@ -609,10 +589,8 @@ function StringSlice(start, end) {
|
||||
|
||||
// ECMA-262 section 15.5.4.14
|
||||
function StringSplit(separator, limit) {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.split"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.split");
|
||||
|
||||
var subject = TO_STRING_INLINE(this);
|
||||
limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
|
||||
|
||||
@ -709,10 +687,8 @@ function StringSplitOnRegExp(subject, separator, limit, length) {
|
||||
|
||||
// ECMA-262 section 15.5.4.15
|
||||
function StringSubstring(start, end) {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.subString"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.subString");
|
||||
|
||||
var s = TO_STRING_INLINE(this);
|
||||
var s_len = s.length;
|
||||
|
||||
@ -744,10 +720,8 @@ function StringSubstring(start, end) {
|
||||
|
||||
// This is not a part of ECMA-262.
|
||||
function StringSubstr(start, n) {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.substr"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.substr");
|
||||
|
||||
var s = TO_STRING_INLINE(this);
|
||||
var len;
|
||||
|
||||
@ -786,65 +760,51 @@ function StringSubstr(start, n) {
|
||||
|
||||
// ECMA-262, 15.5.4.16
|
||||
function StringToLowerCase() {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.toLowerCase"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase");
|
||||
|
||||
return %StringToLowerCase(TO_STRING_INLINE(this));
|
||||
}
|
||||
|
||||
|
||||
// ECMA-262, 15.5.4.17
|
||||
function StringToLocaleLowerCase() {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.toLocaleLowerCase"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleLowerCase");
|
||||
|
||||
return %StringToLowerCase(TO_STRING_INLINE(this));
|
||||
}
|
||||
|
||||
|
||||
// ECMA-262, 15.5.4.18
|
||||
function StringToUpperCase() {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.toUpperCase"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.toUpperCase");
|
||||
|
||||
return %StringToUpperCase(TO_STRING_INLINE(this));
|
||||
}
|
||||
|
||||
|
||||
// ECMA-262, 15.5.4.19
|
||||
function StringToLocaleUpperCase() {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.toLocaleUpperCase"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleUpperCase");
|
||||
|
||||
return %StringToUpperCase(TO_STRING_INLINE(this));
|
||||
}
|
||||
|
||||
// ES5, 15.5.4.20
|
||||
function StringTrim() {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.trim"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.trim");
|
||||
|
||||
return %StringTrim(TO_STRING_INLINE(this), true, true);
|
||||
}
|
||||
|
||||
function StringTrimLeft() {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.trimLeft"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimLeft");
|
||||
|
||||
return %StringTrim(TO_STRING_INLINE(this), true, false);
|
||||
}
|
||||
|
||||
function StringTrimRight() {
|
||||
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
|
||||
throw MakeTypeError("called_on_null_or_undefined",
|
||||
["String.prototype.trimRight"]);
|
||||
}
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimRight");
|
||||
|
||||
return %StringTrim(TO_STRING_INLINE(this), false, true);
|
||||
}
|
||||
|
||||
|
@ -1015,6 +1015,21 @@ function ObjectGetPrototypeOf(obj) {
|
||||
return %GetPrototype(obj);
|
||||
}
|
||||
|
||||
// ES6 section 19.1.2.19.
|
||||
function ObjectSetPrototypeOf(obj, proto) {
|
||||
CHECK_OBJECT_COERCIBLE(obj, "Object.setPrototypeOf");
|
||||
|
||||
if (proto !== null && !IS_SPEC_OBJECT(proto)) {
|
||||
throw MakeTypeError("proto_object_or_null", [proto]);
|
||||
}
|
||||
|
||||
if (IS_SPEC_OBJECT(obj)) {
|
||||
%SetPrototype(obj, proto);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
// ES5 section 15.2.3.3
|
||||
function ObjectGetOwnPropertyDescriptor(obj, p) {
|
||||
@ -1443,6 +1458,7 @@ function SetUpObject() {
|
||||
"defineProperties", ObjectDefineProperties,
|
||||
"freeze", ObjectFreeze,
|
||||
"getPrototypeOf", ObjectGetPrototypeOf,
|
||||
"setPrototypeOf", ObjectSetPrototypeOf,
|
||||
"getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
|
||||
"getOwnPropertyNames", ObjectGetOwnPropertyNames,
|
||||
// getOwnPropertySymbols is added in symbol.js.
|
||||
|
170
test/mjsunit/set-prototype-of.js
Normal file
170
test/mjsunit/set-prototype-of.js
Normal file
@ -0,0 +1,170 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// Flags: --harmony-symbols
|
||||
|
||||
|
||||
function getObjects() {
|
||||
function func() {}
|
||||
return [
|
||||
func,
|
||||
new func(),
|
||||
{x: 5},
|
||||
/regexp/,
|
||||
['array'],
|
||||
// new Error(),
|
||||
new Date(),
|
||||
new Number(1),
|
||||
new Boolean(true),
|
||||
new String('str'),
|
||||
Object(Symbol())
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
var coercibleValues = [
|
||||
1,
|
||||
true,
|
||||
'string',
|
||||
Symbol()
|
||||
];
|
||||
|
||||
|
||||
var nonCoercibleValues = [
|
||||
undefined,
|
||||
null
|
||||
];
|
||||
|
||||
|
||||
var valuesWithoutNull = coercibleValues.concat(undefined);
|
||||
|
||||
|
||||
function TestSetPrototypeOfCoercibleValues() {
|
||||
for (var i = 0; i < coercibleValues.length; i++) {
|
||||
var value = coercibleValues[i];
|
||||
assertThrows(function() {
|
||||
Object.getPrototypeOf(value);
|
||||
}, TypeError);
|
||||
|
||||
assertEquals(Object.setPrototypeOf(value, {}), value);
|
||||
|
||||
assertThrows(function() {
|
||||
Object.getPrototypeOf(value);
|
||||
}, TypeError);
|
||||
}
|
||||
}
|
||||
TestSetPrototypeOfCoercibleValues();
|
||||
|
||||
|
||||
function TestSetPrototypeOfNonCoercibleValues() {
|
||||
for (var i = 0; i < nonCoercibleValues.length; i++) {
|
||||
var value = nonCoercibleValues[i];
|
||||
assertThrows(function() {
|
||||
Object.setPrototypeOf(value, {});
|
||||
}, TypeError);
|
||||
}
|
||||
}
|
||||
TestSetPrototypeOfNonCoercibleValues();
|
||||
|
||||
|
||||
function TestSetPrototypeToNonObject(proto) {
|
||||
var objects = getObjects();
|
||||
for (var i = 0; i < objects.length; i++) {
|
||||
var object = objects[i];
|
||||
for (var j = 0; j < valuesWithoutNull.length; j++) {
|
||||
var proto = valuesWithoutNull[j];
|
||||
assertThrows(function() {
|
||||
Object.setPrototypeOf(object, proto);
|
||||
}, TypeError);
|
||||
}
|
||||
}
|
||||
}
|
||||
TestSetPrototypeToNonObject();
|
||||
|
||||
|
||||
function TestSetPrototypeOf(object, proto) {
|
||||
assertEquals(Object.setPrototypeOf(object, proto), object);
|
||||
assertEquals(Object.getPrototypeOf(object), proto);
|
||||
}
|
||||
|
||||
|
||||
function TestSetPrototypeOfForObjects() {
|
||||
var objects1 = getObjects();
|
||||
var objects2 = getObjects();
|
||||
for (var i = 0; i < objects1.length; i++) {
|
||||
for (var j = 0; j < objects2.length; j++) {
|
||||
TestSetPrototypeOf(objects1[i], objects2[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
TestSetPrototypeOfForObjects();
|
||||
|
||||
|
||||
function TestSetPrototypeToNull() {
|
||||
var objects = getObjects();
|
||||
for (var i = 0; i < objects.length; i++) {
|
||||
TestSetPrototypeOf(objects[i], null);
|
||||
}
|
||||
}
|
||||
TestSetPrototypeToNull();
|
||||
|
||||
|
||||
function TestSetPrototypeOfNonExtensibleObject() {
|
||||
var objects = getObjects();
|
||||
var proto = {};
|
||||
for (var i = 0; i < objects.length; i++) {
|
||||
var object = objects[i];
|
||||
Object.preventExtensions(object);
|
||||
assertThrows(function() {
|
||||
Object.setPrototypeOf(object, proto);
|
||||
}, TypeError);
|
||||
}
|
||||
}
|
||||
TestSetPrototypeOfNonExtensibleObject();
|
||||
|
||||
|
||||
function TestLookup() {
|
||||
var object = {};
|
||||
assertFalse('x' in object);
|
||||
assertFalse('y' in object);
|
||||
|
||||
var oldProto = {
|
||||
x: 'old x',
|
||||
y: 'old y'
|
||||
};
|
||||
Object.setPrototypeOf(object, oldProto);
|
||||
assertEquals(object.x, 'old x');
|
||||
assertEquals(object.y, 'old y');
|
||||
|
||||
var newProto = {
|
||||
x: 'new x'
|
||||
};
|
||||
Object.setPrototypeOf(object, newProto);
|
||||
assertEquals(object.x, 'new x');
|
||||
assertFalse('y' in object);
|
||||
}
|
||||
TestLookup();
|
Loading…
Reference in New Issue
Block a user