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), ¬_an_object);
185 GotoIfNot(IsJSReceiver(CAST(new_obj)), ¬_an_object);
186
187 // 10. Return newObj.
188 args.PopAndReturn(new_obj);
189
190 BIND(¬_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