diff options
Diffstat (limited to 'deps/v8/src/builtins/builtins-call-gen.cc')
-rw-r--r-- | deps/v8/src/builtins/builtins-call-gen.cc | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/deps/v8/src/builtins/builtins-call-gen.cc b/deps/v8/src/builtins/builtins-call-gen.cc index 24935bfbf4..2912c51136 100644 --- a/deps/v8/src/builtins/builtins-call-gen.cc +++ b/deps/v8/src/builtins/builtins-call-gen.cc @@ -9,12 +9,17 @@ #include "src/globals.h" #include "src/isolate.h" #include "src/macro-assembler.h" +#include "src/objects/api-callbacks.h" #include "src/objects/arguments.h" #include "src/objects/property-cell.h" +#include "src/objects/templates.h" namespace v8 { namespace internal { +template <typename T> +using TNode = compiler::TNode<T>; + void Builtins::Generate_CallFunction_ReceiverIsNullOrUndefined( MacroAssembler* masm) { Generate_CallFunction(masm, ConvertReceiverMode::kNullOrUndefined); @@ -373,5 +378,253 @@ TF_BUILTIN(CallWithSpread, CallOrConstructBuiltinsAssembler) { CallOrConstructWithSpread(target, new_target, spread, args_count, context); } +TNode<JSReceiver> CallOrConstructBuiltinsAssembler::GetCompatibleReceiver( + TNode<JSReceiver> receiver, TNode<HeapObject> signature, + TNode<Context> context) { + // Walk up the hidden prototype chain to find the compatible holder + // for the {signature}, starting with the {receiver} itself. + // + // Be careful, these loops are hand-tuned for (close to) ideal CSA + // code generation. Especially the sharing of the {var_template} + // below is intentional (even though it reads a bit funny in the + // first loop). + TVARIABLE(HeapObject, var_holder, receiver); + Label holder_loop(this, &var_holder), holder_found(this, &var_holder), + holder_next(this, Label::kDeferred); + Goto(&holder_loop); + BIND(&holder_loop); + { + // Find the template to compare against the {signature}. We don't + // bother checking that the template is a FunctionTemplateInfo here, + // but instead do that as part of the template loop below. The only + // thing we care about is that the template is actually a HeapObject. + TNode<HeapObject> holder = var_holder.value(); + TVARIABLE(HeapObject, var_template, LoadMap(holder)); + Label template_map_loop(this, &var_template), + template_loop(this, &var_template), + template_from_closure(this, &var_template); + Goto(&template_map_loop); + BIND(&template_map_loop); + { + // Load the constructor field from the current map (in the + // {var_template} variable), and see if that is a HeapObject. + // If it's a Smi then it is non-instance prototype on some + // initial map, which cannot be the case for API instances. + TNode<Object> constructor = LoadObjectField( + var_template.value(), Map::kConstructorOrBackPointerOffset); + GotoIf(TaggedIsSmi(constructor), &holder_next); + + // Now there are three cases for {constructor} that we care + // about here: + // + // 1. {constructor} is a JSFunction, and we can load the template + // from its SharedFunctionInfo::function_data field (which + // may not actually be a FunctionTemplateInfo). + // 2. {constructor} is a Map, in which case it's not a constructor + // but a back-pointer and we follow that. + // 3. {constructor} is a FunctionTemplateInfo (or some other + // HeapObject), in which case we can directly use that for + // the template loop below (non-FunctionTemplateInfo objects + // will be ruled out there). + // + var_template = CAST(constructor); + TNode<Int32T> template_type = LoadInstanceType(var_template.value()); + GotoIf(InstanceTypeEqual(template_type, JS_FUNCTION_TYPE), + &template_from_closure); + Branch(InstanceTypeEqual(template_type, MAP_TYPE), &template_map_loop, + &template_loop); + } + + BIND(&template_from_closure); + { + // The first case from above, where we load the template from the + // SharedFunctionInfo of the closure. We only check that the + // SharedFunctionInfo::function_data is a HeapObject and blindly + // use that as a template, since a non-FunctionTemplateInfo objects + // will be ruled out automatically by the template loop below. + TNode<SharedFunctionInfo> template_shared = + LoadObjectField<SharedFunctionInfo>( + var_template.value(), JSFunction::kSharedFunctionInfoOffset); + TNode<Object> template_data = LoadObjectField( + template_shared, SharedFunctionInfo::kFunctionDataOffset); + GotoIf(TaggedIsSmi(template_data), &holder_next); + var_template = CAST(template_data); + Goto(&template_loop); + } + + BIND(&template_loop); + { + // This loop compares the template to the expected {signature}, + // following the chain of parent templates until it hits the + // end, in which case we continue with the next holder (the + // hidden prototype) if there's any. + TNode<HeapObject> current = var_template.value(); + GotoIf(WordEqual(current, signature), &holder_found); + + GotoIfNot(IsFunctionTemplateInfoMap(LoadMap(current)), &holder_next); + + TNode<HeapObject> current_rare = LoadObjectField<HeapObject>( + current, FunctionTemplateInfo::kFunctionTemplateRareDataOffset); + GotoIf(IsUndefined(current_rare), &holder_next); + var_template = LoadObjectField<HeapObject>( + current_rare, FunctionTemplateRareData::kParentTemplateOffset); + Goto(&template_loop); + } + + BIND(&holder_next); + { + // Continue with the hidden prototype of the {holder} if it + // has one, or throw an illegal invocation exception, since + // the receiver did not pass the {signature} check. + TNode<Map> holder_map = LoadMap(holder); + var_holder = LoadMapPrototype(holder_map); + GotoIf(IsSetWord32(LoadMapBitField3(holder_map), + Map::HasHiddenPrototypeBit::kMask), + &holder_loop); + ThrowTypeError(context, MessageTemplate::kIllegalInvocation); + } + } + + BIND(&holder_found); + return CAST(var_holder.value()); +} + +// This calls an API callback by passing a {FunctionTemplateInfo}, +// does appropriate access and compatible receiver checks. +void CallOrConstructBuiltinsAssembler::CallFunctionTemplate( + CallFunctionTemplateMode mode, + TNode<FunctionTemplateInfo> function_template_info, TNode<IntPtrT> argc, + TNode<Context> context) { + CodeStubArguments args(this, argc); + Label throw_illegal_invocation(this, Label::kDeferred); + + // For API callbacks we need to call ToObject on the receiver. + // And in case the receiver is a JSObject already, we might + // need to perform access checks in the current {context}, + // depending on whether the "needs access check" bit is + // set on the receiver _and_ the {function_template_info} + // doesn't have the "accepts any receiver" bit set. + TVARIABLE(Object, var_receiver, args.GetReceiver()); + if (mode == CallFunctionTemplateMode::kCheckCompatibleReceiver) { + // We are only interested to see that receiver is compatible + // for the {function_template_info}, and don't need to bother + // doing any access checks. So ensure that the receiver is + // actually a JSReceiver. + var_receiver = ToObject_Inline(context, var_receiver.value()); + } else { + Label receiver_is_primitive(this, Label::kDeferred), + receiver_needs_access_check(this, &var_receiver, Label::kDeferred), + receiver_done(this); + + // Check if the receiver needs to be converted, or if it's already + // a JSReceiver, see if the "needs access check" bit is set _and_ + // the {function_template_info} doesn't just accept any receiver. + GotoIf(TaggedIsSmi(var_receiver.value()), &receiver_is_primitive); + TNode<Map> receiver_map = LoadMap(CAST(var_receiver.value())); + GotoIfNot(IsJSReceiverMap(receiver_map), &receiver_is_primitive); + GotoIfNot( + IsSetWord32<Map::IsAccessCheckNeededBit>(LoadMapBitField(receiver_map)), + &receiver_done); + TNode<WordT> function_template_info_flags = LoadAndUntagObjectField( + function_template_info, FunctionTemplateInfo::kFlagOffset); + Branch(IsSetWord(function_template_info_flags, + 1 << FunctionTemplateInfo::kAcceptAnyReceiver), + &receiver_done, &receiver_needs_access_check); + + BIND(&receiver_is_primitive); + { + // Convert primitives to wrapper objects as necessary. In case + // null or undefined were passed, we need to do the access check + // on the global proxy here. + var_receiver = ToObject(context, var_receiver.value()); + args.SetReceiver(var_receiver.value()); + GotoIfNot(IsSetWord32<Map::IsAccessCheckNeededBit>( + LoadMapBitField(LoadMap(CAST(var_receiver.value())))), + &receiver_done); + TNode<WordT> function_template_info_flags = LoadAndUntagObjectField( + function_template_info, FunctionTemplateInfo::kFlagOffset); + Branch(IsSetWord(function_template_info_flags, + 1 << FunctionTemplateInfo::kAcceptAnyReceiver), + &receiver_done, &receiver_needs_access_check); + } + + BIND(&receiver_needs_access_check); + { + CallRuntime(Runtime::kAccessCheck, context, var_receiver.value()); + Goto(&receiver_done); + } + + BIND(&receiver_done); + } + TNode<JSReceiver> receiver = CAST(var_receiver.value()); + + // Figure out the API holder for the {receiver} depending on the + // {mode} and the signature on the {function_template_info}. + TNode<JSReceiver> holder; + if (mode == CallFunctionTemplateMode::kCheckAccess) { + // We did the access check (including the ToObject) above, so + // {receiver} is a JSReceiver at this point, and we don't need + // to perform any "compatible receiver check", so {holder} is + // actually the {receiver}. + holder = receiver; + } else { + // If the {function_template_info} doesn't specify any signature, we + // just use the receiver as the holder for the API callback, otherwise + // we need to look for a compatible holder in the receiver's hidden + // prototype chain. + TNode<HeapObject> signature = LoadObjectField<HeapObject>( + function_template_info, FunctionTemplateInfo::kSignatureOffset); + holder = Select<JSReceiver>( + IsUndefined(signature), // -- + [&]() { return receiver; }, + [&]() { return GetCompatibleReceiver(receiver, signature, context); }); + } + + // Perform the actual API callback invocation via CallApiCallback. + TNode<CallHandlerInfo> call_handler_info = LoadObjectField<CallHandlerInfo>( + function_template_info, FunctionTemplateInfo::kCallCodeOffset); + TNode<Foreign> foreign = LoadObjectField<Foreign>( + call_handler_info, CallHandlerInfo::kJsCallbackOffset); + TNode<RawPtrT> callback = + LoadObjectField<RawPtrT>(foreign, Foreign::kForeignAddressOffset); + TNode<Object> call_data = + LoadObjectField<Object>(call_handler_info, CallHandlerInfo::kDataOffset); + TailCallStub(CodeFactory::CallApiCallback(isolate()), context, callback, argc, + call_data, holder); +} + +TF_BUILTIN(CallFunctionTemplate_CheckAccess, CallOrConstructBuiltinsAssembler) { + TNode<Context> context = CAST(Parameter(Descriptor::kContext)); + TNode<FunctionTemplateInfo> function_template_info = + CAST(Parameter(Descriptor::kFunctionTemplateInfo)); + TNode<IntPtrT> argc = + UncheckedCast<IntPtrT>(Parameter(Descriptor::kArgumentsCount)); + CallFunctionTemplate(CallFunctionTemplateMode::kCheckAccess, + function_template_info, argc, context); +} + +TF_BUILTIN(CallFunctionTemplate_CheckCompatibleReceiver, + CallOrConstructBuiltinsAssembler) { + TNode<Context> context = CAST(Parameter(Descriptor::kContext)); + TNode<FunctionTemplateInfo> function_template_info = + CAST(Parameter(Descriptor::kFunctionTemplateInfo)); + TNode<IntPtrT> argc = + UncheckedCast<IntPtrT>(Parameter(Descriptor::kArgumentsCount)); + CallFunctionTemplate(CallFunctionTemplateMode::kCheckCompatibleReceiver, + function_template_info, argc, context); +} + +TF_BUILTIN(CallFunctionTemplate_CheckAccessAndCompatibleReceiver, + CallOrConstructBuiltinsAssembler) { + TNode<Context> context = CAST(Parameter(Descriptor::kContext)); + TNode<FunctionTemplateInfo> function_template_info = + CAST(Parameter(Descriptor::kFunctionTemplateInfo)); + TNode<IntPtrT> argc = + UncheckedCast<IntPtrT>(Parameter(Descriptor::kArgumentsCount)); + CallFunctionTemplate( + CallFunctionTemplateMode::kCheckAccessAndCompatibleReceiver, + function_template_info, argc, context); +} + } // namespace internal } // namespace v8 |