| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 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. | |
| 4 | |
| 5 use bindings::decoding::{Decoder, ValidationError}; | |
| 6 use bindings::encoding; | |
| 7 use bindings::encoding::{Bits, Encoder, Context, DATA_HEADER_SIZE, DataHeader, D
ataHeaderValue}; | |
| 8 use bindings::message::MessageHeader; | |
| 9 | |
| 10 use std::cmp::Eq; | |
| 11 use std::collections::HashMap; | |
| 12 use std::hash::Hash; | |
| 13 use std::mem; | |
| 14 use std::panic; | |
| 15 use std::ptr; | |
| 16 use std::vec::Vec; | |
| 17 | |
| 18 use system::{MojoResult, CastHandle, Handle, UntypedHandle}; | |
| 19 use system::data_pipe; | |
| 20 use system::message_pipe; | |
| 21 use system::shared_buffer; | |
| 22 use system::wait_set; | |
| 23 | |
| 24 /// The size of a Mojom map plus header in bytes. | |
| 25 const MAP_SIZE: usize = 24; | |
| 26 | |
| 27 /// The sorted set of versions for a map. | |
| 28 const MAP_VERSIONS: [(u32, u32); 1] = [(0, MAP_SIZE as u32)]; | |
| 29 | |
| 30 /// The size of a Mojom union in bytes (header included). | |
| 31 pub const UNION_SIZE: usize = 16; | |
| 32 | |
| 33 /// The size of a Mojom pointer in bits. | |
| 34 pub const POINTER_BIT_SIZE: Bits = Bits(64); | |
| 35 | |
| 36 /// The value of a Mojom null pointer. | |
| 37 pub const MOJOM_NULL_POINTER: u64 = 0; | |
| 38 | |
| 39 /// An enumeration of all the possible low-level Mojom types. | |
| 40 pub enum MojomType { | |
| 41 Simple, | |
| 42 Pointer, | |
| 43 Union, | |
| 44 Handle, | |
| 45 Interface, | |
| 46 } | |
| 47 | |
| 48 /// Whatever implements this trait can be serialized in the Mojom format. | |
| 49 pub trait MojomEncodable: Sized { | |
| 50 /// Get the Mojom type. | |
| 51 fn mojom_type() -> MojomType; | |
| 52 | |
| 53 /// Get this type's Mojom alignment. | |
| 54 fn mojom_alignment() -> usize; | |
| 55 | |
| 56 /// The amount of space in bits the type takes up when inlined | |
| 57 /// into another type at serialization time. | |
| 58 fn embed_size(context: &Context) -> Bits; | |
| 59 | |
| 60 /// Recursively computes the size of the complete Mojom archive | |
| 61 /// starting from this type. | |
| 62 fn compute_size(&self, context: Context) -> usize; | |
| 63 | |
| 64 /// Encodes this type into the encoder given a context. | |
| 65 fn encode(self, encoder: &mut Encoder, context: Context); | |
| 66 | |
| 67 /// Using a decoder, decodes itself out of a byte buffer. | |
| 68 fn decode(decoder: &mut Decoder, context: Context) -> Result<Self, Validatio
nError>; | |
| 69 } | |
| 70 | |
| 71 /// Whatever implements this trait is a Mojom pointer type which means | |
| 72 /// that on encode, a pointer is inlined and the implementer is | |
| 73 /// serialized elsewhere in the output buffer. | |
| 74 pub trait MojomPointer: MojomEncodable { | |
| 75 /// Get the DataHeader meta-data for this pointer type. | |
| 76 fn header_data(&self) -> DataHeaderValue; | |
| 77 | |
| 78 /// Get the size of only this type when serialized. | |
| 79 fn serialized_size(&self, context: &Context) -> usize; | |
| 80 | |
| 81 /// Encodes the actual values of the type into the encoder. | |
| 82 fn encode_value(self, encoder: &mut Encoder, context: Context); | |
| 83 | |
| 84 /// Decodes the actual values of the type into the decoder. | |
| 85 fn decode_value(decoder: &mut Decoder, context: Context) -> Result<Self, Val
idationError>; | |
| 86 | |
| 87 /// Writes a pointer inlined into the current context before calling | |
| 88 /// encode_value. | |
| 89 fn encode_new(self, encoder: &mut Encoder, context: Context) { | |
| 90 let data_size = self.serialized_size(&context); | |
| 91 let data_header = DataHeader::new(data_size, self.header_data()); | |
| 92 let new_context = encoder.add(&data_header).unwrap(); | |
| 93 self.encode_value(encoder, new_context); | |
| 94 } | |
| 95 | |
| 96 /// Reads a pointer inlined into the current context before calling | |
| 97 /// decode_value. | |
| 98 fn decode_new(decoder: &mut Decoder, _context: Context, pointer: u64) -> Res
ult<Self, ValidationError> { | |
| 99 match decoder.claim(pointer as usize) { | |
| 100 Ok(new_context) => Self::decode_value(decoder, new_context), | |
| 101 Err(err) => Err(err), | |
| 102 } | |
| 103 } | |
| 104 } | |
| 105 | |
| 106 /// Whatever implements this trait is a Mojom union type which means that | |
| 107 /// on encode it is inlined, but if the union is nested inside of another | |
| 108 /// union type, it is treated as a pointer type. | |
| 109 pub trait MojomUnion: MojomEncodable { | |
| 110 /// Get the union's current tag. | |
| 111 fn get_tag(&self) -> u32; | |
| 112 | |
| 113 /// Encode the actual value of the union. | |
| 114 fn encode_value(self, encoder: &mut Encoder, context: Context); | |
| 115 | |
| 116 /// Decode the actual value of the union. | |
| 117 fn decode_value(decoder: &mut Decoder, context: Context) -> Result<Self, Val
idationError>; | |
| 118 | |
| 119 /// The embed_size for when the union acts as a pointer type. | |
| 120 fn nested_embed_size() -> Bits { | |
| 121 POINTER_BIT_SIZE | |
| 122 } | |
| 123 | |
| 124 /// The encoding routine for when the union acts as a pointer type. | |
| 125 fn nested_encode(self, encoder: &mut Encoder, context: Context) { | |
| 126 let loc = encoder.size() as u64; | |
| 127 { | |
| 128 let state = encoder.get_mut(&context); | |
| 129 state.encode_pointer(loc); | |
| 130 } | |
| 131 let tag = DataHeaderValue::UnionTag(self.get_tag()); | |
| 132 let data_header = DataHeader::new(UNION_SIZE, tag); | |
| 133 let new_context = encoder.add(&data_header).unwrap(); | |
| 134 self.encode_value(encoder, new_context.set_is_union(true)); | |
| 135 } | |
| 136 | |
| 137 /// The decoding routine for when the union acts as a pointer type. | |
| 138 fn nested_decode(decoder: &mut Decoder, context: Context) -> Result<Self, Va
lidationError> { | |
| 139 let global_offset = { | |
| 140 let state = decoder.get_mut(&context); | |
| 141 match state.decode_pointer() { | |
| 142 Some(ptr) => ptr as usize, | |
| 143 None => return Err(ValidationError::IllegalPointer), | |
| 144 } | |
| 145 }; | |
| 146 if global_offset == (MOJOM_NULL_POINTER as usize) { | |
| 147 return Err(ValidationError::UnexpectedNullPointer); | |
| 148 } | |
| 149 match decoder.claim(global_offset as usize) { | |
| 150 Ok(new_context) => Self::decode_value(decoder, new_context), | |
| 151 Err(err) => Err(err), | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 /// The embed_size for when the union is inlined into the current context. | |
| 156 fn inline_embed_size() -> Bits { | |
| 157 Bits(8 * (UNION_SIZE as usize)) | |
| 158 } | |
| 159 | |
| 160 /// The encoding routine for when the union is inlined into the current cont
ext. | |
| 161 fn inline_encode(self, encoder: &mut Encoder, context: Context) { | |
| 162 { | |
| 163 let mut state = encoder.get_mut(&context); | |
| 164 state.align_to_bytes(8); | |
| 165 state.encode(UNION_SIZE as u32); | |
| 166 state.encode(self.get_tag()); | |
| 167 } | |
| 168 self.encode_value(encoder, context.clone()); | |
| 169 { | |
| 170 let mut state = encoder.get_mut(&context); | |
| 171 state.align_to_bytes(8); | |
| 172 state.align_to_byte(); | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 /// The decoding routine for when the union is inlined into the current cont
ext. | |
| 177 fn inline_decode(decoder: &mut Decoder, context: Context) -> Result<Self, Va
lidationError> { | |
| 178 { | |
| 179 let mut state = decoder.get_mut(&context); | |
| 180 state.align_to_byte(); | |
| 181 state.align_to_bytes(8); | |
| 182 } | |
| 183 let value = Self::decode_value(decoder, context.clone()); | |
| 184 { | |
| 185 let mut state = decoder.get_mut(&context); | |
| 186 state.align_to_byte(); | |
| 187 state.align_to_bytes(8); | |
| 188 } | |
| 189 value | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 /// A marker trait that marks Mojo handles as encodable. | |
| 194 pub trait MojomHandle: CastHandle + MojomEncodable {} | |
| 195 | |
| 196 /// Whatever implements this trait is considered to be a Mojom | |
| 197 /// interface, that is, a message pipe which conforms to some | |
| 198 /// messaging interface. | |
| 199 /// | |
| 200 /// We force an underlying message pipe to be used via the pipe() | |
| 201 /// and unwrap() routines. | |
| 202 pub trait MojomInterface: MojomEncodable { | |
| 203 /// Get the service name for this interface. | |
| 204 fn service_name() -> &'static str; | |
| 205 | |
| 206 /// Get the version for this interface. | |
| 207 fn version(&self) -> u32; | |
| 208 | |
| 209 /// Access the underlying message pipe for this interface. | |
| 210 fn pipe(&self) -> &message_pipe::MessageEndpoint; | |
| 211 | |
| 212 /// Unwrap the interface into its underlying message pipe. | |
| 213 fn unwrap(self) -> message_pipe::MessageEndpoint; | |
| 214 } | |
| 215 | |
| 216 /// An error that may occur when sending data over a Mojom interface. | |
| 217 #[derive(Debug)] | |
| 218 pub enum MojomSendError { | |
| 219 /// Failed to write to the underlying message pipe. | |
| 220 FailedWrite(MojoResult), | |
| 221 | |
| 222 /// The version is too old to write the attempted message. | |
| 223 OldVersion(u32, u32), | |
| 224 } | |
| 225 | |
| 226 /// Whatever implements this trait is considered to be a Mojom | |
| 227 /// interface that may send messages of some generic type. | |
| 228 /// | |
| 229 /// When implementing this trait, the correct way is to specify | |
| 230 /// a tighter trait bound than MojomMessage that limits the types | |
| 231 /// available for sending to those that are valid messages available | |
| 232 /// to the interface. | |
| 233 /// | |
| 234 /// TODO(mknyszek): Add sending control messages | |
| 235 pub trait MojomInterfaceSend<R: MojomMessage>: MojomInterface { | |
| 236 /// Creates a message. | |
| 237 fn create_request(&self, req_id: u64, payload: R) -> (Vec<u8>, Vec<UntypedHa
ndle>) { | |
| 238 let mut header = R::create_header(); | |
| 239 header.request_id = req_id; | |
| 240 let header_size = header.compute_size(Default::default()); | |
| 241 let size = header_size + payload.compute_size(Default::default()); | |
| 242 let mut buffer: Vec<u8> = Vec::with_capacity(size); | |
| 243 buffer.resize(size, 0); | |
| 244 let handles = { | |
| 245 let (header_buf, rest_buf) = buffer.split_at_mut(header_size); | |
| 246 let mut handles = header.serialize(header_buf); | |
| 247 handles.extend(payload.serialize(rest_buf).into_iter()); | |
| 248 handles | |
| 249 }; | |
| 250 (buffer, handles) | |
| 251 } | |
| 252 | |
| 253 /// Creates and sends a message, and returns its request ID. | |
| 254 fn send_request(&self, req_id: u64, payload: R) -> Result<(), MojomSendError
> { | |
| 255 if self.version() < R::min_version() { | |
| 256 return Err(MojomSendError::OldVersion(self.version(), R::min_version
())); | |
| 257 } | |
| 258 let (buffer, handles) = self.create_request(req_id, payload); | |
| 259 match self.pipe().write(&buffer, handles, mpflags!(Write::None)) { | |
| 260 MojoResult::Okay => Ok(()), | |
| 261 err => Err(MojomSendError::FailedWrite(err)), | |
| 262 } | |
| 263 } | |
| 264 } | |
| 265 | |
| 266 /// An error that may occur when attempting to recieve a message over a | |
| 267 /// Mojom interface. | |
| 268 #[derive(Debug)] | |
| 269 pub enum MojomRecvError { | |
| 270 /// Failed to read from the underlying message pipe. | |
| 271 FailedRead(MojoResult), | |
| 272 | |
| 273 /// Failed to validate the buffer during decode. | |
| 274 FailedValidation(ValidationError), | |
| 275 } | |
| 276 | |
| 277 /// Whatever implements this trait is considered to be a Mojom | |
| 278 /// interface that may recieve messages for some interface. | |
| 279 /// | |
| 280 /// When implementing this trait, specify the container "union" type | |
| 281 /// which can contain any of the potential messages that may be recieved. | |
| 282 /// This way, we can return that type and let the user multiplex over | |
| 283 /// what message was received. | |
| 284 /// | |
| 285 /// TODO(mknyszek): Add responding to control messages | |
| 286 pub trait MojomInterfaceRecv: MojomInterface { | |
| 287 type Container: MojomMessageOption; | |
| 288 | |
| 289 /// Tries to read a message from a pipe and decodes it. | |
| 290 fn recv_response(&self) -> Result<(u64, Self::Container), MojomRecvError> { | |
| 291 match self.pipe().read(mpflags!(Read::None)) { | |
| 292 Ok((buffer, handles)) => { | |
| 293 match Self::Container::decode_message(buffer, handles) { | |
| 294 Ok((req_id, val)) => Ok((req_id, val)), | |
| 295 Err(err) => Err(MojomRecvError::FailedValidation(err)), | |
| 296 } | |
| 297 }, | |
| 298 Err(err) => Err(MojomRecvError::FailedRead(err)), | |
| 299 } | |
| 300 } | |
| 301 } | |
| 302 | |
| 303 /// Whatever implements this trait is considered to be a Mojom struct. | |
| 304 /// | |
| 305 /// Mojom structs are always the root of any Mojom message. Thus, we | |
| 306 /// provide convenience functions for serialization here. | |
| 307 pub trait MojomStruct: MojomPointer { | |
| 308 /// Given a pre-allocated buffer, the struct serializes itself. | |
| 309 fn serialize(self, buffer: &mut [u8]) -> Vec<UntypedHandle> { | |
| 310 let mut encoder = Encoder::new(buffer); | |
| 311 self.encode_new(&mut encoder, Default::default()); | |
| 312 encoder.unwrap() | |
| 313 } | |
| 314 | |
| 315 /// The struct computes its own size, allocates a buffer, and then | |
| 316 /// serializes itself into that buffer. | |
| 317 fn auto_serialize(self) -> (Vec<u8>, Vec<UntypedHandle>) { | |
| 318 let size = self.compute_size(Default::default()); | |
| 319 let mut buf = Vec::with_capacity(size); | |
| 320 buf.resize(size, 0); | |
| 321 let handles = self.serialize(&mut buf); | |
| 322 (buf, handles) | |
| 323 } | |
| 324 | |
| 325 /// Decode the type from a byte array and a set of handles. | |
| 326 fn deserialize(buffer: &[u8], handles: Vec<UntypedHandle>) -> Result<Self, V
alidationError> { | |
| 327 let mut decoder = Decoder::new(buffer, handles); | |
| 328 Self::decode_new(&mut decoder, Default::default(), 0) | |
| 329 } | |
| 330 } | |
| 331 | |
| 332 /// Marks a MojomStruct as being capable of being sent across some | |
| 333 /// Mojom interface. | |
| 334 pub trait MojomMessage: MojomStruct { | |
| 335 fn min_version() -> u32; | |
| 336 fn create_header() -> MessageHeader; | |
| 337 } | |
| 338 | |
| 339 /// The trait for a "container" type intended to be used in MojomInterfaceRecv. | |
| 340 /// | |
| 341 /// This trait contains the decode logic which decodes based on the message head
er | |
| 342 /// and returns itself: a union type which may contain any of the possible messa
ges | |
| 343 /// that may be sent across this interface. | |
| 344 pub trait MojomMessageOption: Sized { | |
| 345 /// Decodes the actual payload of the message. | |
| 346 /// | |
| 347 /// Implemented by a code generator. | |
| 348 fn decode_payload(header: MessageHeader, buffer: &[u8], handles: Vec<Untyped
Handle>) -> Result<Self, ValidationError>; | |
| 349 | |
| 350 /// Decodes the message header and then the payload, returning a new | |
| 351 /// copy of itself and the request ID found in the header. | |
| 352 fn decode_message(buffer: Vec<u8>, handles: Vec<UntypedHandle>) -> Result<(u
64, Self), ValidationError> { | |
| 353 let header = try!(MessageHeader::deserialize(&buffer[..], Vec::new())); | |
| 354 let payload_buffer = &buffer[header.serialized_size(&Default::default())
..]; | |
| 355 let req_id = header.request_id; | |
| 356 let ret = try!(Self::decode_payload(header, payload_buffer, handles)); | |
| 357 Ok((req_id, ret)) | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 // ********************************************** // | |
| 362 // ****** IMPLEMENTATIONS FOR COMMON TYPES ****** // | |
| 363 // ********************************************** // | |
| 364 | |
| 365 macro_rules! impl_encodable_for_prim { | |
| 366 ($($prim_type:ty),*) => { | |
| 367 $( | |
| 368 impl MojomEncodable for $prim_type { | |
| 369 fn mojom_type() -> MojomType { | |
| 370 MojomType::Simple | |
| 371 } | |
| 372 fn mojom_alignment() -> usize { | |
| 373 mem::size_of::<$prim_type>() | |
| 374 } | |
| 375 fn embed_size(_context: &Context) -> Bits { | |
| 376 Bits(8 * mem::size_of::<$prim_type>()) | |
| 377 } | |
| 378 fn compute_size(&self, _context: Context) -> usize { | |
| 379 0 // Indicates that this type is inlined and it adds nothing ext
ernal to the size | |
| 380 } | |
| 381 fn encode(self, encoder: &mut Encoder, context: Context) { | |
| 382 let mut state = encoder.get_mut(&context); | |
| 383 state.encode(self); | |
| 384 } | |
| 385 fn decode(decoder: &mut Decoder, context: Context) -> Result<Self, V
alidationError> { | |
| 386 let mut state = decoder.get_mut(&context); | |
| 387 Ok(state.decode::<Self>()) | |
| 388 } | |
| 389 } | |
| 390 )* | |
| 391 } | |
| 392 } | |
| 393 | |
| 394 impl_encodable_for_prim!(i8, i16, i32, i64, u8, u16, u32, u64, f32, f64); | |
| 395 | |
| 396 impl MojomEncodable for bool { | |
| 397 fn mojom_alignment() -> usize { | |
| 398 panic!("Should never check_decode mojom_alignment of bools (they're bit-
aligned)!"); | |
| 399 } | |
| 400 fn mojom_type() -> MojomType { | |
| 401 MojomType::Simple | |
| 402 } | |
| 403 fn embed_size(_context: &Context) -> Bits { | |
| 404 Bits(1) | |
| 405 } | |
| 406 fn compute_size(&self, _context: Context) -> usize { | |
| 407 0 // Indicates that this type is inlined and it adds nothing external to
the size | |
| 408 } | |
| 409 fn encode(self, encoder: &mut Encoder, context: Context) { | |
| 410 let mut state = encoder.get_mut(&context); | |
| 411 state.encode_bool(self); | |
| 412 } | |
| 413 fn decode(decoder: &mut Decoder, context: Context) -> Result<Self, Validatio
nError> { | |
| 414 let mut state = decoder.get_mut(&context); | |
| 415 Ok(state.decode_bool()) | |
| 416 } | |
| 417 } | |
| 418 | |
| 419 // Options should be considered to represent nullability the Mojom IDL. | |
| 420 // Any type wrapped in an Option type is nullable. | |
| 421 | |
| 422 impl<T: MojomEncodable> MojomEncodable for Option<T> { | |
| 423 fn mojom_alignment() -> usize { | |
| 424 T::mojom_alignment() | |
| 425 } | |
| 426 fn mojom_type() -> MojomType { | |
| 427 T::mojom_type() | |
| 428 } | |
| 429 fn embed_size(context: &Context) -> Bits { | |
| 430 T::embed_size(context) | |
| 431 } | |
| 432 fn compute_size(&self, context: Context) -> usize { | |
| 433 match *self { | |
| 434 Some(ref value) => value.compute_size(context), | |
| 435 None => 0, | |
| 436 } | |
| 437 } | |
| 438 fn encode(self, encoder: &mut Encoder, context: Context) { | |
| 439 match self { | |
| 440 Some(value) => value.encode(encoder, context), | |
| 441 None => { | |
| 442 let mut state = encoder.get_mut(&context); | |
| 443 match T::mojom_type() { | |
| 444 MojomType::Pointer => state.encode_null_pointer(), | |
| 445 MojomType::Union => state.encode_null_union(), | |
| 446 MojomType::Handle => state.encode_null_handle(), | |
| 447 MojomType::Interface => { | |
| 448 state.encode_null_handle(); | |
| 449 state.encode(0 as u32); | |
| 450 }, | |
| 451 MojomType::Simple => panic!("Unexpected simple type in Optio
n!"), | |
| 452 } | |
| 453 }, | |
| 454 } | |
| 455 } | |
| 456 fn decode(decoder: &mut Decoder, context: Context) -> Result<Self, Validatio
nError> { | |
| 457 let skipped = { | |
| 458 let mut state = decoder.get_mut(&context); | |
| 459 match T::mojom_type() { | |
| 460 MojomType::Pointer => state.skip_if_null_pointer(), | |
| 461 MojomType::Union => state.skip_if_null_union(), | |
| 462 MojomType::Handle => state.skip_if_null_handle(), | |
| 463 MojomType::Interface => state.skip_if_null_interface(), | |
| 464 MojomType::Simple => panic!("Unexpected simple type in Option!")
, | |
| 465 } | |
| 466 }; | |
| 467 if skipped { | |
| 468 Ok(None) | |
| 469 } else { | |
| 470 match T::decode(decoder, context) { | |
| 471 Ok(value) => Ok(Some(value)), | |
| 472 Err(err) => Err(err), | |
| 473 } | |
| 474 } | |
| 475 } | |
| 476 } | |
| 477 | |
| 478 macro_rules! impl_pointer_for_array { | |
| 479 () => { | |
| 480 fn header_data(&self) -> DataHeaderValue { | |
| 481 DataHeaderValue::Elements(self.len() as u32) | |
| 482 } | |
| 483 fn serialized_size(&self, context: &Context) -> usize { | |
| 484 DATA_HEADER_SIZE + if self.len() > 0 { | |
| 485 (T::embed_size(context) * self.len()).as_bytes() | |
| 486 } else { | |
| 487 0 | |
| 488 } | |
| 489 } | |
| 490 } | |
| 491 } | |
| 492 | |
| 493 macro_rules! impl_encodable_for_array { | |
| 494 () => { | |
| 495 impl_encodable_for_pointer!(); | |
| 496 fn compute_size(&self, context: Context) -> usize { | |
| 497 let mut size = encoding::align_default(self.serialized_size(&context
)); | |
| 498 for elem in self.iter() { | |
| 499 size += elem.compute_size(context.clone()); | |
| 500 } | |
| 501 size | |
| 502 } | |
| 503 } | |
| 504 } | |
| 505 | |
| 506 impl<T: MojomEncodable> MojomPointer for Vec<T> { | |
| 507 impl_pointer_for_array!(); | |
| 508 fn encode_value(self, encoder: &mut Encoder, context: Context) { | |
| 509 for elem in self.into_iter() { | |
| 510 elem.encode(encoder, context.clone()); | |
| 511 } | |
| 512 } | |
| 513 fn decode_value(decoder: &mut Decoder, context: Context) -> Result<Vec<T>, V
alidationError> { | |
| 514 let elems = { | |
| 515 let mut state = decoder.get_mut(&context); | |
| 516 match state.decode_array_header::<T>() { | |
| 517 Ok(header) => header.data(), | |
| 518 Err(err) => return Err(err), | |
| 519 } | |
| 520 }; | |
| 521 let mut value = Vec::with_capacity(elems as usize); | |
| 522 for _ in 0..elems { | |
| 523 match T::decode(decoder, context.clone()) { | |
| 524 Ok(elem) => value.push(elem), | |
| 525 Err(err) => return Err(err), | |
| 526 } | |
| 527 } | |
| 528 Ok(value) | |
| 529 } | |
| 530 } | |
| 531 | |
| 532 impl<T: MojomEncodable> MojomEncodable for Vec<T> { | |
| 533 impl_encodable_for_array!(); | |
| 534 } | |
| 535 | |
| 536 macro_rules! impl_encodable_for_fixed_array { | |
| 537 ($($len:expr),*) => { | |
| 538 $( | |
| 539 impl<T: MojomEncodable> MojomPointer for [T; $len] { | |
| 540 impl_pointer_for_array!(); | |
| 541 fn encode_value(mut self, encoder: &mut Encoder, context: Context) { | |
| 542 let mut panic_error = None; | |
| 543 let mut moves = 0; | |
| 544 unsafe { | |
| 545 // In order to move elements out of an array we need to repl
ace the | |
| 546 // value with uninitialized memory. | |
| 547 for elem in self.iter_mut() { | |
| 548 let owned_elem = mem::replace(elem, mem::uninitialized()
); | |
| 549 // We need to handle if an unwinding panic happens to pr
event use of | |
| 550 // uninitialized memory... | |
| 551 let next_context = context.clone(); | |
| 552 // We assert everything going into this closure is unwin
d safe. If anything | |
| 553 // is added, PLEASE make sure it is also unwind safe... | |
| 554 let result = panic::catch_unwind(panic::AssertUnwindSafe
(|| { | |
| 555 owned_elem.encode(encoder, next_context); | |
| 556 })); | |
| 557 if let Err(err) = result { | |
| 558 panic_error = Some(err); | |
| 559 break; | |
| 560 } | |
| 561 moves += 1; | |
| 562 } | |
| 563 if let Some(err) = panic_error { | |
| 564 for i in moves..self.len() { | |
| 565 ptr::drop_in_place(&mut self[i] as *mut T); | |
| 566 } | |
| 567 // Forget the array to prevent a drop | |
| 568 mem::forget(self); | |
| 569 // Continue unwinding | |
| 570 panic::resume_unwind(err); | |
| 571 } | |
| 572 // We cannot risk drop() getting run on the array values, so
we just | |
| 573 // forget self. | |
| 574 mem::forget(self); | |
| 575 } | |
| 576 } | |
| 577 fn decode_value(decoder: &mut Decoder, context: Context) -> Result<[
T; $len], ValidationError> { | |
| 578 let elems = { | |
| 579 let mut state = decoder.get_mut(&context); | |
| 580 match state.decode_array_header::<T>() { | |
| 581 Ok(header) => header.data(), | |
| 582 Err(err) => return Err(err), | |
| 583 } | |
| 584 }; | |
| 585 if elems != $len { | |
| 586 return Err(ValidationError::UnexpectedArrayHeader); | |
| 587 } | |
| 588 let mut array: [T; $len]; | |
| 589 let mut panic_error = None; | |
| 590 let mut inits = 0; | |
| 591 let mut error = None; | |
| 592 unsafe { | |
| 593 // Since we don't force Clone to be implemented on Mojom typ
es | |
| 594 // (mainly due to handles) we need to create this array as u
ninitialized | |
| 595 // and initialize it manually. | |
| 596 array = mem::uninitialized(); | |
| 597 for elem in &mut array[..] { | |
| 598 // When a panic unwinds it may try to read and drop unin
itialized | |
| 599 // memory, so we need to catch this. However, we pass mu
table state! | |
| 600 // This could be bad as we could observe a broken invari
ant inside | |
| 601 // of decoder and access it as usual, but we do NOT acce
ss decoder | |
| 602 // here, nor do we ever unwind through one of decoder's
methods. | |
| 603 // Therefore, it should be safe to assert that decoder i
s unwind safe. | |
| 604 let next_context = context.clone(); | |
| 605 // We assert everything going into this closure is unwin
d safe. If anything | |
| 606 // is added, PLEASE make sure it is also unwind safe... | |
| 607 let result = panic::catch_unwind(panic::AssertUnwindSafe
(|| { | |
| 608 T::decode(decoder, next_context) | |
| 609 })); | |
| 610 match result { | |
| 611 Ok(non_panic_value) => match non_panic_value { | |
| 612 Ok(value) => ptr::write(elem, value), | |
| 613 Err(err) => { | |
| 614 error = Some(err); | |
| 615 break; | |
| 616 }, | |
| 617 }, | |
| 618 Err(err) => { | |
| 619 panic_error = Some(err); | |
| 620 break; | |
| 621 }, | |
| 622 } | |
| 623 inits += 1; | |
| 624 } | |
| 625 if panic_error.is_some() || error.is_some() { | |
| 626 // Drop everything that was initialized | |
| 627 for i in 0..inits { | |
| 628 ptr::drop_in_place(&mut array[i] as *mut T); | |
| 629 } | |
| 630 // Forget the array to prevent a drop | |
| 631 mem::forget(array); | |
| 632 if let Some(err) = panic_error { | |
| 633 panic::resume_unwind(err); | |
| 634 } | |
| 635 return Err(error.take().expect("Corrupted stack?")); | |
| 636 } | |
| 637 } | |
| 638 Ok(array) | |
| 639 } | |
| 640 } | |
| 641 impl<T: MojomEncodable> MojomEncodable for [T; $len] { | |
| 642 impl_encodable_for_array!(); | |
| 643 } | |
| 644 )* | |
| 645 } | |
| 646 } | |
| 647 | |
| 648 // Unfortunately, we cannot be generic over the length of a fixed array | |
| 649 // even though its part of the type (this will hopefully be added in the | |
| 650 // future) so for now we implement encodable for only the first 33 fixed | |
| 651 // size array types. | |
| 652 impl_encodable_for_fixed_array!( 0, 1, 2, 3, 4, 5, 6, 7, | |
| 653 8, 9, 10, 11, 12, 13, 14, 15, | |
| 654 16, 17, 18, 19, 20, 21, 22, 23, | |
| 655 24, 25, 26, 27, 28, 29, 30, 31, | |
| 656 32); | |
| 657 | |
| 658 impl<T: MojomEncodable> MojomPointer for Box<[T]> { | |
| 659 impl_pointer_for_array!(); | |
| 660 fn encode_value(self, encoder: &mut Encoder, context: Context) { | |
| 661 for elem in self.into_vec().into_iter() { | |
| 662 elem.encode(encoder, context.clone()); | |
| 663 } | |
| 664 } | |
| 665 fn decode_value(decoder: &mut Decoder, context: Context) -> Result<Box<[T]>,
ValidationError> { | |
| 666 match Vec::<T>::decode_value(decoder, context) { | |
| 667 Ok(vec) => Ok(vec.into_boxed_slice()), | |
| 668 Err(err) => Err(err), | |
| 669 } | |
| 670 } | |
| 671 } | |
| 672 | |
| 673 impl<T: MojomEncodable> MojomEncodable for Box<[T]> { | |
| 674 impl_encodable_for_array!(); | |
| 675 } | |
| 676 | |
| 677 // We can represent a Mojom string as just a Rust String type | |
| 678 // since both are UTF-8. | |
| 679 impl MojomPointer for String { | |
| 680 fn header_data(&self) -> DataHeaderValue { | |
| 681 DataHeaderValue::Elements(self.len() as u32) | |
| 682 } | |
| 683 fn serialized_size(&self, _context: &Context) -> usize { | |
| 684 DATA_HEADER_SIZE + self.len() | |
| 685 } | |
| 686 fn encode_value(self, encoder: &mut Encoder, context: Context) { | |
| 687 for byte in self.as_bytes() { | |
| 688 byte.encode(encoder, context.clone()); | |
| 689 } | |
| 690 } | |
| 691 fn decode_value(decoder: &mut Decoder, context: Context) -> Result<String, V
alidationError> { | |
| 692 let mut state = decoder.get_mut(&context); | |
| 693 let elems = match state.decode_array_header::<u8>() { | |
| 694 Ok(header) => header.data(), | |
| 695 Err(err) => return Err(err), | |
| 696 }; | |
| 697 let mut value = Vec::with_capacity(elems as usize); | |
| 698 for _ in 0..elems { | |
| 699 value.push(state.decode::<u8>()); | |
| 700 } | |
| 701 match String::from_utf8(value) { | |
| 702 Ok(string) => Ok(string), | |
| 703 Err(err) => panic!("Error decoding String: {}", err), | |
| 704 } | |
| 705 } | |
| 706 } | |
| 707 | |
| 708 impl MojomEncodable for String { | |
| 709 impl_encodable_for_pointer!(); | |
| 710 fn compute_size(&self, context: Context) -> usize { | |
| 711 encoding::align_default(self.serialized_size(&context)) | |
| 712 } | |
| 713 } | |
| 714 | |
| 715 /// Helper function to clean up duplicate code in HashMap. | |
| 716 fn array_claim_and_decode_header<T: MojomEncodable>(decoder: &mut Decoder, offse
t: usize) -> Result<(Context, usize), ValidationError> { | |
| 717 let context = match decoder.claim(offset) { | |
| 718 Ok(new_context) => new_context, | |
| 719 Err(err) => return Err(err), | |
| 720 }; | |
| 721 let elems = { | |
| 722 let state = decoder.get_mut(&context); | |
| 723 match state.decode_array_header::<T>() { | |
| 724 Ok(header) => header.data(), | |
| 725 Err(err) => return Err(err), | |
| 726 } | |
| 727 }; | |
| 728 Ok((context, elems as usize)) | |
| 729 } | |
| 730 | |
| 731 impl<K: MojomEncodable + Eq + Hash, V: MojomEncodable> MojomPointer for HashMap<
K, V> { | |
| 732 fn header_data(&self) -> DataHeaderValue { | |
| 733 DataHeaderValue::Version(0) | |
| 734 } | |
| 735 fn serialized_size(&self, _context: &Context) -> usize { | |
| 736 MAP_SIZE | |
| 737 } | |
| 738 fn encode_value(self, encoder: &mut Encoder, context: Context) { | |
| 739 let elems = self.len(); | |
| 740 let meta_value = DataHeaderValue::Elements(elems as u32); | |
| 741 // We need to move values into this vector because we can't copy the key
s. | |
| 742 // (Handles are not copyable so MojomEncodable cannot be copyable!) | |
| 743 let mut vals_vec = Vec::with_capacity(elems); | |
| 744 // Key setup | |
| 745 // Write a pointer to the keys array. | |
| 746 let keys_loc = encoder.size() as u64; | |
| 747 { | |
| 748 let state = encoder.get_mut(&context); | |
| 749 state.encode_pointer(keys_loc); | |
| 750 } | |
| 751 // Create the keys data header | |
| 752 let keys_bytes = DATA_HEADER_SIZE + (K::embed_size(&context) * elems).as
_bytes(); | |
| 753 let keys_data_header = DataHeader::new(keys_bytes, meta_value); | |
| 754 // Claim space for the keys array in the encoder | |
| 755 let keys_context = encoder.add(&keys_data_header).unwrap(); | |
| 756 // Encode keys, setup vals | |
| 757 for (key, value) in self.into_iter() { | |
| 758 key.encode(encoder, keys_context.clone()); | |
| 759 vals_vec.push(value); | |
| 760 } | |
| 761 // Encode vals | |
| 762 vals_vec.encode(encoder, context.clone()) | |
| 763 } | |
| 764 fn decode_value(decoder: &mut Decoder, context: Context) -> Result<HashMap<K
, V>, ValidationError> { | |
| 765 let (keys_offset, vals_offset) = { | |
| 766 let state = decoder.get_mut(&context); | |
| 767 match state.decode_struct_header(&MAP_VERSIONS) { | |
| 768 Ok(_) => (), | |
| 769 Err(err) => return Err(err), | |
| 770 }; | |
| 771 // Decode the keys pointer and check for overflow | |
| 772 let keys_offset = match state.decode_pointer() { | |
| 773 Some(ptr) => ptr, | |
| 774 None => return Err(ValidationError::IllegalPointer), | |
| 775 }; | |
| 776 // Decode the keys pointer and check for overflow | |
| 777 let vals_offset = match state.decode_pointer() { | |
| 778 Some(ptr) => ptr, | |
| 779 None => return Err(ValidationError::IllegalPointer), | |
| 780 }; | |
| 781 if keys_offset == MOJOM_NULL_POINTER || vals_offset == MOJOM_NULL_PO
INTER { | |
| 782 return Err(ValidationError::UnexpectedNullPointer); | |
| 783 } | |
| 784 (keys_offset as usize, vals_offset as usize) | |
| 785 }; | |
| 786 let (keys_context, keys_elems) = match array_claim_and_decode_header::<K
>(decoder, keys_offset) { | |
| 787 Ok((context, elems)) => (context, elems), | |
| 788 Err(err) => return Err(err), | |
| 789 }; | |
| 790 let mut keys_vec: Vec<K> = Vec::with_capacity(keys_elems as usize); | |
| 791 for _ in 0..keys_elems { | |
| 792 let key = match K::decode(decoder, keys_context.clone()) { | |
| 793 Ok(value) => value, | |
| 794 Err(err) => return Err(err), | |
| 795 }; | |
| 796 keys_vec.push(key); | |
| 797 } | |
| 798 let (vals_context, vals_elems) = match array_claim_and_decode_header::<V
>(decoder, vals_offset) { | |
| 799 Ok((context, elems)) => (context, elems), | |
| 800 Err(err) => return Err(err), | |
| 801 }; | |
| 802 if keys_elems != vals_elems { | |
| 803 return Err(ValidationError::DifferentSizedArraysInMap); | |
| 804 } | |
| 805 let mut map = HashMap::with_capacity(keys_elems as usize); | |
| 806 for key in keys_vec.into_iter() { | |
| 807 let val = match V::decode(decoder, vals_context.clone()) { | |
| 808 Ok(value) => value, | |
| 809 Err(err) => return Err(err), | |
| 810 }; | |
| 811 map.insert(key, val); | |
| 812 } | |
| 813 Ok(map) | |
| 814 } | |
| 815 } | |
| 816 | |
| 817 impl<K: MojomEncodable + Eq + Hash, V: MojomEncodable> MojomEncodable for HashMa
p<K, V> { | |
| 818 impl_encodable_for_pointer!(); | |
| 819 fn compute_size(&self, context: Context) -> usize { | |
| 820 let mut size = encoding::align_default(self.serialized_size(&context)); | |
| 821 // The size of the one array | |
| 822 size += DATA_HEADER_SIZE; | |
| 823 size += (K::embed_size(&context) * self.len()).as_bytes(); | |
| 824 size = encoding::align_default(size); | |
| 825 // Any extra space used by the keys | |
| 826 for (key, _) in self { | |
| 827 size += key.compute_size(context.clone()); | |
| 828 } | |
| 829 // Need to re-align after this for the next array | |
| 830 size = encoding::align_default(size); | |
| 831 // The size of the one array | |
| 832 size += DATA_HEADER_SIZE; | |
| 833 size += (V::embed_size(&context) * self.len()).as_bytes(); | |
| 834 size = encoding::align_default(size); | |
| 835 // Any extra space used by the values | |
| 836 for (_, value) in self { | |
| 837 size += value.compute_size(context.clone()); | |
| 838 } | |
| 839 // Align one more time at the end to keep the next object aligned. | |
| 840 encoding::align_default(size) | |
| 841 } | |
| 842 } | |
| 843 | |
| 844 impl<T: MojomEncodable + CastHandle + Handle> MojomHandle for T {} | |
| 845 | |
| 846 macro_rules! impl_encodable_for_handle { | |
| 847 ($handle_type:path) => { | |
| 848 fn mojom_alignment() -> usize { | |
| 849 4 | |
| 850 } | |
| 851 fn mojom_type() -> MojomType { | |
| 852 MojomType::Handle | |
| 853 } | |
| 854 fn embed_size(_context: &Context) -> Bits { | |
| 855 Bits(8 * mem::size_of::<u32>()) | |
| 856 } | |
| 857 fn compute_size(&self, _context: Context) -> usize { | |
| 858 0 | |
| 859 } | |
| 860 fn encode(self, encoder: &mut Encoder, context: Context) { | |
| 861 let pos = encoder.add_handle(self.as_untyped()); | |
| 862 let mut state = encoder.get_mut(&context); | |
| 863 state.encode(pos as i32); | |
| 864 } | |
| 865 fn decode(decoder: &mut Decoder, context: Context) -> Result<$handle_typ
e, ValidationError> { | |
| 866 let handle_index = { | |
| 867 let mut state = decoder.get_mut(&context); | |
| 868 state.decode::<i32>() | |
| 869 }; | |
| 870 decoder.claim_handle::<$handle_type>(handle_index) | |
| 871 } | |
| 872 } | |
| 873 } | |
| 874 | |
| 875 impl MojomEncodable for UntypedHandle { | |
| 876 impl_encodable_for_handle!(UntypedHandle); | |
| 877 } | |
| 878 | |
| 879 impl MojomEncodable for message_pipe::MessageEndpoint { | |
| 880 impl_encodable_for_handle!(message_pipe::MessageEndpoint); | |
| 881 } | |
| 882 | |
| 883 impl MojomEncodable for shared_buffer::SharedBuffer { | |
| 884 impl_encodable_for_handle!(shared_buffer::SharedBuffer); | |
| 885 } | |
| 886 | |
| 887 impl<T> MojomEncodable for data_pipe::Consumer<T> { | |
| 888 impl_encodable_for_handle!(data_pipe::Consumer<T>); | |
| 889 } | |
| 890 | |
| 891 impl<T> MojomEncodable for data_pipe::Producer<T> { | |
| 892 impl_encodable_for_handle!(data_pipe::Producer<T>); | |
| 893 } | |
| 894 | |
| 895 impl MojomEncodable for wait_set::WaitSet { | |
| 896 impl_encodable_for_handle!(wait_set::WaitSet); | |
| 897 } | |
| OLD | NEW |