1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/builtins/builtins-proxy-gen.h"
6 #include "src/builtins/builtins-utils-gen.h"
7 #include "src/builtins/builtins-utils.h"
8 #include "src/builtins/builtins.h"
9
10 #include "src/counters.h"
11 #include "src/objects-inl.h"
12
13 namespace v8 {
14 namespace internal {
15
GotoIfRevokedProxy(Node * object,Label * if_proxy_revoked)16 void ProxiesCodeStubAssembler::GotoIfRevokedProxy(Node* object,
17 Label* if_proxy_revoked) {
18 Label proxy_not_revoked(this);
19 GotoIfNot(IsJSProxy(object), &proxy_not_revoked);
20 Branch(IsJSReceiver(CAST(LoadObjectField(object, JSProxy::kHandlerOffset))),
21 &proxy_not_revoked, if_proxy_revoked);
22 BIND(&proxy_not_revoked);
23 }
24
AllocateProxy(Node * target,Node * handler,Node * context)25 Node* ProxiesCodeStubAssembler::AllocateProxy(Node* target, Node* handler,
26 Node* context) {
27 VARIABLE(map, MachineRepresentation::kTagged);
28
29 Label callable_target(this), constructor_target(this), none_target(this),
30 create_proxy(this);
31
32 Node* nativeContext = LoadNativeContext(context);
33
34 Branch(IsCallable(target), &callable_target, &none_target);
35
36 BIND(&callable_target);
37 {
38 // Every object that is a constructor is implicitly callable
39 // so it's okay to nest this check here
40 GotoIf(IsConstructor(target), &constructor_target);
41 map.Bind(
42 LoadContextElement(nativeContext, Context::PROXY_CALLABLE_MAP_INDEX));
43 Goto(&create_proxy);
44 }
45 BIND(&constructor_target);
46 {
47 map.Bind(LoadContextElement(nativeContext,
48 Context::PROXY_CONSTRUCTOR_MAP_INDEX));
49 Goto(&create_proxy);
50 }
51 BIND(&none_target);
52 {
53 map.Bind(LoadContextElement(nativeContext, Context::PROXY_MAP_INDEX));
54 Goto(&create_proxy);
55 }
56
57 BIND(&create_proxy);
58 Node* proxy = Allocate(JSProxy::kSize);
59 StoreMapNoWriteBarrier(proxy, map.value());
60 StoreObjectFieldRoot(proxy, JSProxy::kPropertiesOrHashOffset,
61 Heap::kEmptyPropertyDictionaryRootIndex);
62 StoreObjectFieldNoWriteBarrier(proxy, JSProxy::kTargetOffset, target);
63 StoreObjectFieldNoWriteBarrier(proxy, JSProxy::kHandlerOffset, handler);
64
65 return proxy;
66 }
67
AllocateJSArrayForCodeStubArguments(Node * context,CodeStubArguments & args,Node * argc,ParameterMode mode)68 Node* ProxiesCodeStubAssembler::AllocateJSArrayForCodeStubArguments(
69 Node* context, CodeStubArguments& args, Node* argc, ParameterMode mode) {
70 Comment("AllocateJSArrayForCodeStubArguments");
71
72 Label if_empty_array(this), allocate_js_array(this);
73 // Do not use AllocateJSArray since {elements} might end up in LOS.
74 VARIABLE(elements, MachineRepresentation::kTagged);
75
76 TNode<Smi> length = ParameterToTagged(argc, mode);
77 GotoIf(SmiEqual(length, SmiConstant(0)), &if_empty_array);
78 {
79 Label if_large_object(this, Label::kDeferred);
80 Node* allocated_elements = AllocateFixedArray(PACKED_ELEMENTS, argc, mode,
81 kAllowLargeObjectAllocation);
82 elements.Bind(allocated_elements);
83
84 VARIABLE(index, MachineType::PointerRepresentation(),
85 IntPtrConstant(FixedArrayBase::kHeaderSize - kHeapObjectTag));
86 VariableList list({&index}, zone());
87
88 GotoIf(SmiGreaterThan(length, SmiConstant(FixedArray::kMaxRegularLength)),
89 &if_large_object);
90 args.ForEach(list, [=, &index](Node* arg) {
91 StoreNoWriteBarrier(MachineRepresentation::kTagged, allocated_elements,
92 index.value(), arg);
93 Increment(&index, kPointerSize);
94 });
95 Goto(&allocate_js_array);
96
97 BIND(&if_large_object);
98 {
99 args.ForEach(list, [=, &index](Node* arg) {
100 Store(allocated_elements, index.value(), arg);
101 Increment(&index, kPointerSize);
102 });
103 Goto(&allocate_js_array);
104 }
105 }
106
107 BIND(&if_empty_array);
108 {
109 elements.Bind(EmptyFixedArrayConstant());
110 Goto(&allocate_js_array);
111 }
112
113 BIND(&allocate_js_array);
114 // Allocate the result JSArray.
115 Node* native_context = LoadNativeContext(context);
116 Node* array_map = LoadJSArrayElementsMap(PACKED_ELEMENTS, native_context);
117 Node* array = AllocateUninitializedJSArrayWithoutElements(array_map, length);
118 StoreObjectFieldNoWriteBarrier(array, JSObject::kElementsOffset,
119 elements.value());
120
121 return array;
122 }
123
CreateProxyRevokeFunctionContext(Node * proxy,Node * native_context)124 Node* ProxiesCodeStubAssembler::CreateProxyRevokeFunctionContext(
125 Node* proxy, Node* native_context) {
126 Node* const context = Allocate(FixedArray::SizeFor(kProxyContextLength));
127 StoreMapNoWriteBarrier(context, Heap::kFunctionContextMapRootIndex);
128 InitializeFunctionContext(native_context, context, kProxyContextLength);
129 StoreContextElementNoWriteBarrier(context, kProxySlot, proxy);
130 return context;
131 }
132
AllocateProxyRevokeFunction(Node * proxy,Node * context)133 Node* ProxiesCodeStubAssembler::AllocateProxyRevokeFunction(Node* proxy,
134 Node* context) {
135 Node* const native_context = LoadNativeContext(context);
136
137 Node* const proxy_context =
138 CreateProxyRevokeFunctionContext(proxy, native_context);
139 Node* const revoke_map = LoadContextElement(
140 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
141 Node* const revoke_info =
142 LoadContextElement(native_context, Context::PROXY_REVOKE_SHARED_FUN);
143
144 return AllocateFunctionWithMapAndContext(revoke_map, revoke_info,
145 proxy_context);
146 }
147
148 // ES #sec-proxy-constructor
TF_BUILTIN(ProxyConstructor,ProxiesCodeStubAssembler)149 TF_BUILTIN(ProxyConstructor, ProxiesCodeStubAssembler) {
150 Node* context = Parameter(Descriptor::kContext);
151
152 // 1. If NewTarget is undefined, throw a TypeError exception.
153 Node* new_target = Parameter(Descriptor::kJSNewTarget);
154 Label throwtypeerror(this, Label::kDeferred), createproxy(this);
155 Branch(IsUndefined(new_target), &throwtypeerror, &createproxy);
156
157 BIND(&throwtypeerror);
158 {
159 ThrowTypeError(context, MessageTemplate::kConstructorNotFunction, "Proxy");
160 }
161
162 // 2. Return ? ProxyCreate(target, handler).
163 BIND(&createproxy);
164 {
165 // https://tc39.github.io/ecma262/#sec-proxycreate
166 Node* target = Parameter(Descriptor::kTarget);
167 Node* handler = Parameter(Descriptor::kHandler);
168
169 // 1. If Type(target) is not Object, throw a TypeError exception.
170 // 2. If target is a Proxy exotic object and target.[[ProxyHandler]] is
171 // null, throw a TypeError exception.
172 // 3. If Type(handler) is not Object, throw a TypeError exception.
173 // 4. If handler is a Proxy exotic object and handler.[[ProxyHandler]]
174 // is null, throw a TypeError exception.
175 Label throw_proxy_non_object(this, Label::kDeferred),
176 throw_proxy_handler_or_target_revoked(this, Label::kDeferred),
177 return_create_proxy(this);
178
179 GotoIf(TaggedIsSmi(target), &throw_proxy_non_object);
180 GotoIfNot(IsJSReceiver(target), &throw_proxy_non_object);
181 GotoIfRevokedProxy(target, &throw_proxy_handler_or_target_revoked);
182
183 GotoIf(TaggedIsSmi(handler), &throw_proxy_non_object);
184 GotoIfNot(IsJSReceiver(handler), &throw_proxy_non_object);
185 GotoIfRevokedProxy(handler, &throw_proxy_handler_or_target_revoked);
186
187 // 5. Let P be a newly created object.
188 // 6. Set P's essential internal methods (except for [[Call]] and
189 // [[Construct]]) to the definitions specified in 9.5.
190 // 7. If IsCallable(target) is true, then
191 // a. Set P.[[Call]] as specified in 9.5.12.
192 // b. If IsConstructor(target) is true, then
193 // 1. Set P.[[Construct]] as specified in 9.5.13.
194 // 8. Set P.[[ProxyTarget]] to target.
195 // 9. Set P.[[ProxyHandler]] to handler.
196 // 10. Return P.
197 Return(AllocateProxy(target, handler, context));
198
199 BIND(&throw_proxy_non_object);
200 ThrowTypeError(context, MessageTemplate::kProxyNonObject);
201
202 BIND(&throw_proxy_handler_or_target_revoked);
203 ThrowTypeError(context, MessageTemplate::kProxyHandlerOrTargetRevoked);
204 }
205 }
206
TF_BUILTIN(ProxyRevocable,ProxiesCodeStubAssembler)207 TF_BUILTIN(ProxyRevocable, ProxiesCodeStubAssembler) {
208 Node* const target = Parameter(Descriptor::kTarget);
209 Node* const handler = Parameter(Descriptor::kHandler);
210 Node* const context = Parameter(Descriptor::kContext);
211 Node* const native_context = LoadNativeContext(context);
212
213 Label throw_proxy_non_object(this, Label::kDeferred),
214 throw_proxy_handler_or_target_revoked(this, Label::kDeferred),
215 return_create_proxy(this);
216
217 GotoIf(TaggedIsSmi(target), &throw_proxy_non_object);
218 GotoIfNot(IsJSReceiver(target), &throw_proxy_non_object);
219 GotoIfRevokedProxy(target, &throw_proxy_handler_or_target_revoked);
220
221 GotoIf(TaggedIsSmi(handler), &throw_proxy_non_object);
222 GotoIfNot(IsJSReceiver(handler), &throw_proxy_non_object);
223 GotoIfRevokedProxy(handler, &throw_proxy_handler_or_target_revoked);
224
225 Node* const proxy = AllocateProxy(target, handler, context);
226 Node* const revoke = AllocateProxyRevokeFunction(proxy, context);
227
228 Node* const result = Allocate(JSProxyRevocableResult::kSize);
229 Node* const result_map = LoadContextElement(
230 native_context, Context::PROXY_REVOCABLE_RESULT_MAP_INDEX);
231 StoreMapNoWriteBarrier(result, result_map);
232 StoreObjectFieldRoot(result, JSProxyRevocableResult::kPropertiesOrHashOffset,
233 Heap::kEmptyFixedArrayRootIndex);
234 StoreObjectFieldRoot(result, JSProxyRevocableResult::kElementsOffset,
235 Heap::kEmptyFixedArrayRootIndex);
236 StoreObjectFieldNoWriteBarrier(result, JSProxyRevocableResult::kProxyOffset,
237 proxy);
238 StoreObjectFieldNoWriteBarrier(result, JSProxyRevocableResult::kRevokeOffset,
239 revoke);
240 Return(result);
241
242 BIND(&throw_proxy_non_object);
243 ThrowTypeError(context, MessageTemplate::kProxyNonObject);
244
245 BIND(&throw_proxy_handler_or_target_revoked);
246 ThrowTypeError(context, MessageTemplate::kProxyHandlerOrTargetRevoked);
247 }
248
249 // Proxy Revocation Functions
250 // https://tc39.github.io/ecma262/#sec-proxy-revocation-functions
TF_BUILTIN(ProxyRevoke,ProxiesCodeStubAssembler)251 TF_BUILTIN(ProxyRevoke, ProxiesCodeStubAssembler) {
252 Node* const context = Parameter(Descriptor::kContext);
253
254 // 1. Let p be F.[[RevocableProxy]].
255 Node* const proxy_slot = IntPtrConstant(kProxySlot);
256 Node* const proxy = LoadContextElement(context, proxy_slot);
257
258 Label revoke_called(this);
259
260 // 2. If p is null, ...
261 GotoIf(IsNull(proxy), &revoke_called);
262
263 // 3. Set F.[[RevocableProxy]] to null.
264 StoreContextElement(context, proxy_slot, NullConstant());
265
266 // 4. Assert: p is a Proxy object.
267 CSA_ASSERT(this, IsJSProxy(proxy));
268
269 // 5. Set p.[[ProxyTarget]] to null.
270 StoreObjectField(proxy, JSProxy::kTargetOffset, NullConstant());
271
272 // 6. Set p.[[ProxyHandler]] to null.
273 StoreObjectField(proxy, JSProxy::kHandlerOffset, NullConstant());
274
275 // 7. Return undefined.
276 Return(UndefinedConstant());
277
278 BIND(&revoke_called);
279 // 2. ... return undefined.
280 Return(UndefinedConstant());
281 }
282
TF_BUILTIN(CallProxy,ProxiesCodeStubAssembler)283 TF_BUILTIN(CallProxy, ProxiesCodeStubAssembler) {
284 Node* argc = Parameter(Descriptor::kActualArgumentsCount);
285 Node* argc_ptr = ChangeInt32ToIntPtr(argc);
286 Node* proxy = Parameter(Descriptor::kFunction);
287 Node* context = Parameter(Descriptor::kContext);
288
289 CSA_ASSERT(this, IsJSProxy(proxy));
290 CSA_ASSERT(this, IsCallable(proxy));
291
292 PerformStackCheck(CAST(context));
293
294 Label throw_proxy_handler_revoked(this, Label::kDeferred),
295 trap_undefined(this);
296
297 // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
298 Node* handler = LoadObjectField(proxy, JSProxy::kHandlerOffset);
299
300 // 2. If handler is null, throw a TypeError exception.
301 CSA_ASSERT(this, IsNullOrJSReceiver(handler));
302 GotoIfNot(IsJSReceiver(handler), &throw_proxy_handler_revoked);
303
304 // 3. Assert: Type(handler) is Object.
305 CSA_ASSERT(this, IsJSReceiver(handler));
306
307 // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
308 Node* target = LoadObjectField(proxy, JSProxy::kTargetOffset);
309
310 // 5. Let trap be ? GetMethod(handler, "apply").
311 // 6. If trap is undefined, then
312 Handle<Name> trap_name = factory()->apply_string();
313 Node* trap = GetMethod(context, handler, trap_name, &trap_undefined);
314
315 CodeStubArguments args(this, argc_ptr);
316 Node* receiver = args.GetReceiver();
317
318 // 7. Let argArray be CreateArrayFromList(argumentsList).
319 Node* array = AllocateJSArrayForCodeStubArguments(context, args, argc_ptr,
320 INTPTR_PARAMETERS);
321
322 // 8. Return Call(trap, handler, «target, thisArgument, argArray»).
323 Node* result = CallJS(CodeFactory::Call(isolate()), context, trap, handler,
324 target, receiver, array);
325 args.PopAndReturn(result);
326
327 BIND(&trap_undefined);
328 {
329 // 6.a. Return Call(target, thisArgument, argumentsList).
330 TailCallStub(CodeFactory::Call(isolate()), context, target, argc);
331 }
332
333 BIND(&throw_proxy_handler_revoked);
334 { ThrowTypeError(context, MessageTemplate::kProxyRevoked, "apply"); }
335 }
336
TF_BUILTIN(ConstructProxy,ProxiesCodeStubAssembler)337 TF_BUILTIN(ConstructProxy, ProxiesCodeStubAssembler) {
338 Node* argc = Parameter(Descriptor::kActualArgumentsCount);
339 Node* argc_ptr = ChangeInt32ToIntPtr(argc);
340 Node* proxy = Parameter(Descriptor::kTarget);
341 Node* new_target = Parameter(Descriptor::kNewTarget);
342 Node* context = Parameter(Descriptor::kContext);
343
344 CSA_ASSERT(this, IsJSProxy(proxy));
345 CSA_ASSERT(this, IsCallable(proxy));
346
347 Label throw_proxy_handler_revoked(this, Label::kDeferred),
348 trap_undefined(this), not_an_object(this, Label::kDeferred);
349
350 // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
351 Node* handler = LoadObjectField(proxy, JSProxy::kHandlerOffset);
352
353 // 2. If handler is null, throw a TypeError exception.
354 CSA_ASSERT(this, IsNullOrJSReceiver(handler));
355 GotoIfNot(IsJSReceiver(handler), &throw_proxy_handler_revoked);
356
357 // 3. Assert: Type(handler) is Object.
358 CSA_ASSERT(this, IsJSReceiver(handler));
359
360 // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
361 Node* target = LoadObjectField(proxy, JSProxy::kTargetOffset);
362
363 // 5. Let trap be ? GetMethod(handler, "construct").
364 // 6. If trap is undefined, then
365 Handle<Name> trap_name = factory()->construct_string();
366 Node* trap = GetMethod(context, handler, trap_name, &trap_undefined);
367
368 CodeStubArguments args(this, argc_ptr);
369
370 // 7. Let argArray be CreateArrayFromList(argumentsList).
371 Node* array = AllocateJSArrayForCodeStubArguments(context, args, argc_ptr,
372 INTPTR_PARAMETERS);
373
374 // 8. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »).
375 Node* new_obj = CallJS(CodeFactory::Call(isolate()), context, trap, handler,
376 target, array, new_target);
377
378 // 9. If Type(newObj) is not Object, throw a TypeError exception.
379 GotoIf(TaggedIsSmi(new_obj), ¬_an_object);
380 GotoIfNot(IsJSReceiver(new_obj), ¬_an_object);
381
382 // 10. Return newObj.
383 args.PopAndReturn(new_obj);
384
385 BIND(¬_an_object);
386 {
387 ThrowTypeError(context, MessageTemplate::kProxyConstructNonObject, new_obj);
388 }
389
390 BIND(&trap_undefined);
391 {
392 // 6.a. Assert: target has a [[Construct]] internal method.
393 CSA_ASSERT(this, IsConstructor(target));
394
395 // 6.b. Return ? Construct(target, argumentsList, newTarget).
396 TailCallStub(CodeFactory::Construct(isolate()), context, target, new_target,
397 argc);
398 }
399
400 BIND(&throw_proxy_handler_revoked);
401 { ThrowTypeError(context, MessageTemplate::kProxyRevoked, "construct"); }
402 }
403
TF_BUILTIN(ProxyHasProperty,ProxiesCodeStubAssembler)404 TF_BUILTIN(ProxyHasProperty, ProxiesCodeStubAssembler) {
405 Node* context = Parameter(Descriptor::kContext);
406 Node* proxy = Parameter(Descriptor::kProxy);
407 Node* name = Parameter(Descriptor::kName);
408
409 CSA_ASSERT(this, IsJSProxy(proxy));
410
411 PerformStackCheck(CAST(context));
412
413 // 1. Assert: IsPropertyKey(P) is true.
414 CSA_ASSERT(this, IsName(name));
415 CSA_ASSERT(this, Word32Equal(IsPrivateSymbol(name), Int32Constant(0)));
416
417 Label throw_proxy_handler_revoked(this, Label::kDeferred),
418 trap_undefined(this),
419 if_try_get_own_property_bailout(this, Label::kDeferred),
420 trap_not_callable(this, Label::kDeferred), return_true(this),
421 return_false(this), check_target_desc(this);
422
423 // 2. Let handler be O.[[ProxyHandler]].
424 Node* handler = LoadObjectField(proxy, JSProxy::kHandlerOffset);
425
426 // 3. If handler is null, throw a TypeError exception.
427 // 4. Assert: Type(handler) is Object.
428 GotoIfNot(IsJSReceiver(handler), &throw_proxy_handler_revoked);
429
430 // 5. Let target be O.[[ProxyTarget]].
431 Node* target = LoadObjectField(proxy, JSProxy::kTargetOffset);
432
433 // 6. Let trap be ? GetMethod(handler, "has").
434 // 7. If trap is undefined, then (see 7.a below).
435 Handle<Name> trap_name = factory()->has_string();
436 Node* trap = GetMethod(context, handler, trap_name, &trap_undefined);
437
438 GotoIf(TaggedIsSmi(trap), &trap_not_callable);
439 GotoIfNot(IsCallable(trap), &trap_not_callable);
440
441 // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, P
442 // »)).
443 BranchIfToBooleanIsTrue(CallJS(CodeFactory::Call(isolate()), context, trap,
444 handler, target, name),
445 &return_true, &check_target_desc);
446
447 BIND(&check_target_desc);
448 {
449 // 9. If booleanTrapResult is false, then (see 9.a. in CheckHasTrapResult).
450 CheckHasTrapResult(context, target, proxy, name, &return_false,
451 &if_try_get_own_property_bailout);
452 }
453
454 BIND(&if_try_get_own_property_bailout);
455 {
456 CallRuntime(Runtime::kCheckProxyHasTrap, context, name, target);
457 Return(FalseConstant());
458 }
459
460 BIND(&trap_undefined);
461 {
462 // 7.a. Return ? target.[[HasProperty]](P).
463 TailCallBuiltin(Builtins::kHasProperty, context, target, name);
464 }
465
466 BIND(&return_false);
467 Return(FalseConstant());
468
469 BIND(&return_true);
470 Return(TrueConstant());
471
472 BIND(&throw_proxy_handler_revoked);
473 ThrowTypeError(context, MessageTemplate::kProxyRevoked, "has");
474
475 BIND(&trap_not_callable);
476 ThrowTypeError(context, MessageTemplate::kPropertyNotFunction, trap,
477 StringConstant("has"), proxy);
478 }
479
TF_BUILTIN(ProxyGetProperty,ProxiesCodeStubAssembler)480 TF_BUILTIN(ProxyGetProperty, ProxiesCodeStubAssembler) {
481 Node* context = Parameter(Descriptor::kContext);
482 Node* proxy = Parameter(Descriptor::kProxy);
483 Node* name = Parameter(Descriptor::kName);
484 Node* receiver = Parameter(Descriptor::kReceiverValue);
485 Node* on_non_existent = Parameter(Descriptor::kOnNonExistent);
486
487 CSA_ASSERT(this, IsJSProxy(proxy));
488
489 // 1. Assert: IsPropertyKey(P) is true.
490 CSA_ASSERT(this, TaggedIsNotSmi(name));
491 CSA_ASSERT(this, IsName(name));
492 CSA_ASSERT(this, Word32Equal(IsPrivateSymbol(name), Int32Constant(0)));
493
494 Label throw_proxy_handler_revoked(this, Label::kDeferred),
495 trap_undefined(this);
496
497 // 2. Let handler be O.[[ProxyHandler]].
498 Node* handler = LoadObjectField(proxy, JSProxy::kHandlerOffset);
499
500 // 3. If handler is null, throw a TypeError exception.
501 GotoIf(IsNull(handler), &throw_proxy_handler_revoked);
502
503 // 4. Assert: Type(handler) is Object.
504 CSA_ASSERT(this, IsJSReceiver(handler));
505
506 // 5. Let target be O.[[ProxyTarget]].
507 Node* target = LoadObjectField(proxy, JSProxy::kTargetOffset);
508
509 // 6. Let trap be ? GetMethod(handler, "get").
510 // 7. If trap is undefined, then (see 7.a below).
511 Handle<Name> trap_name = factory()->get_string();
512 Node* trap = GetMethod(context, handler, trap_name, &trap_undefined);
513
514 // 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).
515 Node* trap_result = CallJS(
516 CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
517 context, trap, handler, target, name, receiver);
518
519 // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
520 Label return_result(this);
521 CheckGetSetTrapResult(context, target, proxy, name, trap_result,
522 &return_result, JSProxy::kGet);
523
524 BIND(&return_result);
525 {
526 // 11. Return trapResult.
527 Return(trap_result);
528 }
529
530 BIND(&trap_undefined);
531 {
532 // 7.a. Return ? target.[[Get]](P, Receiver).
533 // TODO(mslekova): Introduce GetPropertyWithReceiver stub
534 Return(CallRuntime(Runtime::kGetPropertyWithReceiver, context, target, name,
535 receiver, on_non_existent));
536 }
537
538 BIND(&throw_proxy_handler_revoked);
539 ThrowTypeError(context, MessageTemplate::kProxyRevoked, "get");
540 }
541
TF_BUILTIN(ProxySetProperty,ProxiesCodeStubAssembler)542 TF_BUILTIN(ProxySetProperty, ProxiesCodeStubAssembler) {
543 Node* context = Parameter(Descriptor::kContext);
544 Node* proxy = Parameter(Descriptor::kProxy);
545 Node* name = Parameter(Descriptor::kName);
546 Node* value = Parameter(Descriptor::kValue);
547 Node* receiver = Parameter(Descriptor::kReceiverValue);
548 TNode<Smi> language_mode = CAST(Parameter(Descriptor::kLanguageMode));
549
550 CSA_ASSERT(this, IsJSProxy(proxy));
551
552 // 1. Assert: IsPropertyKey(P) is true.
553 CSA_ASSERT(this, TaggedIsNotSmi(name));
554 CSA_ASSERT(this, IsName(name));
555
556 Label throw_proxy_handler_revoked(this, Label::kDeferred),
557 trap_undefined(this), failure(this, Label::kDeferred),
558 continue_checks(this), success(this),
559 private_symbol(this, Label::kDeferred);
560
561 GotoIf(IsPrivateSymbol(name), &private_symbol);
562
563 // 2. Let handler be O.[[ProxyHandler]].
564 Node* handler = LoadObjectField(proxy, JSProxy::kHandlerOffset);
565
566 // 3. If handler is null, throw a TypeError exception.
567 GotoIfNot(IsJSReceiver(handler), &throw_proxy_handler_revoked);
568
569 // 4. Assert: Type(handler) is Object.
570 CSA_ASSERT(this, IsJSReceiver(handler));
571
572 // 5. Let target be O.[[ProxyTarget]].
573 Node* target = LoadObjectField(proxy, JSProxy::kTargetOffset);
574
575 // 6. Let trap be ? GetMethod(handler, "set").
576 // 7. If trap is undefined, then (see 7.a below).
577 Handle<Name> set_string = factory()->set_string();
578 Node* trap = GetMethod(context, handler, set_string, &trap_undefined);
579
580 // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler,
581 // « target, P, V, Receiver »)).
582 // 9. If booleanTrapResult is false, return false.
583 BranchIfToBooleanIsTrue(
584 CallJS(CodeFactory::Call(isolate(),
585 ConvertReceiverMode::kNotNullOrUndefined),
586 context, trap, handler, target, name, value, receiver),
587 &continue_checks, &failure);
588
589 BIND(&continue_checks);
590 {
591 // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
592 Label return_result(this);
593 CheckGetSetTrapResult(context, target, proxy, name, value, &success,
594 JSProxy::kSet);
595 }
596
597 BIND(&failure);
598 {
599 Label if_throw(this, Label::kDeferred);
600 Branch(SmiEqual(language_mode, SmiConstant(LanguageMode::kStrict)),
601 &if_throw, &success);
602
603 BIND(&if_throw);
604 ThrowTypeError(context, MessageTemplate::kProxyTrapReturnedFalsishFor,
605 HeapConstant(set_string), name);
606 }
607
608 // 12. Return true.
609 BIND(&success);
610 Return(value);
611
612 BIND(&private_symbol);
613 {
614 Label failure(this), throw_error(this, Label::kDeferred);
615
616 Branch(SmiEqual(language_mode, SmiConstant(LanguageMode::kStrict)),
617 &throw_error, &failure);
618
619 BIND(&failure);
620 Return(UndefinedConstant());
621
622 BIND(&throw_error);
623 ThrowTypeError(context, MessageTemplate::kProxyPrivate);
624 }
625
626 BIND(&trap_undefined);
627 {
628 // 7.a. Return ? target.[[Set]](P, V, Receiver).
629 CallRuntime(Runtime::kSetPropertyWithReceiver, context, target, name, value,
630 receiver, language_mode);
631 Return(value);
632 }
633
634 BIND(&throw_proxy_handler_revoked);
635 ThrowTypeError(context, MessageTemplate::kProxyRevoked, "set");
636 }
637
CheckGetSetTrapResult(Node * context,Node * target,Node * proxy,Node * name,Node * trap_result,Label * check_passed,JSProxy::AccessKind access_kind)638 void ProxiesCodeStubAssembler::CheckGetSetTrapResult(
639 Node* context, Node* target, Node* proxy, Node* name, Node* trap_result,
640 Label* check_passed, JSProxy::AccessKind access_kind) {
641 Node* map = LoadMap(target);
642 VARIABLE(var_value, MachineRepresentation::kTagged);
643 VARIABLE(var_details, MachineRepresentation::kWord32);
644 VARIABLE(var_raw_value, MachineRepresentation::kTagged);
645
646 Label if_found_value(this), check_in_runtime(this, Label::kDeferred);
647
648 Node* instance_type = LoadInstanceType(target);
649 TryGetOwnProperty(context, target, target, map, instance_type, name,
650 &if_found_value, &var_value, &var_details, &var_raw_value,
651 check_passed, &check_in_runtime, kReturnAccessorPair);
652
653 BIND(&if_found_value);
654 {
655 Label throw_non_configurable_data(this, Label::kDeferred),
656 throw_non_configurable_accessor(this, Label::kDeferred),
657 check_accessor(this), check_data(this);
658
659 // If targetDesc is not undefined and targetDesc.[[Configurable]] is
660 // false, then:
661 GotoIfNot(IsSetWord32(var_details.value(),
662 PropertyDetails::kAttributesDontDeleteMask),
663 check_passed);
664
665 // If IsDataDescriptor(targetDesc) is true and
666 // targetDesc.[[Writable]] is false, then:
667 BranchIfAccessorPair(var_raw_value.value(), &check_accessor, &check_data);
668
669 BIND(&check_data);
670 {
671 Node* read_only = IsSetWord32(var_details.value(),
672 PropertyDetails::kAttributesReadOnlyMask);
673 GotoIfNot(read_only, check_passed);
674
675 // If SameValue(trapResult, targetDesc.[[Value]]) is false,
676 // throw a TypeError exception.
677 BranchIfSameValue(trap_result, var_value.value(), check_passed,
678 &throw_non_configurable_data);
679 }
680
681 BIND(&check_accessor);
682 {
683 Node* accessor_pair = var_raw_value.value();
684
685 if (access_kind == JSProxy::kGet) {
686 Label continue_check(this, Label::kDeferred);
687 // 10.b. If IsAccessorDescriptor(targetDesc) is true and
688 // targetDesc.[[Get]] is undefined, then:
689 Node* getter =
690 LoadObjectField(accessor_pair, AccessorPair::kGetterOffset);
691 // Here we check for null as well because if the getter was never
692 // defined it's set as null.
693 GotoIf(IsUndefined(getter), &continue_check);
694 GotoIf(IsNull(getter), &continue_check);
695 Goto(check_passed);
696
697 // 10.b.i. If trapResult is not undefined, throw a TypeError exception.
698 BIND(&continue_check);
699 GotoIfNot(IsUndefined(trap_result), &throw_non_configurable_accessor);
700 } else {
701 // 11.b.i. If targetDesc.[[Set]] is undefined, throw a TypeError
702 // exception.
703 Node* setter =
704 LoadObjectField(accessor_pair, AccessorPair::kSetterOffset);
705 GotoIf(IsUndefined(setter), &throw_non_configurable_accessor);
706 GotoIf(IsNull(setter), &throw_non_configurable_accessor);
707 }
708 Goto(check_passed);
709 }
710
711 BIND(&check_in_runtime);
712 {
713 CallRuntime(Runtime::kCheckProxyGetSetTrapResult, context, name, target,
714 trap_result, SmiConstant(access_kind));
715 Return(trap_result);
716 }
717
718 BIND(&throw_non_configurable_data);
719 {
720 if (access_kind == JSProxy::kGet) {
721 ThrowTypeError(context, MessageTemplate::kProxyGetNonConfigurableData,
722 name, var_value.value(), trap_result);
723 } else {
724 ThrowTypeError(context, MessageTemplate::kProxySetFrozenData, name);
725 }
726 }
727
728 BIND(&throw_non_configurable_accessor);
729 {
730 if (access_kind == JSProxy::kGet) {
731 ThrowTypeError(context,
732 MessageTemplate::kProxyGetNonConfigurableAccessor, name,
733 trap_result);
734 } else {
735 ThrowTypeError(context, MessageTemplate::kProxySetFrozenAccessor, name);
736 }
737 }
738 }
739 }
740
CheckHasTrapResult(Node * context,Node * target,Node * proxy,Node * name,Label * check_passed,Label * if_bailout)741 void ProxiesCodeStubAssembler::CheckHasTrapResult(Node* context, Node* target,
742 Node* proxy, Node* name,
743 Label* check_passed,
744 Label* if_bailout) {
745 Node* target_map = LoadMap(target);
746 VARIABLE(var_value, MachineRepresentation::kTagged);
747 VARIABLE(var_details, MachineRepresentation::kWord32);
748 VARIABLE(var_raw_value, MachineRepresentation::kTagged);
749
750 Label if_found_value(this, Label::kDeferred),
751 throw_non_configurable(this, Label::kDeferred),
752 throw_non_extensible(this, Label::kDeferred);
753
754 // 9.a. Let targetDesc be ? target.[[GetOwnProperty]](P).
755 Node* instance_type = LoadInstanceType(target);
756 TryGetOwnProperty(context, target, target, target_map, instance_type, name,
757 &if_found_value, &var_value, &var_details, &var_raw_value,
758 check_passed, if_bailout, kReturnAccessorPair);
759
760 // 9.b. If targetDesc is not undefined, then (see 9.b.i. below).
761 BIND(&if_found_value);
762 {
763 // 9.b.i. If targetDesc.[[Configurable]] is false, throw a TypeError
764 // exception.
765 Node* non_configurable = IsSetWord32(
766 var_details.value(), PropertyDetails::kAttributesDontDeleteMask);
767 GotoIf(non_configurable, &throw_non_configurable);
768
769 // 9.b.ii. Let extensibleTarget be ? IsExtensible(target).
770 Node* target_extensible = IsExtensibleMap(target_map);
771
772 // 9.b.iii. If extensibleTarget is false, throw a TypeError exception.
773 GotoIfNot(target_extensible, &throw_non_extensible);
774 Goto(check_passed);
775 }
776
777 BIND(&throw_non_configurable);
778 { ThrowTypeError(context, MessageTemplate::kProxyHasNonConfigurable, name); }
779
780 BIND(&throw_non_extensible);
781 { ThrowTypeError(context, MessageTemplate::kProxyHasNonExtensible, name); }
782 }
783
784 } // namespace internal
785 } // namespace v8
786