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 10namespace v8 { 11namespace internal { 12 13class ShadowRealmBuiltinsAssembler : public CodeStubAssembler { 14 public: 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 25TNode<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 38void 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 49TF_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 154TF_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