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