1 // Copyright (c) 2012 The Chromium 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.
5 #include "ipc/ipc_message_utils.h"
7 #include "base/files/file_path.h"
8 #include "base/json/json_writer.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/nullable_string16.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/time/time.h"
14 #include "base/values.h"
15 #include "ipc/ipc_channel_handle.h"
16 #include "ipc/ipc_message_attachment_set.h"
26 const int kMaxRecursionDepth
= 100;
28 template<typename CharType
>
29 void LogBytes(const std::vector
<CharType
>& data
, std::string
* out
) {
31 // Windows has a GUI for logging, which can handle arbitrary binary data.
32 for (size_t i
= 0; i
< data
.size(); ++i
)
33 out
->push_back(data
[i
]);
35 // On POSIX, we log to stdout, which we assume can display ASCII.
36 static const size_t kMaxBytesToLog
= 100;
37 for (size_t i
= 0; i
< std::min(data
.size(), kMaxBytesToLog
); ++i
) {
39 out
->push_back(data
[i
]);
42 base::StringPrintf("[%02X]", static_cast<unsigned char>(data
[i
])));
44 if (data
.size() > kMaxBytesToLog
) {
45 out
->append(base::StringPrintf(
47 static_cast<unsigned>(data
.size() - kMaxBytesToLog
)));
52 bool ReadValue(const Message
* m
, PickleIterator
* iter
, base::Value
** value
,
55 void WriteValue(Message
* m
, const base::Value
* value
, int recursion
) {
57 if (recursion
> kMaxRecursionDepth
) {
58 LOG(WARNING
) << "Max recursion depth hit in WriteValue.";
62 m
->WriteInt(value
->GetType());
64 switch (value
->GetType()) {
65 case base::Value::TYPE_NULL
:
67 case base::Value::TYPE_BOOLEAN
: {
69 result
= value
->GetAsBoolean(&val
);
74 case base::Value::TYPE_INTEGER
: {
76 result
= value
->GetAsInteger(&val
);
81 case base::Value::TYPE_DOUBLE
: {
83 result
= value
->GetAsDouble(&val
);
88 case base::Value::TYPE_STRING
: {
90 result
= value
->GetAsString(&val
);
95 case base::Value::TYPE_BINARY
: {
96 const base::BinaryValue
* binary
=
97 static_cast<const base::BinaryValue
*>(value
);
98 m
->WriteData(binary
->GetBuffer(), static_cast<int>(binary
->GetSize()));
101 case base::Value::TYPE_DICTIONARY
: {
102 const base::DictionaryValue
* dict
=
103 static_cast<const base::DictionaryValue
*>(value
);
105 WriteParam(m
, static_cast<int>(dict
->size()));
107 for (base::DictionaryValue::Iterator
it(*dict
); !it
.IsAtEnd();
109 WriteParam(m
, it
.key());
110 WriteValue(m
, &it
.value(), recursion
+ 1);
114 case base::Value::TYPE_LIST
: {
115 const base::ListValue
* list
= static_cast<const base::ListValue
*>(value
);
116 WriteParam(m
, static_cast<int>(list
->GetSize()));
117 for (base::ListValue::const_iterator it
= list
->begin();
118 it
!= list
->end(); ++it
) {
119 WriteValue(m
, *it
, recursion
+ 1);
126 // Helper for ReadValue that reads a DictionaryValue into a pre-allocated
128 bool ReadDictionaryValue(const Message
* m
, PickleIterator
* iter
,
129 base::DictionaryValue
* value
, int recursion
) {
131 if (!ReadParam(m
, iter
, &size
))
134 for (int i
= 0; i
< size
; ++i
) {
137 if (!ReadParam(m
, iter
, &key
) ||
138 !ReadValue(m
, iter
, &subval
, recursion
+ 1))
140 value
->SetWithoutPathExpansion(key
, subval
);
146 // Helper for ReadValue that reads a ReadListValue into a pre-allocated
148 bool ReadListValue(const Message
* m
, PickleIterator
* iter
,
149 base::ListValue
* value
, int recursion
) {
151 if (!ReadParam(m
, iter
, &size
))
154 for (int i
= 0; i
< size
; ++i
) {
156 if (!ReadValue(m
, iter
, &subval
, recursion
+ 1))
158 value
->Set(i
, subval
);
164 bool ReadValue(const Message
* m
, PickleIterator
* iter
, base::Value
** value
,
166 if (recursion
> kMaxRecursionDepth
) {
167 LOG(WARNING
) << "Max recursion depth hit in ReadValue.";
172 if (!ReadParam(m
, iter
, &type
))
176 case base::Value::TYPE_NULL
:
177 *value
= base::Value::CreateNullValue();
179 case base::Value::TYPE_BOOLEAN
: {
181 if (!ReadParam(m
, iter
, &val
))
183 *value
= new base::FundamentalValue(val
);
186 case base::Value::TYPE_INTEGER
: {
188 if (!ReadParam(m
, iter
, &val
))
190 *value
= new base::FundamentalValue(val
);
193 case base::Value::TYPE_DOUBLE
: {
195 if (!ReadParam(m
, iter
, &val
))
197 *value
= new base::FundamentalValue(val
);
200 case base::Value::TYPE_STRING
: {
202 if (!ReadParam(m
, iter
, &val
))
204 *value
= new base::StringValue(val
);
207 case base::Value::TYPE_BINARY
: {
210 if (!iter
->ReadData(&data
, &length
))
212 *value
= base::BinaryValue::CreateWithCopiedBuffer(data
, length
);
215 case base::Value::TYPE_DICTIONARY
: {
216 scoped_ptr
<base::DictionaryValue
> val(new base::DictionaryValue());
217 if (!ReadDictionaryValue(m
, iter
, val
.get(), recursion
))
219 *value
= val
.release();
222 case base::Value::TYPE_LIST
: {
223 scoped_ptr
<base::ListValue
> val(new base::ListValue());
224 if (!ReadListValue(m
, iter
, val
.get(), recursion
))
226 *value
= val
.release();
238 // -----------------------------------------------------------------------------
248 LogData::~LogData() {
251 void ParamTraits
<bool>::Log(const param_type
& p
, std::string
* l
) {
252 l
->append(p
? "true" : "false");
255 void ParamTraits
<unsigned char>::Write(Message
* m
, const param_type
& p
) {
256 m
->WriteBytes(&p
, sizeof(param_type
));
259 bool ParamTraits
<unsigned char>::Read(const Message
* m
, PickleIterator
* iter
,
262 if (!iter
->ReadBytes(&data
, sizeof(param_type
)))
264 memcpy(r
, data
, sizeof(param_type
));
268 void ParamTraits
<unsigned char>::Log(const param_type
& p
, std::string
* l
) {
269 l
->append(base::UintToString(p
));
272 void ParamTraits
<unsigned short>::Write(Message
* m
, const param_type
& p
) {
273 m
->WriteBytes(&p
, sizeof(param_type
));
276 bool ParamTraits
<unsigned short>::Read(const Message
* m
, PickleIterator
* iter
,
279 if (!iter
->ReadBytes(&data
, sizeof(param_type
)))
281 memcpy(r
, data
, sizeof(param_type
));
285 void ParamTraits
<unsigned short>::Log(const param_type
& p
, std::string
* l
) {
286 l
->append(base::UintToString(p
));
289 void ParamTraits
<int>::Log(const param_type
& p
, std::string
* l
) {
290 l
->append(base::IntToString(p
));
293 void ParamTraits
<unsigned int>::Log(const param_type
& p
, std::string
* l
) {
294 l
->append(base::UintToString(p
));
297 void ParamTraits
<long>::Log(const param_type
& p
, std::string
* l
) {
298 l
->append(base::Int64ToString(static_cast<int64
>(p
)));
301 void ParamTraits
<unsigned long>::Log(const param_type
& p
, std::string
* l
) {
302 l
->append(base::Uint64ToString(static_cast<uint64
>(p
)));
305 void ParamTraits
<long long>::Log(const param_type
& p
, std::string
* l
) {
306 l
->append(base::Int64ToString(static_cast<int64
>(p
)));
309 void ParamTraits
<unsigned long long>::Log(const param_type
& p
, std::string
* l
) {
310 l
->append(base::Uint64ToString(p
));
313 void ParamTraits
<float>::Log(const param_type
& p
, std::string
* l
) {
314 l
->append(base::StringPrintf("%e", p
));
317 void ParamTraits
<double>::Write(Message
* m
, const param_type
& p
) {
318 m
->WriteBytes(reinterpret_cast<const char*>(&p
), sizeof(param_type
));
321 bool ParamTraits
<double>::Read(const Message
* m
, PickleIterator
* iter
,
324 if (!iter
->ReadBytes(&data
, sizeof(*r
))) {
328 memcpy(r
, data
, sizeof(param_type
));
332 void ParamTraits
<double>::Log(const param_type
& p
, std::string
* l
) {
333 l
->append(base::StringPrintf("%e", p
));
337 void ParamTraits
<std::string
>::Log(const param_type
& p
, std::string
* l
) {
341 void ParamTraits
<std::wstring
>::Log(const param_type
& p
, std::string
* l
) {
342 l
->append(base::WideToUTF8(p
));
345 #if !defined(WCHAR_T_IS_UTF16)
346 void ParamTraits
<base::string16
>::Log(const param_type
& p
, std::string
* l
) {
347 l
->append(base::UTF16ToUTF8(p
));
351 void ParamTraits
<std::vector
<char> >::Write(Message
* m
, const param_type
& p
) {
353 m
->WriteData(NULL
, 0);
355 m
->WriteData(&p
.front(), static_cast<int>(p
.size()));
359 bool ParamTraits
<std::vector
<char> >::Read(const Message
* m
,
360 PickleIterator
* iter
,
364 if (!iter
->ReadData(&data
, &data_size
) || data_size
< 0)
366 r
->resize(data_size
);
368 memcpy(&r
->front(), data
, data_size
);
372 void ParamTraits
<std::vector
<char> >::Log(const param_type
& p
, std::string
* l
) {
376 void ParamTraits
<std::vector
<unsigned char> >::Write(Message
* m
,
377 const param_type
& p
) {
379 m
->WriteData(NULL
, 0);
381 m
->WriteData(reinterpret_cast<const char*>(&p
.front()),
382 static_cast<int>(p
.size()));
386 bool ParamTraits
<std::vector
<unsigned char> >::Read(const Message
* m
,
387 PickleIterator
* iter
,
391 if (!iter
->ReadData(&data
, &data_size
) || data_size
< 0)
393 r
->resize(data_size
);
395 memcpy(&r
->front(), data
, data_size
);
399 void ParamTraits
<std::vector
<unsigned char> >::Log(const param_type
& p
,
404 void ParamTraits
<std::vector
<bool> >::Write(Message
* m
, const param_type
& p
) {
405 WriteParam(m
, static_cast<int>(p
.size()));
406 // Cast to bool below is required because libc++'s
407 // vector<bool>::const_reference is different from bool, and we want to avoid
408 // writing an extra specialization of ParamTraits for it.
409 for (size_t i
= 0; i
< p
.size(); i
++)
410 WriteParam(m
, static_cast<bool>(p
[i
]));
413 bool ParamTraits
<std::vector
<bool> >::Read(const Message
* m
,
414 PickleIterator
* iter
,
417 // ReadLength() checks for < 0 itself.
418 if (!iter
->ReadLength(&size
))
421 for (int i
= 0; i
< size
; i
++) {
423 if (!ReadParam(m
, iter
, &value
))
430 void ParamTraits
<std::vector
<bool> >::Log(const param_type
& p
, std::string
* l
) {
431 for (size_t i
= 0; i
< p
.size(); ++i
) {
434 LogParam(static_cast<bool>(p
[i
]), l
);
438 void ParamTraits
<base::DictionaryValue
>::Write(Message
* m
,
439 const param_type
& p
) {
440 WriteValue(m
, &p
, 0);
443 bool ParamTraits
<base::DictionaryValue
>::Read(
444 const Message
* m
, PickleIterator
* iter
, param_type
* r
) {
446 if (!ReadParam(m
, iter
, &type
) || type
!= base::Value::TYPE_DICTIONARY
)
449 return ReadDictionaryValue(m
, iter
, r
, 0);
452 void ParamTraits
<base::DictionaryValue
>::Log(const param_type
& p
,
455 base::JSONWriter::Write(&p
, &json
);
459 #if defined(OS_POSIX)
460 void ParamTraits
<base::FileDescriptor
>::Write(Message
* m
, const param_type
& p
) {
461 const bool valid
= p
.fd
>= 0;
462 WriteParam(m
, valid
);
468 if (!m
->WriteFile(base::ScopedFD(p
.fd
)))
471 if (!m
->WriteBorrowingFile(p
.fd
))
476 bool ParamTraits
<base::FileDescriptor
>::Read(const Message
* m
,
477 PickleIterator
* iter
,
479 *r
= base::FileDescriptor();
482 if (!ReadParam(m
, iter
, &valid
))
485 // TODO(morrita): Seems like this should return false.
490 if (!m
->ReadFile(iter
, &fd
))
493 *r
= base::FileDescriptor(fd
.release(), true);
497 void ParamTraits
<base::FileDescriptor
>::Log(const param_type
& p
,
500 l
->append(base::StringPrintf("FD(%d auto-close)", p
.fd
));
502 l
->append(base::StringPrintf("FD(%d)", p
.fd
));
505 #endif // defined(OS_POSIX)
507 void ParamTraits
<base::FilePath
>::Write(Message
* m
, const param_type
& p
) {
511 bool ParamTraits
<base::FilePath
>::Read(const Message
* m
,
512 PickleIterator
* iter
,
514 return r
->ReadFromPickle(iter
);
517 void ParamTraits
<base::FilePath
>::Log(const param_type
& p
, std::string
* l
) {
518 ParamTraits
<base::FilePath::StringType
>::Log(p
.value(), l
);
521 void ParamTraits
<base::ListValue
>::Write(Message
* m
, const param_type
& p
) {
522 WriteValue(m
, &p
, 0);
525 bool ParamTraits
<base::ListValue
>::Read(
526 const Message
* m
, PickleIterator
* iter
, param_type
* r
) {
528 if (!ReadParam(m
, iter
, &type
) || type
!= base::Value::TYPE_LIST
)
531 return ReadListValue(m
, iter
, r
, 0);
534 void ParamTraits
<base::ListValue
>::Log(const param_type
& p
, std::string
* l
) {
536 base::JSONWriter::Write(&p
, &json
);
540 void ParamTraits
<base::NullableString16
>::Write(Message
* m
,
541 const param_type
& p
) {
542 WriteParam(m
, p
.string());
543 WriteParam(m
, p
.is_null());
546 bool ParamTraits
<base::NullableString16
>::Read(const Message
* m
,
547 PickleIterator
* iter
,
549 base::string16 string
;
550 if (!ReadParam(m
, iter
, &string
))
553 if (!ReadParam(m
, iter
, &is_null
))
555 *r
= base::NullableString16(string
, is_null
);
559 void ParamTraits
<base::NullableString16
>::Log(const param_type
& p
,
562 LogParam(p
.string(), l
);
564 LogParam(p
.is_null(), l
);
568 void ParamTraits
<base::File::Info
>::Write(Message
* m
,
569 const param_type
& p
) {
570 WriteParam(m
, p
.size
);
571 WriteParam(m
, p
.is_directory
);
572 WriteParam(m
, p
.last_modified
.ToDoubleT());
573 WriteParam(m
, p
.last_accessed
.ToDoubleT());
574 WriteParam(m
, p
.creation_time
.ToDoubleT());
577 bool ParamTraits
<base::File::Info
>::Read(const Message
* m
,
578 PickleIterator
* iter
,
580 double last_modified
, last_accessed
, creation_time
;
581 if (!ReadParam(m
, iter
, &p
->size
) ||
582 !ReadParam(m
, iter
, &p
->is_directory
) ||
583 !ReadParam(m
, iter
, &last_modified
) ||
584 !ReadParam(m
, iter
, &last_accessed
) ||
585 !ReadParam(m
, iter
, &creation_time
))
587 p
->last_modified
= base::Time::FromDoubleT(last_modified
);
588 p
->last_accessed
= base::Time::FromDoubleT(last_accessed
);
589 p
->creation_time
= base::Time::FromDoubleT(creation_time
);
593 void ParamTraits
<base::File::Info
>::Log(const param_type
& p
,
598 LogParam(p
.is_directory
, l
);
600 LogParam(p
.last_modified
.ToDoubleT(), l
);
602 LogParam(p
.last_accessed
.ToDoubleT(), l
);
604 LogParam(p
.creation_time
.ToDoubleT(), l
);
608 void ParamTraits
<base::Time
>::Write(Message
* m
, const param_type
& p
) {
609 ParamTraits
<int64
>::Write(m
, p
.ToInternalValue());
612 bool ParamTraits
<base::Time
>::Read(const Message
* m
, PickleIterator
* iter
,
615 if (!ParamTraits
<int64
>::Read(m
, iter
, &value
))
617 *r
= base::Time::FromInternalValue(value
);
621 void ParamTraits
<base::Time
>::Log(const param_type
& p
, std::string
* l
) {
622 ParamTraits
<int64
>::Log(p
.ToInternalValue(), l
);
625 void ParamTraits
<base::TimeDelta
>::Write(Message
* m
, const param_type
& p
) {
626 ParamTraits
<int64
>::Write(m
, p
.ToInternalValue());
629 bool ParamTraits
<base::TimeDelta
>::Read(const Message
* m
,
630 PickleIterator
* iter
,
633 bool ret
= ParamTraits
<int64
>::Read(m
, iter
, &value
);
635 *r
= base::TimeDelta::FromInternalValue(value
);
640 void ParamTraits
<base::TimeDelta
>::Log(const param_type
& p
, std::string
* l
) {
641 ParamTraits
<int64
>::Log(p
.ToInternalValue(), l
);
644 void ParamTraits
<base::TimeTicks
>::Write(Message
* m
, const param_type
& p
) {
645 ParamTraits
<int64
>::Write(m
, p
.ToInternalValue());
648 bool ParamTraits
<base::TimeTicks
>::Read(const Message
* m
,
649 PickleIterator
* iter
,
652 bool ret
= ParamTraits
<int64
>::Read(m
, iter
, &value
);
654 *r
= base::TimeTicks::FromInternalValue(value
);
659 void ParamTraits
<base::TimeTicks
>::Log(const param_type
& p
, std::string
* l
) {
660 ParamTraits
<int64
>::Log(p
.ToInternalValue(), l
);
663 void ParamTraits
<IPC::ChannelHandle
>::Write(Message
* m
, const param_type
& p
) {
665 // On Windows marshalling pipe handle is not supported.
666 DCHECK(p
.pipe
.handle
== NULL
);
667 #endif // defined (OS_WIN)
668 WriteParam(m
, p
.name
);
669 #if defined(OS_POSIX)
670 WriteParam(m
, p
.socket
);
674 bool ParamTraits
<IPC::ChannelHandle
>::Read(const Message
* m
,
675 PickleIterator
* iter
,
677 return ReadParam(m
, iter
, &r
->name
)
678 #if defined(OS_POSIX)
679 && ReadParam(m
, iter
, &r
->socket
)
684 void ParamTraits
<IPC::ChannelHandle
>::Log(const param_type
& p
,
686 l
->append(base::StringPrintf("ChannelHandle(%s", p
.name
.c_str()));
687 #if defined(OS_POSIX)
689 ParamTraits
<base::FileDescriptor
>::Log(p
.socket
, l
);
694 void ParamTraits
<LogData
>::Write(Message
* m
, const param_type
& p
) {
695 WriteParam(m
, p
.channel
);
696 WriteParam(m
, p
.routing_id
);
697 WriteParam(m
, p
.type
);
698 WriteParam(m
, p
.flags
);
699 WriteParam(m
, p
.sent
);
700 WriteParam(m
, p
.receive
);
701 WriteParam(m
, p
.dispatch
);
702 WriteParam(m
, p
.message_name
);
703 WriteParam(m
, p
.params
);
706 bool ParamTraits
<LogData
>::Read(const Message
* m
,
707 PickleIterator
* iter
,
710 ReadParam(m
, iter
, &r
->channel
) &&
711 ReadParam(m
, iter
, &r
->routing_id
) &&
712 ReadParam(m
, iter
, &r
->type
) &&
713 ReadParam(m
, iter
, &r
->flags
) &&
714 ReadParam(m
, iter
, &r
->sent
) &&
715 ReadParam(m
, iter
, &r
->receive
) &&
716 ReadParam(m
, iter
, &r
->dispatch
) &&
717 ReadParam(m
, iter
, &r
->message_name
) &&
718 ReadParam(m
, iter
, &r
->params
);
721 void ParamTraits
<LogData
>::Log(const param_type
& p
, std::string
* l
) {
722 // Doesn't make sense to implement this!
725 void ParamTraits
<Message
>::Write(Message
* m
, const Message
& p
) {
726 #if defined(OS_POSIX)
727 // We don't serialize the file descriptors in the nested message, so there
728 // better not be any.
729 DCHECK(!p
.HasFileDescriptors());
732 // Don't just write out the message. This is used to send messages between
733 // NaCl (Posix environment) and the browser (could be on Windows). The message
734 // header formats differ between these systems (so does handle sharing, but
735 // we already asserted we don't have any handles). So just write out the
736 // parts of the header we use.
738 // Be careful also to use only explicitly-sized types. The NaCl environment
739 // could be 64-bit and the host browser could be 32-bits. The nested message
740 // may or may not be safe to send between 32-bit and 64-bit systems, but we
741 // leave that up to the code sending the message to ensure.
742 m
->WriteUInt32(static_cast<uint32
>(p
.routing_id()));
743 m
->WriteUInt32(p
.type());
744 m
->WriteUInt32(p
.flags());
745 m
->WriteData(p
.payload(), static_cast<uint32
>(p
.payload_size()));
748 bool ParamTraits
<Message
>::Read(const Message
* m
, PickleIterator
* iter
,
750 uint32 routing_id
, type
, flags
;
751 if (!iter
->ReadUInt32(&routing_id
) ||
752 !iter
->ReadUInt32(&type
) ||
753 !iter
->ReadUInt32(&flags
))
758 if (!iter
->ReadData(&payload
, &payload_size
))
761 r
->SetHeaderValues(static_cast<int32
>(routing_id
), type
, flags
);
762 return r
->WriteBytes(payload
, payload_size
);
765 void ParamTraits
<Message
>::Log(const Message
& p
, std::string
* l
) {
766 l
->append("<IPC::Message>");
770 // Note that HWNDs/HANDLE/HCURSOR/HACCEL etc are always 32 bits, even on 64
771 // bit systems. That's why we use the Windows macros to convert to 32 bits.
772 void ParamTraits
<HANDLE
>::Write(Message
* m
, const param_type
& p
) {
773 m
->WriteInt(HandleToLong(p
));
776 bool ParamTraits
<HANDLE
>::Read(const Message
* m
, PickleIterator
* iter
,
779 if (!iter
->ReadInt(&temp
))
781 *r
= LongToHandle(temp
);
785 void ParamTraits
<HANDLE
>::Log(const param_type
& p
, std::string
* l
) {
786 l
->append(base::StringPrintf("0x%X", p
));
789 void ParamTraits
<LOGFONT
>::Write(Message
* m
, const param_type
& p
) {
790 m
->WriteData(reinterpret_cast<const char*>(&p
), sizeof(LOGFONT
));
793 bool ParamTraits
<LOGFONT
>::Read(const Message
* m
, PickleIterator
* iter
,
797 if (iter
->ReadData(&data
, &data_size
) && data_size
== sizeof(LOGFONT
)) {
798 const LOGFONT
*font
= reinterpret_cast<LOGFONT
*>(const_cast<char*>(data
));
799 if (_tcsnlen(font
->lfFaceName
, LF_FACESIZE
) < LF_FACESIZE
) {
800 memcpy(r
, data
, sizeof(LOGFONT
));
809 void ParamTraits
<LOGFONT
>::Log(const param_type
& p
, std::string
* l
) {
810 l
->append(base::StringPrintf("<LOGFONT>"));
813 void ParamTraits
<MSG
>::Write(Message
* m
, const param_type
& p
) {
814 m
->WriteData(reinterpret_cast<const char*>(&p
), sizeof(MSG
));
817 bool ParamTraits
<MSG
>::Read(const Message
* m
, PickleIterator
* iter
,
821 bool result
= iter
->ReadData(&data
, &data_size
);
822 if (result
&& data_size
== sizeof(MSG
)) {
823 memcpy(r
, data
, sizeof(MSG
));
832 void ParamTraits
<MSG
>::Log(const param_type
& p
, std::string
* l
) {