• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 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-utils-gen.h"
6 #include "src/builtins/builtins.h"
7 #include "src/codegen/code-stub-assembler.h"
8 #include "src/objects/descriptor-array.h"
9 
10 namespace v8 {
11 namespace internal {
12 
13 class ShadowRealmBuiltinsAssembler : public CodeStubAssembler {
14  public:
ShadowRealmBuiltinsAssembler(compiler::CodeAssemblerState * state)15   explicit ShadowRealmBuiltinsAssembler(compiler::CodeAssemblerState* state)
16       : CodeStubAssembler(state) {}
17 
18  protected:
19   TNode<JSObject> AllocateJSWrappedFunction(TNode<Context> context,
20                                             TNode<Object> target);
21   void CheckAccessor(TNode<DescriptorArray> array, TNode<IntPtrT> index,
22                      TNode<Name> name, Label* bailout);
23 };
24 
AllocateJSWrappedFunction(TNode<Context> context,TNode<Object> target)25 TNode<JSObject> ShadowRealmBuiltinsAssembler::AllocateJSWrappedFunction(
26     TNode<Context> context, TNode<Object> target) {
27   TNode<NativeContext> native_context = LoadNativeContext(context);
28   TNode<Map> map = CAST(
29       LoadContextElement(native_context, Context::WRAPPED_FUNCTION_MAP_INDEX));
30   TNode<JSObject> wrapped = AllocateJSObjectFromMap(map);
31   StoreObjectFieldNoWriteBarrier(
32       wrapped, JSWrappedFunction::kWrappedTargetFunctionOffset, target);
33   StoreObjectFieldNoWriteBarrier(wrapped, JSWrappedFunction::kContextOffset,
34                                  context);
35   return wrapped;
36 }
37 
CheckAccessor(TNode<DescriptorArray> array,TNode<IntPtrT> index,TNode<Name> name,Label * bailout)38 void ShadowRealmBuiltinsAssembler::CheckAccessor(TNode<DescriptorArray> array,
39                                                  TNode<IntPtrT> index,
40                                                  TNode<Name> name,
41                                                  Label* bailout) {
42   TNode<Name> key = LoadKeyByDescriptorEntry(array, index);
43   GotoIfNot(TaggedEqual(key, name), bailout);
44   TNode<Object> value = LoadValueByDescriptorEntry(array, index);
45   GotoIfNot(IsAccessorInfo(CAST(value)), bailout);
46 }
47 
48 // https://tc39.es/proposal-shadowrealm/#sec-getwrappedvalue
TF_BUILTIN(ShadowRealmGetWrappedValue,ShadowRealmBuiltinsAssembler)49 TF_BUILTIN(ShadowRealmGetWrappedValue, ShadowRealmBuiltinsAssembler) {
50   auto context = Parameter<Context>(Descriptor::kContext);
51   auto creation_context = Parameter<Context>(Descriptor::kCreationContext);
52   auto target_context = Parameter<Context>(Descriptor::kTargetContext);
53   auto value = Parameter<Object>(Descriptor::kValue);
54 
55   Label if_primitive(this), if_callable(this), unwrap(this), wrap(this),
56       slow_wrap(this, Label::kDeferred), bailout(this, Label::kDeferred);
57 
58   // 2. Return value.
59   GotoIf(TaggedIsSmi(value), &if_primitive);
60   GotoIfNot(IsJSReceiver(CAST(value)), &if_primitive);
61 
62   // 1. If Type(value) is Object, then
63   // 1a. If IsCallable(value) is false, throw a TypeError exception.
64   // 1b. Return ? WrappedFunctionCreate(callerRealm, value).
65   Branch(IsCallable(CAST(value)), &if_callable, &bailout);
66 
67   BIND(&if_primitive);
68   Return(value);
69 
70   BIND(&if_callable);
71   TVARIABLE(Object, target);
72   target = value;
73   // WrappedFunctionCreate
74   // https://tc39.es/proposal-shadowrealm/#sec-wrappedfunctioncreate
75   Branch(IsJSWrappedFunction(CAST(value)), &unwrap, &wrap);
76 
77   BIND(&unwrap);
78   // The intermediate wrapped functions are not user-visible. And calling a
79   // wrapped function won't cause a side effect in the creation realm.
80   // Unwrap here to avoid nested unwrapping at the call site.
81   TNode<JSWrappedFunction> target_wrapped_function = CAST(value);
82   target = LoadObjectField(target_wrapped_function,
83                            JSWrappedFunction::kWrappedTargetFunctionOffset);
84   Goto(&wrap);
85 
86   BIND(&wrap);
87   // Disallow wrapping of slow-mode functions. We need to figure out
88   // whether the length and name property are in the original state.
89   TNode<Map> map = LoadMap(CAST(target.value()));
90   GotoIf(IsDictionaryMap(map), &slow_wrap);
91 
92   // Check whether the length and name properties are still present as
93   // AccessorInfo objects. If so, their value can be recomputed even if
94   // the actual value on the object changes.
95   TNode<Uint32T> bit_field3 = LoadMapBitField3(map);
96   TNode<IntPtrT> number_of_own_descriptors = Signed(
97       DecodeWordFromWord32<Map::Bits3::NumberOfOwnDescriptorsBits>(bit_field3));
98   GotoIf(IntPtrLessThan(
99              number_of_own_descriptors,
100              IntPtrConstant(JSFunction::kMinDescriptorsForFastBindAndWrap)),
101          &slow_wrap);
102 
103   // We don't need to check the exact accessor here because the only case
104   // custom accessor arise is with function templates via API, and in that
105   // case the object is in dictionary mode
106   TNode<DescriptorArray> descriptors = LoadMapInstanceDescriptors(map);
107   CheckAccessor(
108       descriptors,
109       IntPtrConstant(
110           JSFunctionOrBoundFunctionOrWrappedFunction::kLengthDescriptorIndex),
111       LengthStringConstant(), &slow_wrap);
112   CheckAccessor(
113       descriptors,
114       IntPtrConstant(
115           JSFunctionOrBoundFunctionOrWrappedFunction::kNameDescriptorIndex),
116       NameStringConstant(), &slow_wrap);
117 
118   // Verify that prototype matches the function prototype of the target
119   // context.
120   TNode<Object> prototype = LoadMapPrototype(map);
121   TNode<Object> function_map =
122       LoadContextElement(target_context, Context::WRAPPED_FUNCTION_MAP_INDEX);
123   TNode<Object> function_prototype = LoadMapPrototype(CAST(function_map));
124   GotoIf(TaggedNotEqual(prototype, function_prototype), &slow_wrap);
125 
126   // 1. Let internalSlotsList be the internal slots listed in Table 2, plus
127   // [[Prototype]] and [[Extensible]].
128   // 2. Let wrapped be ! MakeBasicObject(internalSlotsList).
129   // 3. Set wrapped.[[Prototype]] to
130   // callerRealm.[[Intrinsics]].[[%Function.prototype%]].
131   // 4. Set wrapped.[[Call]] as described in 2.1.
132   // 5. Set wrapped.[[WrappedTargetFunction]] to Target.
133   // 6. Set wrapped.[[Realm]] to callerRealm.
134   // 7. Let result be CopyNameAndLength(wrapped, Target, "wrapped").
135   // 8. If result is an Abrupt Completion, throw a TypeError exception.
136   // Installed with default accessors.
137   TNode<JSObject> wrapped =
138       AllocateJSWrappedFunction(creation_context, target.value());
139 
140   // 9. Return wrapped.
141   Return(wrapped);
142 
143   BIND(&slow_wrap);
144   {
145     Return(CallRuntime(Runtime::kShadowRealmWrappedFunctionCreate, context,
146                        creation_context, target.value()));
147   }
148 
149   BIND(&bailout);
150   ThrowTypeError(context, MessageTemplate::kNotCallable, value);
151 }
152 
153 // https://tc39.es/proposal-shadowrealm/#sec-wrapped-function-exotic-objects-call-thisargument-argumentslist
TF_BUILTIN(CallWrappedFunction,ShadowRealmBuiltinsAssembler)154 TF_BUILTIN(CallWrappedFunction, ShadowRealmBuiltinsAssembler) {
155   auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
156   TNode<IntPtrT> argc_ptr = ChangeInt32ToIntPtr(argc);
157   auto wrapped_function = Parameter<JSWrappedFunction>(Descriptor::kFunction);
158   auto context = Parameter<Context>(Descriptor::kContext);
159 
160   PerformStackCheck(context);
161 
162   Label call_exception(this, Label::kDeferred),
163       target_not_callable(this, Label::kDeferred);
164 
165   // 1. Let target be F.[[WrappedTargetFunction]].
166   TNode<JSReceiver> target = CAST(LoadObjectField(
167       wrapped_function, JSWrappedFunction::kWrappedTargetFunctionOffset));
168   // 2. Assert: IsCallable(target) is true.
169   CSA_DCHECK(this, IsCallable(target));
170 
171   // 4. Let callerRealm be ? GetFunctionRealm(F).
172   TNode<Context> caller_context = LoadObjectField<Context>(
173       wrapped_function, JSWrappedFunction::kContextOffset);
174   // 3. Let targetRealm be ? GetFunctionRealm(target).
175   TNode<Context> target_context =
176       GetFunctionRealm(caller_context, target, &target_not_callable);
177   // 5. NOTE: Any exception objects produced after this point are associated
178   // with callerRealm.
179 
180   CodeStubArguments args(this, argc_ptr);
181   TNode<Object> receiver = args.GetReceiver();
182 
183   // 6. Let wrappedArgs be a new empty List.
184   TNode<FixedArray> wrapped_args =
185       CAST(AllocateFixedArray(ElementsKind::PACKED_ELEMENTS, argc_ptr));
186   // Fill the fixed array so that heap verifier doesn't complain about it.
187   FillFixedArrayWithValue(ElementsKind::PACKED_ELEMENTS, wrapped_args,
188                           IntPtrConstant(0), argc_ptr,
189                           RootIndex::kUndefinedValue);
190 
191   // 8. Let wrappedThisArgument to ? GetWrappedValue(targetRealm, thisArgument).
192   // Create wrapped value in the target realm.
193   TNode<Object> wrapped_receiver =
194       CallBuiltin(Builtin::kShadowRealmGetWrappedValue, caller_context,
195                   target_context, caller_context, receiver);
196   StoreFixedArrayElement(wrapped_args, 0, wrapped_receiver);
197   // 7. For each element arg of argumentsList, do
198   BuildFastLoop<IntPtrT>(
199       IntPtrConstant(0), args.GetLengthWithoutReceiver(),
200       [&](TNode<IntPtrT> index) {
201         // 7a. Let wrappedValue be ? GetWrappedValue(targetRealm, arg).
202         // Create wrapped value in the target realm.
203         TNode<Object> wrapped_value =
204             CallBuiltin(Builtin::kShadowRealmGetWrappedValue, caller_context,
205                         target_context, caller_context, args.AtIndex(index));
206         // 7b. Append wrappedValue to wrappedArgs.
207         StoreFixedArrayElement(
208             wrapped_args, IntPtrAdd(index, IntPtrConstant(1)), wrapped_value);
209       },
210       1, IndexAdvanceMode::kPost);
211 
212   TVARIABLE(Object, var_exception);
213   TNode<Object> result;
214   {
215     compiler::ScopedExceptionHandler handler(this, &call_exception,
216                                              &var_exception);
217     TNode<Int32T> args_count = Int32Constant(0);  // args already on the stack
218     Callable callable = CodeFactory::CallVarargs(isolate());
219 
220     // 9. Let result be the Completion Record of Call(target,
221     // wrappedThisArgument, wrappedArgs).
222     result = CallStub(callable, target_context, target, args_count, argc,
223                       wrapped_args);
224   }
225 
226   // 10. If result.[[Type]] is normal or result.[[Type]] is return, then
227   // 10a. Return ? GetWrappedValue(callerRealm, result.[[Value]]).
228   TNode<Object> wrapped_result =
229       CallBuiltin(Builtin::kShadowRealmGetWrappedValue, caller_context,
230                   caller_context, target_context, result);
231   args.PopAndReturn(wrapped_result);
232 
233   // 11. Else,
234   BIND(&call_exception);
235   // 11a. Throw a TypeError exception.
236   // TODO(v8:11989): provide a non-observable inspection on the
237   // pending_exception to the newly created TypeError.
238   // https://github.com/tc39/proposal-shadowrealm/issues/353
239   ThrowTypeError(context, MessageTemplate::kCallShadowRealmFunctionThrown,
240                  var_exception.value());
241 
242   BIND(&target_not_callable);
243   // A wrapped value should not be non-callable.
244   Unreachable();
245 }
246 
247 }  // namespace internal
248 }  // namespace v8
249