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/logging/counters.h"
11 #include "src/objects/js-proxy.h"
12 #include "src/objects/objects-inl.h"
13 
14 #include "torque-generated/exported-macros-assembler-tq.h"
15 
16 namespace v8 {
17 namespace internal {
18 
AllocateProxy(TNode<Context> context,TNode<JSReceiver> target,TNode<JSReceiver> handler)19 TNode<JSProxy> ProxiesCodeStubAssembler::AllocateProxy(
20     TNode<Context> context, TNode<JSReceiver> target,
21     TNode<JSReceiver> handler) {
22   TVARIABLE(Map, map);
23 
24   Label callable_target(this), constructor_target(this), none_target(this),
25       create_proxy(this);
26 
27   TNode<NativeContext> nativeContext = LoadNativeContext(context);
28 
29   Branch(IsCallable(target), &callable_target, &none_target);
30 
31   BIND(&callable_target);
32   {
33     // Every object that is a constructor is implicitly callable
34     // so it's okay to nest this check here
35     GotoIf(IsConstructor(target), &constructor_target);
36     map = CAST(
37         LoadContextElement(nativeContext, Context::PROXY_CALLABLE_MAP_INDEX));
38     Goto(&create_proxy);
39   }
40   BIND(&constructor_target);
41   {
42     map = CAST(LoadContextElement(nativeContext,
43                                   Context::PROXY_CONSTRUCTOR_MAP_INDEX));
44     Goto(&create_proxy);
45   }
46   BIND(&none_target);
47   {
48     map = CAST(LoadContextElement(nativeContext, Context::PROXY_MAP_INDEX));
49     Goto(&create_proxy);
50   }
51 
52   BIND(&create_proxy);
53   TNode<HeapObject> proxy = Allocate(JSProxy::kSize);
54   StoreMapNoWriteBarrier(proxy, map.value());
55   StoreObjectFieldRoot(proxy, JSProxy::kPropertiesOrHashOffset,
56                        RootIndex::kEmptyPropertyDictionary);
57   StoreObjectFieldNoWriteBarrier(proxy, JSProxy::kTargetOffset, target);
58   StoreObjectFieldNoWriteBarrier(proxy, JSProxy::kHandlerOffset, handler);
59 
60   return CAST(proxy);
61 }
62 
CreateProxyRevokeFunctionContext(TNode<JSProxy> proxy,TNode<NativeContext> native_context)63 TNode<Context> ProxiesCodeStubAssembler::CreateProxyRevokeFunctionContext(
64     TNode<JSProxy> proxy, TNode<NativeContext> native_context) {
65   const TNode<Context> context =
66       AllocateSyntheticFunctionContext(native_context, kProxyContextLength);
67   StoreContextElementNoWriteBarrier(context, kProxySlot, proxy);
68   return context;
69 }
70 
AllocateProxyRevokeFunction(TNode<Context> context,TNode<JSProxy> proxy)71 TNode<JSFunction> ProxiesCodeStubAssembler::AllocateProxyRevokeFunction(
72     TNode<Context> context, TNode<JSProxy> proxy) {
73   const TNode<NativeContext> native_context = LoadNativeContext(context);
74 
75   const TNode<Context> proxy_context =
76       CreateProxyRevokeFunctionContext(proxy, native_context);
77   const TNode<Map> revoke_map = CAST(LoadContextElement(
78       native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
79   const TNode<SharedFunctionInfo> revoke_info = CAST(
80       LoadContextElement(native_context, Context::PROXY_REVOKE_SHARED_FUN));
81 
82   return AllocateFunctionWithMapAndContext(revoke_map, revoke_info,
83                                            proxy_context);
84 }
85 
TF_BUILTIN(CallProxy,ProxiesCodeStubAssembler)86 TF_BUILTIN(CallProxy, ProxiesCodeStubAssembler) {
87   TNode<Int32T> argc =
88       UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount));
89   TNode<IntPtrT> argc_ptr = ChangeInt32ToIntPtr(argc);
90   TNode<JSProxy> proxy = CAST(Parameter(Descriptor::kFunction));
91   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
92 
93   CSA_ASSERT(this, IsCallable(proxy));
94 
95   PerformStackCheck(context);
96 
97   Label throw_proxy_handler_revoked(this, Label::kDeferred),
98       trap_undefined(this);
99 
100   // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
101   TNode<HeapObject> handler =
102       CAST(LoadObjectField(proxy, JSProxy::kHandlerOffset));
103 
104   // 2. If handler is null, throw a TypeError exception.
105   CSA_ASSERT(this, IsNullOrJSReceiver(handler));
106   GotoIfNot(IsJSReceiver(handler), &throw_proxy_handler_revoked);
107 
108   // 3. Assert: Type(handler) is Object.
109   CSA_ASSERT(this, IsJSReceiver(handler));
110 
111   // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
112   TNode<Object> target = LoadObjectField(proxy, JSProxy::kTargetOffset);
113 
114   // 5. Let trap be ? GetMethod(handler, "apply").
115   // 6. If trap is undefined, then
116   Handle<Name> trap_name = factory()->apply_string();
117   TNode<Object> trap = GetMethod(context, handler, trap_name, &trap_undefined);
118 
119   CodeStubArguments args(this, argc_ptr);
120   TNode<Object> receiver = args.GetReceiver();
121 
122   // 7. Let argArray be CreateArrayFromList(argumentsList).
123   TNode<JSArray> array =
124       EmitFastNewAllArguments(UncheckedCast<Context>(context),
125                               UncheckedCast<RawPtrT>(LoadFramePointer()),
126                               UncheckedCast<IntPtrT>(argc_ptr));
127 
128   // 8. Return Call(trap, handler, «target, thisArgument, argArray»).
129   TNode<Object> result = Call(context, trap, handler, target, receiver, array);
130   args.PopAndReturn(result);
131 
132   BIND(&trap_undefined);
133   {
134     // 6.a. Return Call(target, thisArgument, argumentsList).
135     TailCallStub(CodeFactory::Call(isolate()), context, target, argc);
136   }
137 
138   BIND(&throw_proxy_handler_revoked);
139   { ThrowTypeError(context, MessageTemplate::kProxyRevoked, "apply"); }
140 }
141 
TF_BUILTIN(ConstructProxy,ProxiesCodeStubAssembler)142 TF_BUILTIN(ConstructProxy, ProxiesCodeStubAssembler) {
143   TNode<Int32T> argc =
144       UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount));
145   TNode<IntPtrT> argc_ptr = ChangeInt32ToIntPtr(argc);
146   TNode<JSProxy> proxy = CAST(Parameter(Descriptor::kTarget));
147   TNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget));
148   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
149 
150   CSA_ASSERT(this, IsCallable(proxy));
151 
152   Label throw_proxy_handler_revoked(this, Label::kDeferred),
153       trap_undefined(this), not_an_object(this, Label::kDeferred);
154 
155   // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
156   TNode<HeapObject> handler =
157       CAST(LoadObjectField(proxy, JSProxy::kHandlerOffset));
158 
159   // 2. If handler is null, throw a TypeError exception.
160   CSA_ASSERT(this, IsNullOrJSReceiver(handler));
161   GotoIfNot(IsJSReceiver(handler), &throw_proxy_handler_revoked);
162 
163   // 3. Assert: Type(handler) is Object.
164   CSA_ASSERT(this, IsJSReceiver(handler));
165 
166   // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
167   TNode<Object> target = LoadObjectField(proxy, JSProxy::kTargetOffset);
168 
169   // 5. Let trap be ? GetMethod(handler, "construct").
170   // 6. If trap is undefined, then
171   Handle<Name> trap_name = factory()->construct_string();
172   TNode<Object> trap = GetMethod(context, handler, trap_name, &trap_undefined);
173 
174   CodeStubArguments args(this, argc_ptr);
175 
176   // 7. Let argArray be CreateArrayFromList(argumentsList).
177   TNode<JSArray> array =
178       EmitFastNewAllArguments(UncheckedCast<Context>(context),
179                               UncheckedCast<RawPtrT>(LoadFramePointer()),
180                               UncheckedCast<IntPtrT>(argc_ptr));
181 
182   // 8. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »).
183   TNode<Object> new_obj =
184       Call(context, trap, handler, target, array, new_target);
185 
186   // 9. If Type(newObj) is not Object, throw a TypeError exception.
187   GotoIf(TaggedIsSmi(new_obj), &not_an_object);
188   GotoIfNot(IsJSReceiver(CAST(new_obj)), &not_an_object);
189 
190   // 10. Return newObj.
191   args.PopAndReturn(new_obj);
192 
193   BIND(&not_an_object);
194   {
195     ThrowTypeError(context, MessageTemplate::kProxyConstructNonObject, new_obj);
196   }
197 
198   BIND(&trap_undefined);
199   {
200     // 6.a. Assert: target has a [[Construct]] internal method.
201     CSA_ASSERT(this, IsConstructor(CAST(target)));
202 
203     // 6.b. Return ? Construct(target, argumentsList, newTarget).
204     TailCallStub(CodeFactory::Construct(isolate()), context, target, new_target,
205                  argc);
206   }
207 
208   BIND(&throw_proxy_handler_revoked);
209   { ThrowTypeError(context, MessageTemplate::kProxyRevoked, "construct"); }
210 }
211 
CheckGetSetTrapResult(TNode<Context> context,TNode<JSReceiver> target,TNode<JSProxy> proxy,TNode<Name> name,TNode<Object> trap_result,JSProxy::AccessKind access_kind)212 void ProxiesCodeStubAssembler::CheckGetSetTrapResult(
213     TNode<Context> context, TNode<JSReceiver> target, TNode<JSProxy> proxy,
214     TNode<Name> name, TNode<Object> trap_result,
215     JSProxy::AccessKind access_kind) {
216   // TODO(mslekova): Think of a better name for the trap_result param.
217   TNode<Map> map = LoadMap(target);
218   TVARIABLE(Object, var_value);
219   TVARIABLE(Uint32T, var_details);
220   TVARIABLE(Object, var_raw_value);
221 
222   Label if_found_value(this), check_in_runtime(this, Label::kDeferred),
223       check_passed(this);
224 
225   GotoIfNot(IsUniqueNameNoIndex(name), &check_in_runtime);
226   TNode<Uint16T> instance_type = LoadInstanceType(target);
227   TryGetOwnProperty(context, target, target, map, instance_type, name,
228                     &if_found_value, &var_value, &var_details, &var_raw_value,
229                     &check_passed, &check_in_runtime, kReturnAccessorPair);
230 
231   BIND(&if_found_value);
232   {
233     Label throw_non_configurable_data(this, Label::kDeferred),
234         throw_non_configurable_accessor(this, Label::kDeferred),
235         check_accessor(this), check_data(this);
236 
237     // If targetDesc is not undefined and targetDesc.[[Configurable]] is
238     // false, then:
239     GotoIfNot(IsSetWord32(var_details.value(),
240                           PropertyDetails::kAttributesDontDeleteMask),
241               &check_passed);
242 
243     // If IsDataDescriptor(targetDesc) is true and
244     // targetDesc.[[Writable]] is false, then:
245     BranchIfAccessorPair(var_raw_value.value(), &check_accessor, &check_data);
246 
247     BIND(&check_data);
248     {
249       TNode<BoolT> read_only = IsSetWord32(
250           var_details.value(), PropertyDetails::kAttributesReadOnlyMask);
251       GotoIfNot(read_only, &check_passed);
252 
253       // If SameValue(trapResult, targetDesc.[[Value]]) is false,
254       // throw a TypeError exception.
255       BranchIfSameValue(trap_result, var_value.value(), &check_passed,
256                         &throw_non_configurable_data);
257     }
258 
259     BIND(&check_accessor);
260     {
261       TNode<HeapObject> accessor_pair = CAST(var_raw_value.value());
262 
263       if (access_kind == JSProxy::kGet) {
264         Label continue_check(this, Label::kDeferred);
265         // 10.b. If IsAccessorDescriptor(targetDesc) is true and
266         // targetDesc.[[Get]] is undefined, then:
267         TNode<Object> getter =
268             LoadObjectField(accessor_pair, AccessorPair::kGetterOffset);
269         // Here we check for null as well because if the getter was never
270         // defined it's set as null.
271         GotoIf(IsUndefined(getter), &continue_check);
272         GotoIf(IsNull(getter), &continue_check);
273         Goto(&check_passed);
274 
275         // 10.b.i. If trapResult is not undefined, throw a TypeError exception.
276         BIND(&continue_check);
277         GotoIfNot(IsUndefined(trap_result), &throw_non_configurable_accessor);
278       } else {
279         // 11.b.i. If targetDesc.[[Set]] is undefined, throw a TypeError
280         // exception.
281         TNode<Object> setter =
282             LoadObjectField(accessor_pair, AccessorPair::kSetterOffset);
283         GotoIf(IsUndefined(setter), &throw_non_configurable_accessor);
284         GotoIf(IsNull(setter), &throw_non_configurable_accessor);
285       }
286       Goto(&check_passed);
287     }
288 
289     BIND(&throw_non_configurable_data);
290     {
291       if (access_kind == JSProxy::kGet) {
292         ThrowTypeError(context, MessageTemplate::kProxyGetNonConfigurableData,
293                        name, var_value.value(), trap_result);
294       } else {
295         ThrowTypeError(context, MessageTemplate::kProxySetFrozenData, name);
296       }
297     }
298 
299     BIND(&throw_non_configurable_accessor);
300     {
301       if (access_kind == JSProxy::kGet) {
302         ThrowTypeError(context,
303                        MessageTemplate::kProxyGetNonConfigurableAccessor, name,
304                        trap_result);
305       } else {
306         ThrowTypeError(context, MessageTemplate::kProxySetFrozenAccessor, name);
307       }
308     }
309 
310     BIND(&check_in_runtime);
311     {
312       CallRuntime(Runtime::kCheckProxyGetSetTrapResult, context, name, target,
313                   trap_result, SmiConstant(access_kind));
314       Goto(&check_passed);
315     }
316 
317     BIND(&check_passed);
318   }
319 }
320 
CheckHasTrapResult(TNode<Context> context,TNode<JSReceiver> target,TNode<JSProxy> proxy,TNode<Name> name)321 void ProxiesCodeStubAssembler::CheckHasTrapResult(TNode<Context> context,
322                                                   TNode<JSReceiver> target,
323                                                   TNode<JSProxy> proxy,
324                                                   TNode<Name> name) {
325   TNode<Map> target_map = LoadMap(target);
326   TVARIABLE(Object, var_value);
327   TVARIABLE(Uint32T, var_details);
328   TVARIABLE(Object, var_raw_value);
329 
330   Label if_found_value(this, Label::kDeferred),
331       throw_non_configurable(this, Label::kDeferred),
332       throw_non_extensible(this, Label::kDeferred), check_passed(this),
333       check_in_runtime(this, Label::kDeferred);
334 
335   // 9.a. Let targetDesc be ? target.[[GetOwnProperty]](P).
336   GotoIfNot(IsUniqueNameNoIndex(name), &check_in_runtime);
337   TNode<Uint16T> instance_type = LoadInstanceType(target);
338   TryGetOwnProperty(context, target, target, target_map, instance_type, name,
339                     &if_found_value, &var_value, &var_details, &var_raw_value,
340                     &check_passed, &check_in_runtime, kReturnAccessorPair);
341 
342   // 9.b. If targetDesc is not undefined, then (see 9.b.i. below).
343   BIND(&if_found_value);
344   {
345     // 9.b.i. If targetDesc.[[Configurable]] is false, throw a TypeError
346     // exception.
347     TNode<BoolT> non_configurable = IsSetWord32(
348         var_details.value(), PropertyDetails::kAttributesDontDeleteMask);
349     GotoIf(non_configurable, &throw_non_configurable);
350 
351     // 9.b.ii. Let extensibleTarget be ? IsExtensible(target).
352     TNode<BoolT> target_extensible = IsExtensibleMap(target_map);
353 
354     // 9.b.iii. If extensibleTarget is false, throw a TypeError exception.
355     GotoIfNot(target_extensible, &throw_non_extensible);
356     Goto(&check_passed);
357   }
358 
359   BIND(&throw_non_configurable);
360   { ThrowTypeError(context, MessageTemplate::kProxyHasNonConfigurable, name); }
361 
362   BIND(&throw_non_extensible);
363   { ThrowTypeError(context, MessageTemplate::kProxyHasNonExtensible, name); }
364 
365   BIND(&check_in_runtime);
366   {
367     CallRuntime(Runtime::kCheckProxyHasTrapResult, context, name, target);
368     Goto(&check_passed);
369   }
370 
371   BIND(&check_passed);
372 }
373 
CheckDeleteTrapResult(TNode<Context> context,TNode<JSReceiver> target,TNode<JSProxy> proxy,TNode<Name> name)374 void ProxiesCodeStubAssembler::CheckDeleteTrapResult(TNode<Context> context,
375                                                      TNode<JSReceiver> target,
376                                                      TNode<JSProxy> proxy,
377                                                      TNode<Name> name) {
378   TNode<Map> target_map = LoadMap(target);
379   TVARIABLE(Object, var_value);
380   TVARIABLE(Uint32T, var_details);
381   TVARIABLE(Object, var_raw_value);
382 
383   Label if_found_value(this, Label::kDeferred),
384       throw_non_configurable(this, Label::kDeferred),
385       throw_non_extensible(this, Label::kDeferred), check_passed(this),
386       check_in_runtime(this, Label::kDeferred);
387 
388   // 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
389   GotoIfNot(IsUniqueNameNoIndex(name), &check_in_runtime);
390   TNode<Uint16T> instance_type = LoadInstanceType(target);
391   TryGetOwnProperty(context, target, target, target_map, instance_type, name,
392                     &if_found_value, &var_value, &var_details, &var_raw_value,
393                     &check_passed, &check_in_runtime, kReturnAccessorPair);
394 
395   // 11. If targetDesc is undefined, return true.
396   BIND(&if_found_value);
397   {
398     // 12. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
399     TNode<BoolT> non_configurable = IsSetWord32(
400         var_details.value(), PropertyDetails::kAttributesDontDeleteMask);
401     GotoIf(non_configurable, &throw_non_configurable);
402 
403     // 13. Let extensibleTarget be ? IsExtensible(target).
404     TNode<BoolT> target_extensible = IsExtensibleMap(target_map);
405 
406     // 14. If extensibleTarget is false, throw a TypeError exception.
407     GotoIfNot(target_extensible, &throw_non_extensible);
408     Goto(&check_passed);
409   }
410 
411   BIND(&throw_non_configurable);
412   {
413     ThrowTypeError(context,
414                    MessageTemplate::kProxyDeletePropertyNonConfigurable, name);
415   }
416 
417   BIND(&throw_non_extensible);
418   {
419     ThrowTypeError(context, MessageTemplate::kProxyDeletePropertyNonExtensible,
420                    name);
421   }
422 
423   BIND(&check_in_runtime);
424   {
425     CallRuntime(Runtime::kCheckProxyDeleteTrapResult, context, name, target);
426     Goto(&check_passed);
427   }
428 
429   BIND(&check_passed);
430 }
431 
432 }  // namespace internal
433 }  // namespace v8
434