• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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), &not_an_object);
380   GotoIfNot(IsJSReceiver(new_obj), &not_an_object);
381 
382   // 10. Return newObj.
383   args.PopAndReturn(new_obj);
384 
385   BIND(&not_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