Make bound functions have poisoned .caller and .arguments.
Also makes func.caller return null if the caller is a bound function, matching JSC. Fix bug preventing poisoned setters from triggering. TEST=mjsunit/function-bind, mjsunit/strict-mode Review URL: http://codereview.chromium.org/8333019 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9681 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
feeb0b0211
commit
cefbb1e7f8
@ -759,7 +759,12 @@ MaybeObject* Accessors::FunctionGetCaller(Object* object, void*) {
|
|||||||
caller = potential_caller;
|
caller = potential_caller;
|
||||||
potential_caller = it.next();
|
potential_caller = it.next();
|
||||||
}
|
}
|
||||||
|
// If caller is bound, return null. This is compatible with JSC, and
|
||||||
|
// allows us to make bound functions use the strict function map
|
||||||
|
// and its associated throwing caller and arguments.
|
||||||
|
if (caller->shared()->bound()) {
|
||||||
|
return isolate->heap()->null_value();
|
||||||
|
}
|
||||||
return CheckNonStrictCallerOrThrow(isolate, caller);
|
return CheckNonStrictCallerOrThrow(isolate, caller);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -522,7 +522,7 @@ Handle<DescriptorArray> Genesis::ComputeStrictFunctionInstanceDescriptor(
|
|||||||
? 4
|
? 4
|
||||||
: 5);
|
: 5);
|
||||||
PropertyAttributes attributes = static_cast<PropertyAttributes>(
|
PropertyAttributes attributes = static_cast<PropertyAttributes>(
|
||||||
DONT_ENUM | DONT_DELETE | READ_ONLY);
|
DONT_ENUM | DONT_DELETE);
|
||||||
|
|
||||||
{ // length
|
{ // length
|
||||||
Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionLength);
|
Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionLength);
|
||||||
@ -547,8 +547,8 @@ Handle<DescriptorArray> Genesis::ComputeStrictFunctionInstanceDescriptor(
|
|||||||
|
|
||||||
// prototype
|
// prototype
|
||||||
if (prototypeMode != DONT_ADD_PROTOTYPE) {
|
if (prototypeMode != DONT_ADD_PROTOTYPE) {
|
||||||
if (prototypeMode == ADD_WRITEABLE_PROTOTYPE) {
|
if (prototypeMode != ADD_WRITEABLE_PROTOTYPE) {
|
||||||
attributes = static_cast<PropertyAttributes>(attributes & ~READ_ONLY);
|
attributes = static_cast<PropertyAttributes>(attributes | READ_ONLY);
|
||||||
}
|
}
|
||||||
Handle<Foreign> foreign =
|
Handle<Foreign> foreign =
|
||||||
factory()->NewForeign(&Accessors::FunctionPrototype);
|
factory()->NewForeign(&Accessors::FunctionPrototype);
|
||||||
|
@ -1413,6 +1413,7 @@ MaybeObject* StoreIC::Store(State state,
|
|||||||
return TypeError("strict_read_only_property", object, name);
|
return TypeError("strict_read_only_property", object, name);
|
||||||
}
|
}
|
||||||
// Ignore other stores where the receiver is not a JSObject.
|
// Ignore other stores where the receiver is not a JSObject.
|
||||||
|
// TODO(1475): Must check prototype chains of object wrappers.
|
||||||
return *value;
|
return *value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1444,7 +1445,6 @@ MaybeObject* StoreIC::Store(State state,
|
|||||||
// Lookup the property locally in the receiver.
|
// Lookup the property locally in the receiver.
|
||||||
if (FLAG_use_ic && !receiver->IsJSGlobalProxy()) {
|
if (FLAG_use_ic && !receiver->IsJSGlobalProxy()) {
|
||||||
LookupResult lookup(isolate());
|
LookupResult lookup(isolate());
|
||||||
|
|
||||||
if (LookupForWrite(*receiver, *name, &lookup)) {
|
if (LookupForWrite(*receiver, *name, &lookup)) {
|
||||||
// Generate a stub for this store.
|
// Generate a stub for this store.
|
||||||
UpdateCaches(&lookup, state, strict_mode, receiver, name, value);
|
UpdateCaches(&lookup, state, strict_mode, receiver, name, value);
|
||||||
|
@ -1520,6 +1520,8 @@ function FunctionBind(this_arg) { // Length is 1.
|
|||||||
throw new $TypeError('Bind must be called on a function');
|
throw new $TypeError('Bind must be called on a function');
|
||||||
}
|
}
|
||||||
var boundFunction = function () {
|
var boundFunction = function () {
|
||||||
|
// Poison .arguments and .caller, but is otherwise not detectable.
|
||||||
|
"use strict";
|
||||||
// This function must not use any object literals (Object, Array, RegExp),
|
// This function must not use any object literals (Object, Array, RegExp),
|
||||||
// since the literals-array is being used to store the bound data.
|
// since the literals-array is being used to store the bound data.
|
||||||
if (%_IsConstructCall()) {
|
if (%_IsConstructCall()) {
|
||||||
|
@ -263,3 +263,34 @@ assertEquals(["foo", 0, undefined], s());
|
|||||||
|
|
||||||
s = soo.bind(true);
|
s = soo.bind(true);
|
||||||
assertEquals([true, 0, undefined], s());
|
assertEquals([true, 0, undefined], s());
|
||||||
|
|
||||||
|
// Test that .arguments and .caller are poisoned according to the ES5 spec.
|
||||||
|
|
||||||
|
// Check that property descriptors are correct (unconfigurable, unenumerable,
|
||||||
|
// and both get and set is the ThrowTypeError function).
|
||||||
|
var cdesc = Object.getOwnPropertyDescriptor(f, "caller");
|
||||||
|
var adesc = Object.getOwnPropertyDescriptor(f, "arguments");
|
||||||
|
|
||||||
|
assertFalse(cdesc.enumerable);
|
||||||
|
assertFalse(cdesc.configurable);
|
||||||
|
|
||||||
|
assertFalse(adesc.enumerable);
|
||||||
|
assertFalse(adesc.configurable);
|
||||||
|
|
||||||
|
assertSame(cdesc.get, cdesc.set);
|
||||||
|
assertSame(cdesc.get, adesc.get);
|
||||||
|
assertSame(cdesc.get, adesc.set);
|
||||||
|
|
||||||
|
assertTrue(cdesc.get instanceof Function);
|
||||||
|
assertEquals(0, cdesc.get.length);
|
||||||
|
assertThrows(cdesc.get, TypeError);
|
||||||
|
|
||||||
|
assertThrows(function() { return f.caller; }, TypeError);
|
||||||
|
assertThrows(function() { f.caller = 42; }, TypeError);
|
||||||
|
assertThrows(function() { return f.arguments; }, TypeError);
|
||||||
|
assertThrows(function() { f.arguments = 42; }, TypeError);
|
||||||
|
|
||||||
|
// Shouldn't throw. Accessing the functions caller must throw if
|
||||||
|
// the caller is strict and the callee isn't. A bound function is built-in,
|
||||||
|
// but not considered strict.
|
||||||
|
(function foo() { return foo.caller; }).bind()();
|
||||||
|
@ -1051,14 +1051,20 @@ function CheckPillDescriptor(func, name) {
|
|||||||
}
|
}
|
||||||
assertThrows(function() { strict.caller; }, TypeError);
|
assertThrows(function() { strict.caller; }, TypeError);
|
||||||
assertThrows(function() { strict.arguments; }, TypeError);
|
assertThrows(function() { strict.arguments; }, TypeError);
|
||||||
|
assertThrows(function() { strict.caller = 42; }, TypeError);
|
||||||
|
assertThrows(function() { strict.arguments = 42; }, TypeError);
|
||||||
|
|
||||||
var another = new Function("'use strict'");
|
var another = new Function("'use strict'");
|
||||||
assertThrows(function() { another.caller; }, TypeError);
|
assertThrows(function() { another.caller; }, TypeError);
|
||||||
assertThrows(function() { another.arguments; }, TypeError);
|
assertThrows(function() { another.arguments; }, TypeError);
|
||||||
|
assertThrows(function() { another.caller = 42; }, TypeError);
|
||||||
|
assertThrows(function() { another.arguments = 42; }, TypeError);
|
||||||
|
|
||||||
var third = (function() { "use strict"; return function() {}; })();
|
var third = (function() { "use strict"; return function() {}; })();
|
||||||
assertThrows(function() { third.caller; }, TypeError);
|
assertThrows(function() { third.caller; }, TypeError);
|
||||||
assertThrows(function() { third.arguments; }, TypeError);
|
assertThrows(function() { third.arguments; }, TypeError);
|
||||||
|
assertThrows(function() { third.caller = 42; }, TypeError);
|
||||||
|
assertThrows(function() { third.arguments = 42; }, TypeError);
|
||||||
|
|
||||||
CheckPillDescriptor(strict, "caller");
|
CheckPillDescriptor(strict, "caller");
|
||||||
CheckPillDescriptor(strict, "arguments");
|
CheckPillDescriptor(strict, "arguments");
|
||||||
|
@ -30,10 +30,6 @@ def FAIL_OK = FAIL, OKAY
|
|||||||
|
|
||||||
############################### BUGS ###################################
|
############################### BUGS ###################################
|
||||||
|
|
||||||
# A bound function should fail on access to 'caller' and 'arguments'.
|
|
||||||
S15.3.4.5_A1: FAIL
|
|
||||||
S15.3.4.5_A2: FAIL
|
|
||||||
|
|
||||||
# '__proto__' should be treated as a normal property in JSON.
|
# '__proto__' should be treated as a normal property in JSON.
|
||||||
S15.12.2_A1: FAIL
|
S15.12.2_A1: FAIL
|
||||||
|
|
||||||
|
@ -30,10 +30,6 @@ def FAIL_OK = FAIL, OKAY
|
|||||||
|
|
||||||
############################### BUGS ###################################
|
############################### BUGS ###################################
|
||||||
|
|
||||||
# A bound function should fail on access to 'caller' and 'arguments'.
|
|
||||||
S15.3.4.5_A1: FAIL
|
|
||||||
S15.3.4.5_A2: FAIL
|
|
||||||
|
|
||||||
# '__proto__' should be treated as a normal property in JSON.
|
# '__proto__' should be treated as a normal property in JSON.
|
||||||
S15.12.2_A1: FAIL
|
S15.12.2_A1: FAIL
|
||||||
|
|
||||||
@ -43,9 +39,6 @@ S8.7_A5_T2: FAIL
|
|||||||
# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1624
|
# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1624
|
||||||
S10.4.2.1_A1: FAIL
|
S10.4.2.1_A1: FAIL
|
||||||
|
|
||||||
# V8 Bug.
|
|
||||||
S13.2.3_A1: FAIL
|
|
||||||
|
|
||||||
# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1530
|
# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1530
|
||||||
S15.3.3.1_A4: FAIL
|
S15.3.3.1_A4: FAIL
|
||||||
|
|
||||||
@ -128,12 +121,6 @@ S15.3.3.1_A4: FAIL
|
|||||||
15.2.3.7-6-a-176: FAIL
|
15.2.3.7-6-a-176: FAIL
|
||||||
15.2.3.7-6-a-177: FAIL
|
15.2.3.7-6-a-177: FAIL
|
||||||
|
|
||||||
# V8 Bug: http://code.google.com/p/v8/issues/detail?id=893
|
|
||||||
15.3.4.5-20-2: FAIL
|
|
||||||
15.3.4.5-20-3: FAIL
|
|
||||||
15.3.4.5-21-2: FAIL
|
|
||||||
15.3.4.5-21-3: FAIL
|
|
||||||
|
|
||||||
# Invalid test cases (recent change adding var changes semantics)
|
# Invalid test cases (recent change adding var changes semantics)
|
||||||
S8.3_A1_T1: FAIL
|
S8.3_A1_T1: FAIL
|
||||||
S15.3_A3_T1: FAIL
|
S15.3_A3_T1: FAIL
|
||||||
|
Loading…
Reference in New Issue
Block a user