Home | History | Annotate | Download | only in lib
      1 // Copyright 2014 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 (function() {
      6   var internal = mojo.internal;
      7 
      8   var kErrorUnsigned = "Passing negative value to unsigned";
      9   var kErrorArray = "Passing non Array for array type";
     10   var kErrorString = "Passing non String for string type";
     11   var kErrorMap = "Passing non Map for map type";
     12 
     13   // Memory -------------------------------------------------------------------
     14 
     15   var kAlignment = 8;
     16 
     17   function align(size) {
     18     return size + (kAlignment - (size % kAlignment)) % kAlignment;
     19   }
     20 
     21   function isAligned(offset) {
     22     return offset >= 0 && (offset % kAlignment) === 0;
     23   }
     24 
     25   // Constants ----------------------------------------------------------------
     26 
     27   var kArrayHeaderSize = 8;
     28   var kStructHeaderSize = 8;
     29   var kMessageV0HeaderSize = 24;
     30   var kMessageV1HeaderSize = 32;
     31   var kMessageV2HeaderSize = 48;
     32   var kMapStructPayloadSize = 16;
     33 
     34   var kStructHeaderNumBytesOffset = 0;
     35   var kStructHeaderVersionOffset = 4;
     36 
     37   var kEncodedInvalidHandleValue = 0xFFFFFFFF;
     38 
     39   // Decoder ------------------------------------------------------------------
     40 
     41   function Decoder(buffer, handles, associatedEndpointHandles, base) {
     42     this.buffer = buffer;
     43     this.handles = handles;
     44     this.associatedEndpointHandles = associatedEndpointHandles;
     45     this.base = base;
     46     this.next = base;
     47   }
     48 
     49   Decoder.prototype.align = function() {
     50     this.next = align(this.next);
     51   };
     52 
     53   Decoder.prototype.skip = function(offset) {
     54     this.next += offset;
     55   };
     56 
     57   Decoder.prototype.readInt8 = function() {
     58     var result = this.buffer.getInt8(this.next);
     59     this.next += 1;
     60     return result;
     61   };
     62 
     63   Decoder.prototype.readUint8 = function() {
     64     var result = this.buffer.getUint8(this.next);
     65     this.next += 1;
     66     return result;
     67   };
     68 
     69   Decoder.prototype.readInt16 = function() {
     70     var result = this.buffer.getInt16(this.next);
     71     this.next += 2;
     72     return result;
     73   };
     74 
     75   Decoder.prototype.readUint16 = function() {
     76     var result = this.buffer.getUint16(this.next);
     77     this.next += 2;
     78     return result;
     79   };
     80 
     81   Decoder.prototype.readInt32 = function() {
     82     var result = this.buffer.getInt32(this.next);
     83     this.next += 4;
     84     return result;
     85   };
     86 
     87   Decoder.prototype.readUint32 = function() {
     88     var result = this.buffer.getUint32(this.next);
     89     this.next += 4;
     90     return result;
     91   };
     92 
     93   Decoder.prototype.readInt64 = function() {
     94     var result = this.buffer.getInt64(this.next);
     95     this.next += 8;
     96     return result;
     97   };
     98 
     99   Decoder.prototype.readUint64 = function() {
    100     var result = this.buffer.getUint64(this.next);
    101     this.next += 8;
    102     return result;
    103   };
    104 
    105   Decoder.prototype.readFloat = function() {
    106     var result = this.buffer.getFloat32(this.next);
    107     this.next += 4;
    108     return result;
    109   };
    110 
    111   Decoder.prototype.readDouble = function() {
    112     var result = this.buffer.getFloat64(this.next);
    113     this.next += 8;
    114     return result;
    115   };
    116 
    117   Decoder.prototype.decodePointer = function() {
    118     // TODO(abarth): To correctly decode a pointer, we need to know the real
    119     // base address of the array buffer.
    120     var offsetPointer = this.next;
    121     var offset = this.readUint64();
    122     if (!offset)
    123       return 0;
    124     return offsetPointer + offset;
    125   };
    126 
    127   Decoder.prototype.decodeAndCreateDecoder = function(pointer) {
    128     return new Decoder(this.buffer, this.handles,
    129         this.associatedEndpointHandles, pointer);
    130   };
    131 
    132   Decoder.prototype.decodeHandle = function() {
    133     return this.handles[this.readUint32()] || null;
    134   };
    135 
    136   Decoder.prototype.decodeAssociatedEndpointHandle = function() {
    137     return this.associatedEndpointHandles[this.readUint32()] || null;
    138   };
    139 
    140   Decoder.prototype.decodeString = function() {
    141     var numberOfBytes = this.readUint32();
    142     var numberOfElements = this.readUint32();
    143     var base = this.next;
    144     this.next += numberOfElements;
    145     return internal.decodeUtf8String(
    146         new Uint8Array(this.buffer.arrayBuffer, base, numberOfElements));
    147   };
    148 
    149   Decoder.prototype.decodeArray = function(cls) {
    150     var numberOfBytes = this.readUint32();
    151     var numberOfElements = this.readUint32();
    152     var val = new Array(numberOfElements);
    153     if (cls === PackedBool) {
    154       var byte;
    155       for (var i = 0; i < numberOfElements; ++i) {
    156         if (i % 8 === 0)
    157           byte = this.readUint8();
    158         val[i] = (byte & (1 << i % 8)) ? true : false;
    159       }
    160     } else {
    161       for (var i = 0; i < numberOfElements; ++i) {
    162         val[i] = cls.decode(this);
    163       }
    164     }
    165     return val;
    166   };
    167 
    168   Decoder.prototype.decodeStruct = function(cls) {
    169     return cls.decode(this);
    170   };
    171 
    172   Decoder.prototype.decodeStructPointer = function(cls) {
    173     var pointer = this.decodePointer();
    174     if (!pointer) {
    175       return null;
    176     }
    177     return cls.decode(this.decodeAndCreateDecoder(pointer));
    178   };
    179 
    180   Decoder.prototype.decodeArrayPointer = function(cls) {
    181     var pointer = this.decodePointer();
    182     if (!pointer) {
    183       return null;
    184     }
    185     return this.decodeAndCreateDecoder(pointer).decodeArray(cls);
    186   };
    187 
    188   Decoder.prototype.decodeStringPointer = function() {
    189     var pointer = this.decodePointer();
    190     if (!pointer) {
    191       return null;
    192     }
    193     return this.decodeAndCreateDecoder(pointer).decodeString();
    194   };
    195 
    196   Decoder.prototype.decodeMap = function(keyClass, valueClass) {
    197     this.skip(4); // numberOfBytes
    198     this.skip(4); // version
    199     var keys = this.decodeArrayPointer(keyClass);
    200     var values = this.decodeArrayPointer(valueClass);
    201     var val = new Map();
    202     for (var i = 0; i < keys.length; i++)
    203       val.set(keys[i], values[i]);
    204     return val;
    205   };
    206 
    207   Decoder.prototype.decodeMapPointer = function(keyClass, valueClass) {
    208     var pointer = this.decodePointer();
    209     if (!pointer) {
    210       return null;
    211     }
    212     var decoder = this.decodeAndCreateDecoder(pointer);
    213     return decoder.decodeMap(keyClass, valueClass);
    214   };
    215 
    216   // Encoder ------------------------------------------------------------------
    217 
    218   function Encoder(buffer, handles, associatedEndpointHandles, base) {
    219     this.buffer = buffer;
    220     this.handles = handles;
    221     this.associatedEndpointHandles = associatedEndpointHandles;
    222     this.base = base;
    223     this.next = base;
    224   }
    225 
    226   Encoder.prototype.align = function() {
    227     this.next = align(this.next);
    228   };
    229 
    230   Encoder.prototype.skip = function(offset) {
    231     this.next += offset;
    232   };
    233 
    234   Encoder.prototype.writeInt8 = function(val) {
    235     this.buffer.setInt8(this.next, val);
    236     this.next += 1;
    237   };
    238 
    239   Encoder.prototype.writeUint8 = function(val) {
    240     if (val < 0) {
    241       throw new Error(kErrorUnsigned);
    242     }
    243     this.buffer.setUint8(this.next, val);
    244     this.next += 1;
    245   };
    246 
    247   Encoder.prototype.writeInt16 = function(val) {
    248     this.buffer.setInt16(this.next, val);
    249     this.next += 2;
    250   };
    251 
    252   Encoder.prototype.writeUint16 = function(val) {
    253     if (val < 0) {
    254       throw new Error(kErrorUnsigned);
    255     }
    256     this.buffer.setUint16(this.next, val);
    257     this.next += 2;
    258   };
    259 
    260   Encoder.prototype.writeInt32 = function(val) {
    261     this.buffer.setInt32(this.next, val);
    262     this.next += 4;
    263   };
    264 
    265   Encoder.prototype.writeUint32 = function(val) {
    266     if (val < 0) {
    267       throw new Error(kErrorUnsigned);
    268     }
    269     this.buffer.setUint32(this.next, val);
    270     this.next += 4;
    271   };
    272 
    273   Encoder.prototype.writeInt64 = function(val) {
    274     this.buffer.setInt64(this.next, val);
    275     this.next += 8;
    276   };
    277 
    278   Encoder.prototype.writeUint64 = function(val) {
    279     if (val < 0) {
    280       throw new Error(kErrorUnsigned);
    281     }
    282     this.buffer.setUint64(this.next, val);
    283     this.next += 8;
    284   };
    285 
    286   Encoder.prototype.writeFloat = function(val) {
    287     this.buffer.setFloat32(this.next, val);
    288     this.next += 4;
    289   };
    290 
    291   Encoder.prototype.writeDouble = function(val) {
    292     this.buffer.setFloat64(this.next, val);
    293     this.next += 8;
    294   };
    295 
    296   Encoder.prototype.encodePointer = function(pointer) {
    297     if (!pointer)
    298       return this.writeUint64(0);
    299     // TODO(abarth): To correctly encode a pointer, we need to know the real
    300     // base address of the array buffer.
    301     var offset = pointer - this.next;
    302     this.writeUint64(offset);
    303   };
    304 
    305   Encoder.prototype.createAndEncodeEncoder = function(size) {
    306     var pointer = this.buffer.alloc(align(size));
    307     this.encodePointer(pointer);
    308     return new Encoder(this.buffer, this.handles,
    309         this.associatedEndpointHandles, pointer);
    310   };
    311 
    312   Encoder.prototype.encodeHandle = function(handle) {
    313     if (handle) {
    314       this.handles.push(handle);
    315       this.writeUint32(this.handles.length - 1);
    316     } else {
    317       this.writeUint32(kEncodedInvalidHandleValue);
    318     }
    319   };
    320 
    321   Encoder.prototype.encodeAssociatedEndpointHandle = function(endpointHandle) {
    322     if (endpointHandle) {
    323       this.associatedEndpointHandles.push(endpointHandle);
    324       this.writeUint32(this.associatedEndpointHandles.length - 1);
    325     } else {
    326       this.writeUint32(kEncodedInvalidHandleValue);
    327     }
    328   };
    329 
    330   Encoder.prototype.encodeString = function(val) {
    331     var base = this.next + kArrayHeaderSize;
    332     var numberOfElements = internal.encodeUtf8String(
    333         val, new Uint8Array(this.buffer.arrayBuffer, base));
    334     var numberOfBytes = kArrayHeaderSize + numberOfElements;
    335     this.writeUint32(numberOfBytes);
    336     this.writeUint32(numberOfElements);
    337     this.next += numberOfElements;
    338   };
    339 
    340   Encoder.prototype.encodeArray =
    341       function(cls, val, numberOfElements, encodedSize) {
    342     if (numberOfElements === undefined)
    343       numberOfElements = val.length;
    344     if (encodedSize === undefined)
    345       encodedSize = kArrayHeaderSize + cls.encodedSize * numberOfElements;
    346 
    347     this.writeUint32(encodedSize);
    348     this.writeUint32(numberOfElements);
    349 
    350     if (cls === PackedBool) {
    351       var byte = 0;
    352       for (i = 0; i < numberOfElements; ++i) {
    353         if (val[i])
    354           byte |= (1 << i % 8);
    355         if (i % 8 === 7 || i == numberOfElements - 1) {
    356           Uint8.encode(this, byte);
    357           byte = 0;
    358         }
    359       }
    360     } else {
    361       for (var i = 0; i < numberOfElements; ++i)
    362         cls.encode(this, val[i]);
    363     }
    364   };
    365 
    366   Encoder.prototype.encodeStruct = function(cls, val) {
    367     return cls.encode(this, val);
    368   };
    369 
    370   Encoder.prototype.encodeStructPointer = function(cls, val) {
    371     if (val == null) {
    372       // Also handles undefined, since undefined == null.
    373       this.encodePointer(val);
    374       return;
    375     }
    376     var encoder = this.createAndEncodeEncoder(cls.encodedSize);
    377     cls.encode(encoder, val);
    378   };
    379 
    380   Encoder.prototype.encodeArrayPointer = function(cls, val) {
    381     if (val == null) {
    382       // Also handles undefined, since undefined == null.
    383       this.encodePointer(val);
    384       return;
    385     }
    386 
    387     var numberOfElements = val.length;
    388     if (!Number.isSafeInteger(numberOfElements) || numberOfElements < 0)
    389       throw new Error(kErrorArray);
    390 
    391     var encodedSize = kArrayHeaderSize + ((cls === PackedBool) ?
    392         Math.ceil(numberOfElements / 8) : cls.encodedSize * numberOfElements);
    393     var encoder = this.createAndEncodeEncoder(encodedSize);
    394     encoder.encodeArray(cls, val, numberOfElements, encodedSize);
    395   };
    396 
    397   Encoder.prototype.encodeStringPointer = function(val) {
    398     if (val == null) {
    399       // Also handles undefined, since undefined == null.
    400       this.encodePointer(val);
    401       return;
    402     }
    403     // Only accepts string primivites, not String Objects like new String("foo")
    404     if (typeof(val) !== "string") {
    405       throw new Error(kErrorString);
    406     }
    407     var encodedSize = kArrayHeaderSize + internal.utf8Length(val);
    408     var encoder = this.createAndEncodeEncoder(encodedSize);
    409     encoder.encodeString(val);
    410   };
    411 
    412   Encoder.prototype.encodeMap = function(keyClass, valueClass, val) {
    413     var keys = new Array(val.size);
    414     var values = new Array(val.size);
    415     var i = 0;
    416     val.forEach(function(value, key) {
    417       values[i] = value;
    418       keys[i++] = key;
    419     });
    420     this.writeUint32(kStructHeaderSize + kMapStructPayloadSize);
    421     this.writeUint32(0);  // version
    422     this.encodeArrayPointer(keyClass, keys);
    423     this.encodeArrayPointer(valueClass, values);
    424   }
    425 
    426   Encoder.prototype.encodeMapPointer = function(keyClass, valueClass, val) {
    427     if (val == null) {
    428       // Also handles undefined, since undefined == null.
    429       this.encodePointer(val);
    430       return;
    431     }
    432     if (!(val instanceof Map)) {
    433       throw new Error(kErrorMap);
    434     }
    435     var encodedSize = kStructHeaderSize + kMapStructPayloadSize;
    436     var encoder = this.createAndEncodeEncoder(encodedSize);
    437     encoder.encodeMap(keyClass, valueClass, val);
    438   };
    439 
    440   // Message ------------------------------------------------------------------
    441 
    442   var kMessageInterfaceIdOffset = kStructHeaderSize;
    443   var kMessageNameOffset = kMessageInterfaceIdOffset + 4;
    444   var kMessageFlagsOffset = kMessageNameOffset + 4;
    445   var kMessageRequestIDOffset = kMessageFlagsOffset + 8;
    446   var kMessagePayloadInterfaceIdsPointerOffset = kMessageV2HeaderSize - 8;
    447 
    448   var kMessageExpectsResponse = 1 << 0;
    449   var kMessageIsResponse      = 1 << 1;
    450 
    451   function Message(buffer, handles, associatedEndpointHandles) {
    452     if (associatedEndpointHandles === undefined) {
    453       associatedEndpointHandles = [];
    454     }
    455 
    456     this.buffer = buffer;
    457     this.handles = handles;
    458     this.associatedEndpointHandles = associatedEndpointHandles;
    459   }
    460 
    461   Message.prototype.getHeaderNumBytes = function() {
    462     return this.buffer.getUint32(kStructHeaderNumBytesOffset);
    463   };
    464 
    465   Message.prototype.getHeaderVersion = function() {
    466     return this.buffer.getUint32(kStructHeaderVersionOffset);
    467   };
    468 
    469   Message.prototype.getName = function() {
    470     return this.buffer.getUint32(kMessageNameOffset);
    471   };
    472 
    473   Message.prototype.getFlags = function() {
    474     return this.buffer.getUint32(kMessageFlagsOffset);
    475   };
    476 
    477   Message.prototype.getInterfaceId = function() {
    478     return this.buffer.getUint32(kMessageInterfaceIdOffset);
    479   };
    480 
    481   Message.prototype.getPayloadInterfaceIds = function() {
    482     if (this.getHeaderVersion() < 2) {
    483       return null;
    484     }
    485 
    486     var decoder = new Decoder(this.buffer, this.handles,
    487         this.associatedEndpointHandles,
    488         kMessagePayloadInterfaceIdsPointerOffset);
    489     var payloadInterfaceIds = decoder.decodeArrayPointer(Uint32);
    490     return payloadInterfaceIds;
    491   };
    492 
    493   Message.prototype.isResponse = function() {
    494     return (this.getFlags() & kMessageIsResponse) != 0;
    495   };
    496 
    497   Message.prototype.expectsResponse = function() {
    498     return (this.getFlags() & kMessageExpectsResponse) != 0;
    499   };
    500 
    501   Message.prototype.setRequestID = function(requestID) {
    502     // TODO(darin): Verify that space was reserved for this field!
    503     this.buffer.setUint64(kMessageRequestIDOffset, requestID);
    504   };
    505 
    506   Message.prototype.setInterfaceId = function(interfaceId) {
    507     this.buffer.setUint32(kMessageInterfaceIdOffset, interfaceId);
    508   };
    509 
    510   Message.prototype.setPayloadInterfaceIds_ = function(payloadInterfaceIds) {
    511     if (this.getHeaderVersion() < 2) {
    512       throw new Error(
    513           "Version of message does not support payload interface ids");
    514     }
    515 
    516     var decoder = new Decoder(this.buffer, this.handles,
    517         this.associatedEndpointHandles,
    518         kMessagePayloadInterfaceIdsPointerOffset);
    519     var payloadInterfaceIdsOffset = decoder.decodePointer();
    520     var encoder = new Encoder(this.buffer, this.handles,
    521         this.associatedEndpointHandles,
    522         payloadInterfaceIdsOffset);
    523     encoder.encodeArray(Uint32, payloadInterfaceIds);
    524   };
    525 
    526   Message.prototype.serializeAssociatedEndpointHandles = function(
    527       associatedGroupController) {
    528     if (this.associatedEndpointHandles.length > 0) {
    529       if (this.getHeaderVersion() < 2) {
    530         throw new Error(
    531             "Version of message does not support associated endpoint handles");
    532       }
    533 
    534       var data = [];
    535       for (var i = 0; i < this.associatedEndpointHandles.length; i++) {
    536         var handle = this.associatedEndpointHandles[i];
    537         data.push(associatedGroupController.associateInterface(handle));
    538       }
    539       this.associatedEndpointHandles = [];
    540       this.setPayloadInterfaceIds_(data);
    541     }
    542   };
    543 
    544   Message.prototype.deserializeAssociatedEndpointHandles = function(
    545       associatedGroupController) {
    546     if (this.getHeaderVersion() < 2) {
    547       return true;
    548     }
    549 
    550     this.associatedEndpointHandles = [];
    551     var ids = this.getPayloadInterfaceIds();
    552 
    553     var result = true;
    554     for (var i = 0; i < ids.length; i++) {
    555       var handle = associatedGroupController.createLocalEndpointHandle(ids[i]);
    556       if (internal.isValidInterfaceId(ids[i]) && !handle.isValid()) {
    557         // |ids[i]| itself is valid but handle creation failed. In that case,
    558         // mark deserialization as failed but continue to deserialize the
    559         // rest of handles.
    560         result = false;
    561       }
    562       this.associatedEndpointHandles.push(handle);
    563       ids[i] = internal.kInvalidInterfaceId;
    564     }
    565 
    566     this.setPayloadInterfaceIds_(ids);
    567     return result;
    568   };
    569 
    570 
    571   // MessageV0Builder ---------------------------------------------------------
    572 
    573   function MessageV0Builder(messageName, payloadSize) {
    574     // Currently, we don't compute the payload size correctly ahead of time.
    575     // Instead, we resize the buffer at the end.
    576     var numberOfBytes = kMessageV0HeaderSize + payloadSize;
    577     this.buffer = new internal.Buffer(numberOfBytes);
    578     this.handles = [];
    579     var encoder = this.createEncoder(kMessageV0HeaderSize);
    580     encoder.writeUint32(kMessageV0HeaderSize);
    581     encoder.writeUint32(0);  // version.
    582     encoder.writeUint32(0);  // interface ID.
    583     encoder.writeUint32(messageName);
    584     encoder.writeUint32(0);  // flags.
    585     encoder.writeUint32(0);  // padding.
    586   }
    587 
    588   MessageV0Builder.prototype.createEncoder = function(size) {
    589     var pointer = this.buffer.alloc(size);
    590     return new Encoder(this.buffer, this.handles, [], pointer);
    591   };
    592 
    593   MessageV0Builder.prototype.encodeStruct = function(cls, val) {
    594     cls.encode(this.createEncoder(cls.encodedSize), val);
    595   };
    596 
    597   MessageV0Builder.prototype.finish = function() {
    598     // TODO(abarth): Rather than resizing the buffer at the end, we could
    599     // compute the size we need ahead of time, like we do in C++.
    600     this.buffer.trim();
    601     var message = new Message(this.buffer, this.handles);
    602     this.buffer = null;
    603     this.handles = null;
    604     this.encoder = null;
    605     return message;
    606   };
    607 
    608   // MessageV1Builder -----------------------------------------------
    609 
    610   function MessageV1Builder(messageName, payloadSize, flags,
    611                                        requestID) {
    612     // Currently, we don't compute the payload size correctly ahead of time.
    613     // Instead, we resize the buffer at the end.
    614     var numberOfBytes = kMessageV1HeaderSize + payloadSize;
    615     this.buffer = new internal.Buffer(numberOfBytes);
    616     this.handles = [];
    617     var encoder = this.createEncoder(kMessageV1HeaderSize);
    618     encoder.writeUint32(kMessageV1HeaderSize);
    619     encoder.writeUint32(1);  // version.
    620     encoder.writeUint32(0);  // interface ID.
    621     encoder.writeUint32(messageName);
    622     encoder.writeUint32(flags);
    623     encoder.writeUint32(0);  // padding.
    624     encoder.writeUint64(requestID);
    625   }
    626 
    627   MessageV1Builder.prototype =
    628       Object.create(MessageV0Builder.prototype);
    629 
    630   MessageV1Builder.prototype.constructor =
    631       MessageV1Builder;
    632 
    633   // MessageV2 -----------------------------------------------
    634 
    635   function MessageV2Builder(messageName, payloadSize, flags, requestID) {
    636     // Currently, we don't compute the payload size correctly ahead of time.
    637     // Instead, we resize the buffer at the end.
    638     var numberOfBytes = kMessageV2HeaderSize + payloadSize;
    639     this.buffer = new internal.Buffer(numberOfBytes);
    640     this.handles = [];
    641 
    642     this.payload = null;
    643     this.associatedEndpointHandles = [];
    644 
    645     this.encoder = this.createEncoder(kMessageV2HeaderSize);
    646     this.encoder.writeUint32(kMessageV2HeaderSize);
    647     this.encoder.writeUint32(2);  // version.
    648     // Gets set to an appropriate interfaceId for the endpoint by the Router.
    649     this.encoder.writeUint32(0);  // interface ID.
    650     this.encoder.writeUint32(messageName);
    651     this.encoder.writeUint32(flags);
    652     this.encoder.writeUint32(0);  // padding.
    653     this.encoder.writeUint64(requestID);
    654   }
    655 
    656   MessageV2Builder.prototype.createEncoder = function(size) {
    657     var pointer = this.buffer.alloc(size);
    658     return new Encoder(this.buffer, this.handles,
    659         this.associatedEndpointHandles, pointer);
    660   };
    661 
    662   MessageV2Builder.prototype.setPayload = function(cls, val) {
    663     this.payload = {cls: cls, val: val};
    664   };
    665 
    666   MessageV2Builder.prototype.finish = function() {
    667     if (!this.payload) {
    668       throw new Error("Payload needs to be set before calling finish");
    669     }
    670 
    671     this.encoder.encodeStructPointer(this.payload.cls, this.payload.val);
    672     this.encoder.encodeArrayPointer(Uint32,
    673         new Array(this.associatedEndpointHandles.length));
    674 
    675     this.buffer.trim();
    676     var message = new Message(this.buffer, this.handles,
    677         this.associatedEndpointHandles);
    678     this.buffer = null;
    679     this.handles = null;
    680     this.encoder = null;
    681     this.payload = null;
    682     this.associatedEndpointHandles = null;
    683 
    684     return message;
    685   };
    686 
    687   // MessageReader ------------------------------------------------------------
    688 
    689   function MessageReader(message) {
    690     this.decoder = new Decoder(message.buffer, message.handles,
    691         message.associatedEndpointHandles, 0);
    692     var messageHeaderSize = this.decoder.readUint32();
    693     this.payloadSize = message.buffer.byteLength - messageHeaderSize;
    694     var version = this.decoder.readUint32();
    695     var interface_id = this.decoder.readUint32();
    696     this.messageName = this.decoder.readUint32();
    697     this.flags = this.decoder.readUint32();
    698     // Skip the padding.
    699     this.decoder.skip(4);
    700     if (version >= 1)
    701       this.requestID = this.decoder.readUint64();
    702     this.decoder.skip(messageHeaderSize - this.decoder.next);
    703   }
    704 
    705   MessageReader.prototype.decodeStruct = function(cls) {
    706     return cls.decode(this.decoder);
    707   };
    708 
    709   // Built-in types -----------------------------------------------------------
    710 
    711   // This type is only used with ArrayOf(PackedBool).
    712   function PackedBool() {
    713   }
    714 
    715   function Int8() {
    716   }
    717 
    718   Int8.encodedSize = 1;
    719 
    720   Int8.decode = function(decoder) {
    721     return decoder.readInt8();
    722   };
    723 
    724   Int8.encode = function(encoder, val) {
    725     encoder.writeInt8(val);
    726   };
    727 
    728   Uint8.encode = function(encoder, val) {
    729     encoder.writeUint8(val);
    730   };
    731 
    732   function Uint8() {
    733   }
    734 
    735   Uint8.encodedSize = 1;
    736 
    737   Uint8.decode = function(decoder) {
    738     return decoder.readUint8();
    739   };
    740 
    741   Uint8.encode = function(encoder, val) {
    742     encoder.writeUint8(val);
    743   };
    744 
    745   function Int16() {
    746   }
    747 
    748   Int16.encodedSize = 2;
    749 
    750   Int16.decode = function(decoder) {
    751     return decoder.readInt16();
    752   };
    753 
    754   Int16.encode = function(encoder, val) {
    755     encoder.writeInt16(val);
    756   };
    757 
    758   function Uint16() {
    759   }
    760 
    761   Uint16.encodedSize = 2;
    762 
    763   Uint16.decode = function(decoder) {
    764     return decoder.readUint16();
    765   };
    766 
    767   Uint16.encode = function(encoder, val) {
    768     encoder.writeUint16(val);
    769   };
    770 
    771   function Int32() {
    772   }
    773 
    774   Int32.encodedSize = 4;
    775 
    776   Int32.decode = function(decoder) {
    777     return decoder.readInt32();
    778   };
    779 
    780   Int32.encode = function(encoder, val) {
    781     encoder.writeInt32(val);
    782   };
    783 
    784   function Uint32() {
    785   }
    786 
    787   Uint32.encodedSize = 4;
    788 
    789   Uint32.decode = function(decoder) {
    790     return decoder.readUint32();
    791   };
    792 
    793   Uint32.encode = function(encoder, val) {
    794     encoder.writeUint32(val);
    795   };
    796 
    797   function Int64() {
    798   }
    799 
    800   Int64.encodedSize = 8;
    801 
    802   Int64.decode = function(decoder) {
    803     return decoder.readInt64();
    804   };
    805 
    806   Int64.encode = function(encoder, val) {
    807     encoder.writeInt64(val);
    808   };
    809 
    810   function Uint64() {
    811   }
    812 
    813   Uint64.encodedSize = 8;
    814 
    815   Uint64.decode = function(decoder) {
    816     return decoder.readUint64();
    817   };
    818 
    819   Uint64.encode = function(encoder, val) {
    820     encoder.writeUint64(val);
    821   };
    822 
    823   function String() {
    824   };
    825 
    826   String.encodedSize = 8;
    827 
    828   String.decode = function(decoder) {
    829     return decoder.decodeStringPointer();
    830   };
    831 
    832   String.encode = function(encoder, val) {
    833     encoder.encodeStringPointer(val);
    834   };
    835 
    836   function NullableString() {
    837   }
    838 
    839   NullableString.encodedSize = String.encodedSize;
    840 
    841   NullableString.decode = String.decode;
    842 
    843   NullableString.encode = String.encode;
    844 
    845   function Float() {
    846   }
    847 
    848   Float.encodedSize = 4;
    849 
    850   Float.decode = function(decoder) {
    851     return decoder.readFloat();
    852   };
    853 
    854   Float.encode = function(encoder, val) {
    855     encoder.writeFloat(val);
    856   };
    857 
    858   function Double() {
    859   }
    860 
    861   Double.encodedSize = 8;
    862 
    863   Double.decode = function(decoder) {
    864     return decoder.readDouble();
    865   };
    866 
    867   Double.encode = function(encoder, val) {
    868     encoder.writeDouble(val);
    869   };
    870 
    871   function Enum(cls) {
    872     this.cls = cls;
    873   }
    874 
    875   Enum.prototype.encodedSize = 4;
    876 
    877   Enum.prototype.decode = function(decoder) {
    878     return decoder.readInt32();
    879   };
    880 
    881   Enum.prototype.encode = function(encoder, val) {
    882     encoder.writeInt32(val);
    883   };
    884 
    885   function PointerTo(cls) {
    886     this.cls = cls;
    887   }
    888 
    889   PointerTo.prototype.encodedSize = 8;
    890 
    891   PointerTo.prototype.decode = function(decoder) {
    892     var pointer = decoder.decodePointer();
    893     if (!pointer) {
    894       return null;
    895     }
    896     return this.cls.decode(decoder.decodeAndCreateDecoder(pointer));
    897   };
    898 
    899   PointerTo.prototype.encode = function(encoder, val) {
    900     if (!val) {
    901       encoder.encodePointer(val);
    902       return;
    903     }
    904     var objectEncoder = encoder.createAndEncodeEncoder(this.cls.encodedSize);
    905     this.cls.encode(objectEncoder, val);
    906   };
    907 
    908   function NullablePointerTo(cls) {
    909     PointerTo.call(this, cls);
    910   }
    911 
    912   NullablePointerTo.prototype = Object.create(PointerTo.prototype);
    913 
    914   function ArrayOf(cls, length) {
    915     this.cls = cls;
    916     this.length = length || 0;
    917   }
    918 
    919   ArrayOf.prototype.encodedSize = 8;
    920 
    921   ArrayOf.prototype.dimensions = function() {
    922     return [this.length].concat(
    923       (this.cls instanceof ArrayOf) ? this.cls.dimensions() : []);
    924   }
    925 
    926   ArrayOf.prototype.decode = function(decoder) {
    927     return decoder.decodeArrayPointer(this.cls);
    928   };
    929 
    930   ArrayOf.prototype.encode = function(encoder, val) {
    931     encoder.encodeArrayPointer(this.cls, val);
    932   };
    933 
    934   function NullableArrayOf(cls) {
    935     ArrayOf.call(this, cls);
    936   }
    937 
    938   NullableArrayOf.prototype = Object.create(ArrayOf.prototype);
    939 
    940   function Handle() {
    941   }
    942 
    943   Handle.encodedSize = 4;
    944 
    945   Handle.decode = function(decoder) {
    946     return decoder.decodeHandle();
    947   };
    948 
    949   Handle.encode = function(encoder, val) {
    950     encoder.encodeHandle(val);
    951   };
    952 
    953   function NullableHandle() {
    954   }
    955 
    956   NullableHandle.encodedSize = Handle.encodedSize;
    957 
    958   NullableHandle.decode = Handle.decode;
    959 
    960   NullableHandle.encode = Handle.encode;
    961 
    962   function Interface(cls) {
    963     this.cls = cls;
    964   }
    965 
    966   Interface.prototype.encodedSize = 8;
    967 
    968   Interface.prototype.decode = function(decoder) {
    969     var interfacePtrInfo = new mojo.InterfacePtrInfo(
    970         decoder.decodeHandle(), decoder.readUint32());
    971     var interfacePtr = new this.cls();
    972     interfacePtr.ptr.bind(interfacePtrInfo);
    973     return interfacePtr;
    974   };
    975 
    976   Interface.prototype.encode = function(encoder, val) {
    977     var interfacePtrInfo =
    978         val ? val.ptr.passInterface() : new mojo.InterfacePtrInfo(null, 0);
    979     encoder.encodeHandle(interfacePtrInfo.handle);
    980     encoder.writeUint32(interfacePtrInfo.version);
    981   };
    982 
    983   function NullableInterface(cls) {
    984     Interface.call(this, cls);
    985   }
    986 
    987   NullableInterface.prototype = Object.create(Interface.prototype);
    988 
    989   function AssociatedInterfacePtrInfo() {
    990   }
    991 
    992   AssociatedInterfacePtrInfo.prototype.encodedSize = 8;
    993 
    994   AssociatedInterfacePtrInfo.decode = function(decoder) {
    995     return new mojo.AssociatedInterfacePtrInfo(
    996       decoder.decodeAssociatedEndpointHandle(), decoder.readUint32());
    997   };
    998 
    999   AssociatedInterfacePtrInfo.encode = function(encoder, val) {
   1000     var associatedinterfacePtrInfo =
   1001         val ? val : new mojo.AssociatedInterfacePtrInfo(null, 0);
   1002     encoder.encodeAssociatedEndpointHandle(
   1003         associatedinterfacePtrInfo.interfaceEndpointHandle);
   1004     encoder.writeUint32(associatedinterfacePtrInfo.version);
   1005   };
   1006 
   1007   function NullableAssociatedInterfacePtrInfo() {
   1008   }
   1009 
   1010   NullableAssociatedInterfacePtrInfo.encodedSize =
   1011       AssociatedInterfacePtrInfo.encodedSize;
   1012 
   1013   NullableAssociatedInterfacePtrInfo.decode =
   1014       AssociatedInterfacePtrInfo.decode;
   1015 
   1016   NullableAssociatedInterfacePtrInfo.encode =
   1017       AssociatedInterfacePtrInfo.encode;
   1018 
   1019   function InterfaceRequest() {
   1020   }
   1021 
   1022   InterfaceRequest.encodedSize = 4;
   1023 
   1024   InterfaceRequest.decode = function(decoder) {
   1025     return new mojo.InterfaceRequest(decoder.decodeHandle());
   1026   };
   1027 
   1028   InterfaceRequest.encode = function(encoder, val) {
   1029     encoder.encodeHandle(val ? val.handle : null);
   1030   };
   1031 
   1032   function NullableInterfaceRequest() {
   1033   }
   1034 
   1035   NullableInterfaceRequest.encodedSize = InterfaceRequest.encodedSize;
   1036 
   1037   NullableInterfaceRequest.decode = InterfaceRequest.decode;
   1038 
   1039   NullableInterfaceRequest.encode = InterfaceRequest.encode;
   1040 
   1041   function AssociatedInterfaceRequest() {
   1042   }
   1043 
   1044   AssociatedInterfaceRequest.decode = function(decoder) {
   1045     var handle = decoder.decodeAssociatedEndpointHandle();
   1046     return new mojo.AssociatedInterfaceRequest(handle);
   1047   };
   1048 
   1049   AssociatedInterfaceRequest.encode = function(encoder, val) {
   1050     encoder.encodeAssociatedEndpointHandle(
   1051         val ? val.interfaceEndpointHandle : null);
   1052   };
   1053 
   1054   AssociatedInterfaceRequest.encodedSize = 4;
   1055 
   1056   function NullableAssociatedInterfaceRequest() {
   1057   }
   1058 
   1059   NullableAssociatedInterfaceRequest.encodedSize =
   1060       AssociatedInterfaceRequest.encodedSize;
   1061 
   1062   NullableAssociatedInterfaceRequest.decode =
   1063       AssociatedInterfaceRequest.decode;
   1064 
   1065   NullableAssociatedInterfaceRequest.encode =
   1066       AssociatedInterfaceRequest.encode;
   1067 
   1068   function MapOf(keyClass, valueClass) {
   1069     this.keyClass = keyClass;
   1070     this.valueClass = valueClass;
   1071   }
   1072 
   1073   MapOf.prototype.encodedSize = 8;
   1074 
   1075   MapOf.prototype.decode = function(decoder) {
   1076     return decoder.decodeMapPointer(this.keyClass, this.valueClass);
   1077   };
   1078 
   1079   MapOf.prototype.encode = function(encoder, val) {
   1080     encoder.encodeMapPointer(this.keyClass, this.valueClass, val);
   1081   };
   1082 
   1083   function NullableMapOf(keyClass, valueClass) {
   1084     MapOf.call(this, keyClass, valueClass);
   1085   }
   1086 
   1087   NullableMapOf.prototype = Object.create(MapOf.prototype);
   1088 
   1089   internal.align = align;
   1090   internal.isAligned = isAligned;
   1091   internal.Message = Message;
   1092   internal.MessageV0Builder = MessageV0Builder;
   1093   internal.MessageV1Builder = MessageV1Builder;
   1094   internal.MessageV2Builder = MessageV2Builder;
   1095   internal.MessageReader = MessageReader;
   1096   internal.kArrayHeaderSize = kArrayHeaderSize;
   1097   internal.kMapStructPayloadSize = kMapStructPayloadSize;
   1098   internal.kStructHeaderSize = kStructHeaderSize;
   1099   internal.kEncodedInvalidHandleValue = kEncodedInvalidHandleValue;
   1100   internal.kMessageV0HeaderSize = kMessageV0HeaderSize;
   1101   internal.kMessageV1HeaderSize = kMessageV1HeaderSize;
   1102   internal.kMessageV2HeaderSize = kMessageV2HeaderSize;
   1103   internal.kMessagePayloadInterfaceIdsPointerOffset =
   1104       kMessagePayloadInterfaceIdsPointerOffset;
   1105   internal.kMessageExpectsResponse = kMessageExpectsResponse;
   1106   internal.kMessageIsResponse = kMessageIsResponse;
   1107   internal.Int8 = Int8;
   1108   internal.Uint8 = Uint8;
   1109   internal.Int16 = Int16;
   1110   internal.Uint16 = Uint16;
   1111   internal.Int32 = Int32;
   1112   internal.Uint32 = Uint32;
   1113   internal.Int64 = Int64;
   1114   internal.Uint64 = Uint64;
   1115   internal.Float = Float;
   1116   internal.Double = Double;
   1117   internal.String = String;
   1118   internal.Enum = Enum;
   1119   internal.NullableString = NullableString;
   1120   internal.PointerTo = PointerTo;
   1121   internal.NullablePointerTo = NullablePointerTo;
   1122   internal.ArrayOf = ArrayOf;
   1123   internal.NullableArrayOf = NullableArrayOf;
   1124   internal.PackedBool = PackedBool;
   1125   internal.Handle = Handle;
   1126   internal.NullableHandle = NullableHandle;
   1127   internal.Interface = Interface;
   1128   internal.NullableInterface = NullableInterface;
   1129   internal.InterfaceRequest = InterfaceRequest;
   1130   internal.NullableInterfaceRequest = NullableInterfaceRequest;
   1131   internal.AssociatedInterfacePtrInfo = AssociatedInterfacePtrInfo;
   1132   internal.NullableAssociatedInterfacePtrInfo =
   1133       NullableAssociatedInterfacePtrInfo;
   1134   internal.AssociatedInterfaceRequest = AssociatedInterfaceRequest;
   1135   internal.NullableAssociatedInterfaceRequest =
   1136       NullableAssociatedInterfaceRequest;
   1137   internal.MapOf = MapOf;
   1138   internal.NullableMapOf = NullableMapOf;
   1139 })();
   1140