Commit 18b9c1ce authored by caitpotter88's avatar caitpotter88 Committed by Commit bot

[proxies] throw TypeError if is_callable Map bit is unset

Per ProxyCreate() (https://tc39.github.io/ecma262/#sec-proxycreate), a Proxy
is only given a [[Call]] slot if the target has a [[Call]] slot as well. This
was previously implemented correctly for [[Construct]], but not for [[Call]].

BUG=v8:4797, v8:4796, v8:1543
LOG=N
R=cbruni@chromium.org, neis@chromium.org, adamk@chromium.org, littledan@chromium.org

Review URL: https://codereview.chromium.org/1752133004

Cr-Commit-Position: refs/heads/master@{#34461}
parent a3a583db
......@@ -2260,6 +2260,12 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
__ cmp(r5, Operand(JS_BOUND_FUNCTION_TYPE));
__ Jump(masm->isolate()->builtins()->CallBoundFunction(tail_call_mode),
RelocInfo::CODE_TARGET, eq);
// Check if target has a [[Call]] internal method.
__ ldrb(r4, FieldMemOperand(r4, Map::kBitFieldOffset));
__ tst(r4, Operand(1 << Map::kIsCallable));
__ b(eq, &non_callable);
__ cmp(r5, Operand(JS_PROXY_TYPE));
__ b(ne, &non_function);
......@@ -2280,10 +2286,6 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
// 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception).
__ bind(&non_function);
// Check if target has a [[Call]] internal method.
__ ldrb(r4, FieldMemOperand(r4, Map::kBitFieldOffset));
__ tst(r4, Operand(1 << Map::kIsCallable));
__ b(eq, &non_callable);
// Overwrite the original receiver the (original) target.
__ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
// Let the "call_as_function_delegate" take care of the rest.
......
......@@ -2300,6 +2300,11 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
__ Cmp(x5, JS_BOUND_FUNCTION_TYPE);
__ Jump(masm->isolate()->builtins()->CallBoundFunction(tail_call_mode),
RelocInfo::CODE_TARGET, eq);
// Check if target has a [[Call]] internal method.
__ Ldrb(x4, FieldMemOperand(x4, Map::kBitFieldOffset));
__ TestAndBranchIfAllClear(x4, 1 << Map::kIsCallable, &non_callable);
__ Cmp(x5, JS_PROXY_TYPE);
__ B(ne, &non_function);
......@@ -2320,9 +2325,6 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
// 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception).
__ Bind(&non_function);
// Check if target has a [[Call]] internal method.
__ Ldrb(x4, FieldMemOperand(x4, Map::kBitFieldOffset));
__ TestAndBranchIfAllClear(x4, 1 << Map::kIsCallable, &non_callable);
// Overwrite the original receiver with the (original) target.
__ Poke(x1, Operand(x0, LSL, kXRegSizeLog2));
// Let the "call_as_function_delegate" take care of the rest.
......
......@@ -2202,6 +2202,11 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
__ CmpInstanceType(ecx, JS_BOUND_FUNCTION_TYPE);
__ j(equal, masm->isolate()->builtins()->CallBoundFunction(tail_call_mode),
RelocInfo::CODE_TARGET);
// Check if target has a [[Call]] internal method.
__ test_b(FieldOperand(ecx, Map::kBitFieldOffset), 1 << Map::kIsCallable);
__ j(zero, &non_callable);
__ CmpInstanceType(ecx, JS_PROXY_TYPE);
__ j(not_equal, &non_function);
......@@ -2224,9 +2229,6 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
// 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception).
__ bind(&non_function);
// Check if target has a [[Call]] internal method.
__ test_b(FieldOperand(ecx, Map::kBitFieldOffset), 1 << Map::kIsCallable);
__ j(zero, &non_callable, Label::kNear);
// Overwrite the original receiver with the (original) target.
__ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi);
// Let the "call_as_function_delegate" take care of the rest.
......
......@@ -2274,6 +2274,12 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
RelocInfo::CODE_TARGET, eq, t2, Operand(JS_FUNCTION_TYPE));
__ Jump(masm->isolate()->builtins()->CallBoundFunction(tail_call_mode),
RelocInfo::CODE_TARGET, eq, t2, Operand(JS_BOUND_FUNCTION_TYPE));
// Check if target has a [[Call]] internal method.
__ lbu(t1, FieldMemOperand(t1, Map::kBitFieldOffset));
__ And(t1, t1, Operand(1 << Map::kIsCallable));
__ Branch(&non_callable, eq, t1, Operand(zero_reg));
__ Branch(&non_function, ne, t2, Operand(JS_PROXY_TYPE));
// 0. Prepare for tail call if necessary.
......@@ -2293,10 +2299,6 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
// 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception).
__ bind(&non_function);
// Check if target has a [[Call]] internal method.
__ lbu(t1, FieldMemOperand(t1, Map::kBitFieldOffset));
__ And(t1, t1, Operand(1 << Map::kIsCallable));
__ Branch(&non_callable, eq, t1, Operand(zero_reg));
// Overwrite the original receiver with the (original) target.
__ Lsa(at, sp, a0, kPointerSizeLog2);
__ sw(a1, MemOperand(at));
......
......@@ -2267,6 +2267,12 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
RelocInfo::CODE_TARGET, eq, t2, Operand(JS_FUNCTION_TYPE));
__ Jump(masm->isolate()->builtins()->CallBoundFunction(tail_call_mode),
RelocInfo::CODE_TARGET, eq, t2, Operand(JS_BOUND_FUNCTION_TYPE));
// Check if target has a [[Call]] internal method.
__ lbu(t1, FieldMemOperand(t1, Map::kBitFieldOffset));
__ And(t1, t1, Operand(1 << Map::kIsCallable));
__ Branch(&non_callable, eq, t1, Operand(zero_reg));
__ Branch(&non_function, ne, t2, Operand(JS_PROXY_TYPE));
// 0. Prepare for tail call if necessary.
......@@ -2286,10 +2292,6 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
// 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception).
__ bind(&non_function);
// Check if target has a [[Call]] internal method.
__ lbu(t1, FieldMemOperand(t1, Map::kBitFieldOffset));
__ And(t1, t1, Operand(1 << Map::kIsCallable));
__ Branch(&non_callable, eq, t1, Operand(zero_reg));
// Overwrite the original receiver with the (original) target.
__ Dlsa(at, sp, a0, kPointerSizeLog2);
__ sd(a1, MemOperand(at));
......
......@@ -2409,6 +2409,12 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
__ CmpInstanceType(rcx, JS_BOUND_FUNCTION_TYPE);
__ j(equal, masm->isolate()->builtins()->CallBoundFunction(tail_call_mode),
RelocInfo::CODE_TARGET);
// Check if target has a [[Call]] internal method.
__ testb(FieldOperand(rcx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsCallable));
__ j(zero, &non_callable);
__ CmpInstanceType(rcx, JS_PROXY_TYPE);
__ j(not_equal, &non_function);
......@@ -2431,10 +2437,6 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
// 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception).
__ bind(&non_function);
// Check if target has a [[Call]] internal method.
__ testb(FieldOperand(rcx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsCallable));
__ j(zero, &non_callable, Label::kNear);
// Overwrite the original receiver with the (original) target.
__ movp(args.GetReceiverOperand(), rdi);
// Let the "call_as_function_delegate" take care of the rest.
......
......@@ -87,3 +87,36 @@
assertTrue(called_target);
assertTrue(called_handler);
})();
(function testCallProxyNonCallableTarget() {
var values = [NaN, 1.5, 100, /RegExp/, "string", {}, [], Symbol(),
new Map(), new Set(), new WeakMap(), new WeakSet()];
values.forEach(target => {
target = Object(target);
var proxy = new Proxy(target, { apply() { assertUnreachable(); } });
assertThrows(() => { proxy(); }, TypeError);
assertThrows(() => { ({ proxy }).proxy(); }, TypeError);
assertThrows(() => { Reflect.apply(proxy, null, []); }, TypeError);
assertThrows(() => { Reflect.apply(proxy, { proxy }, []); }, TypeError);
assertThrows(() => {
Function.prototype.call.apply(proxy, [null]);
}, TypeError);
assertThrows(() => {
Function.prototype.apply.apply(proxy, [null, []]);
}, TypeError);
var proxy_to_proxy = new Proxy(proxy, { apply() { assertUnreachable(); } });
assertThrows(() => { proxy_to_proxy(); }, TypeError);
assertThrows(() => { ({ proxy_to_proxy }).proxy_to_proxy(); }, TypeError);
assertThrows(() => { Reflect.apply(proxy_to_proxy, null, []); }, TypeError);
assertThrows(() => { Reflect.apply(proxy_to_proxy, { proxy }, []); },
TypeError);
assertThrows(() => {
Function.prototype.call.apply(proxy_to_proxy, [null]);
}, TypeError);
assertThrows(() => {
Function.prototype.apply.apply(proxy_to_proxy, [null, []]);
}, TypeError);
});
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment