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