1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20var Int64 = require('node-int64');
21var Thrift = require('./thrift');
22var Type = Thrift.Type;
23var util = require("util");
24
25var Int64Util = require('./int64_util');
26var json_parse = require('./json_parse');
27
28var InputBufferUnderrunError = require('./input_buffer_underrun_error');
29
30module.exports = TJSONProtocol;
31
32/**
33 * Initializes a Thrift JSON protocol instance.
34 * @constructor
35 * @param {Thrift.Transport} trans - The transport to serialize to/from.
36 * @classdesc Apache Thrift Protocols perform serialization which enables cross
37 * language RPC. The Protocol type is the JavaScript browser implementation
38 * of the Apache Thrift TJSONProtocol.
39 * @example
40 *     var protocol  = new Thrift.Protocol(transport);
41 */
42function TJSONProtocol(trans) {
43  this.tstack = [];
44  this.tpos = [];
45  this.trans = trans;
46};
47
48/**
49 * Thrift IDL type Id to string mapping.
50 * @readonly
51 * @see {@link Thrift.Type}
52 */
53TJSONProtocol.Type = {};
54TJSONProtocol.Type[Type.BOOL] = '"tf"';
55TJSONProtocol.Type[Type.BYTE] = '"i8"';
56TJSONProtocol.Type[Type.I16] = '"i16"';
57TJSONProtocol.Type[Type.I32] = '"i32"';
58TJSONProtocol.Type[Type.I64] = '"i64"';
59TJSONProtocol.Type[Type.DOUBLE] = '"dbl"';
60TJSONProtocol.Type[Type.STRUCT] = '"rec"';
61TJSONProtocol.Type[Type.STRING] = '"str"';
62TJSONProtocol.Type[Type.MAP] = '"map"';
63TJSONProtocol.Type[Type.LIST] = '"lst"';
64TJSONProtocol.Type[Type.SET] = '"set"';
65
66/**
67 * Thrift IDL type string to Id mapping.
68 * @readonly
69 * @see {@link Thrift.Type}
70 */
71TJSONProtocol.RType = {};
72TJSONProtocol.RType.tf = Type.BOOL;
73TJSONProtocol.RType.i8 = Type.BYTE;
74TJSONProtocol.RType.i16 = Type.I16;
75TJSONProtocol.RType.i32 = Type.I32;
76TJSONProtocol.RType.i64 = Type.I64;
77TJSONProtocol.RType.dbl = Type.DOUBLE;
78TJSONProtocol.RType.rec = Type.STRUCT;
79TJSONProtocol.RType.str = Type.STRING;
80TJSONProtocol.RType.map = Type.MAP;
81TJSONProtocol.RType.lst = Type.LIST;
82TJSONProtocol.RType.set = Type.SET;
83
84/**
85 * The TJSONProtocol version number.
86 * @readonly
87 * @const {number} Version
88 * @memberof Thrift.Protocol
89 */
90TJSONProtocol.Version = 1;
91
92TJSONProtocol.prototype.flush = function() {
93  this.writeToTransportIfStackIsFlushable();
94  return this.trans.flush();
95};
96
97TJSONProtocol.prototype.writeToTransportIfStackIsFlushable = function() {
98  if (this.tstack.length === 1) {
99    this.trans.write(this.tstack.pop());
100  }
101};
102
103/**
104 * Serializes the beginning of a Thrift RPC message.
105 * @param {string} name - The service method to call.
106 * @param {Thrift.MessageType} messageType - The type of method call.
107 * @param {number} seqid - The sequence number of this call (always 0 in Apache Thrift).
108 */
109TJSONProtocol.prototype.writeMessageBegin = function(name, messageType, seqid) {
110  this.tstack.push([TJSONProtocol.Version, '"' + name + '"', messageType, seqid]);
111};
112
113/**
114 * Serializes the end of a Thrift RPC message.
115 */
116TJSONProtocol.prototype.writeMessageEnd = function() {
117  var obj = this.tstack.pop();
118
119  this.wobj = this.tstack.pop();
120  this.wobj.push(obj);
121
122  this.wbuf = '[' + this.wobj.join(',') + ']';
123
124  // we assume there is nothing more to come so we write
125  this.trans.write(this.wbuf);
126};
127
128/**
129 * Serializes the beginning of a struct.
130 * @param {string} name - The name of the struct.
131 */
132TJSONProtocol.prototype.writeStructBegin = function(name) {
133  this.tpos.push(this.tstack.length);
134  this.tstack.push({});
135};
136
137/**
138 * Serializes the end of a struct.
139 */
140TJSONProtocol.prototype.writeStructEnd = function() {
141  var p = this.tpos.pop();
142  var struct = this.tstack[p];
143  var str = '{';
144  var first = true;
145  for (var key in struct) {
146    if (first) {
147      first = false;
148    } else {
149      str += ',';
150    }
151
152    str += key + ':' + struct[key];
153  }
154
155  str += '}';
156  this.tstack[p] = str;
157
158  this.writeToTransportIfStackIsFlushable();
159};
160
161/**
162 * Serializes the beginning of a struct field.
163 * @param {string} name - The name of the field.
164 * @param {Thrift.Protocol.Type} fieldType - The data type of the field.
165 * @param {number} fieldId - The field's unique identifier.
166 */
167TJSONProtocol.prototype.writeFieldBegin = function(name, fieldType, fieldId) {
168  this.tpos.push(this.tstack.length);
169  this.tstack.push({ 'fieldId': '"' +
170    fieldId + '"', 'fieldType': TJSONProtocol.Type[fieldType]
171  });
172};
173
174/**
175 * Serializes the end of a field.
176 */
177TJSONProtocol.prototype.writeFieldEnd = function() {
178  var value = this.tstack.pop();
179  var fieldInfo = this.tstack.pop();
180
181  if (':' + value === ":[object Object]") {
182    this.tstack[this.tstack.length - 1][fieldInfo.fieldId] = '{' +
183      fieldInfo.fieldType + ':' + JSON.stringify(value) + '}';
184  } else {
185    this.tstack[this.tstack.length - 1][fieldInfo.fieldId] = '{' +
186      fieldInfo.fieldType + ':' + value + '}';
187  }
188  this.tpos.pop();
189
190  this.writeToTransportIfStackIsFlushable();
191};
192
193/**
194 * Serializes the end of the set of fields for a struct.
195 */
196TJSONProtocol.prototype.writeFieldStop = function() {
197};
198
199/**
200 * Serializes the beginning of a map collection.
201 * @param {Thrift.Type} keyType - The data type of the key.
202 * @param {Thrift.Type} valType - The data type of the value.
203 * @param {number} [size] - The number of elements in the map (ignored).
204 */
205TJSONProtocol.prototype.writeMapBegin = function(keyType, valType, size) {
206  //size is invalid, we'll set it on end.
207  this.tpos.push(this.tstack.length);
208  this.tstack.push([TJSONProtocol.Type[keyType], TJSONProtocol.Type[valType], 0]);
209};
210
211/**
212 * Serializes the end of a map.
213 */
214TJSONProtocol.prototype.writeMapEnd = function() {
215  var p = this.tpos.pop();
216
217  if (p == this.tstack.length) {
218    return;
219  }
220
221  if ((this.tstack.length - p - 1) % 2 !== 0) {
222    this.tstack.push('');
223  }
224
225  var size = (this.tstack.length - p - 1) / 2;
226
227  this.tstack[p][this.tstack[p].length - 1] = size;
228
229  var map = '}';
230  var first = true;
231  while (this.tstack.length > p + 1) {
232    var v = this.tstack.pop();
233    var k = this.tstack.pop();
234    if (first) {
235      first = false;
236    } else {
237      map = ',' + map;
238    }
239
240    if (! isNaN(k)) { k = '"' + k + '"'; } //json "keys" need to be strings
241    map = k + ':' + v + map;
242  }
243  map = '{' + map;
244
245  this.tstack[p].push(map);
246  this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
247
248  this.writeToTransportIfStackIsFlushable();
249};
250
251/**
252 * Serializes the beginning of a list collection.
253 * @param {Thrift.Type} elemType - The data type of the elements.
254 * @param {number} size - The number of elements in the list.
255 */
256TJSONProtocol.prototype.writeListBegin = function(elemType, size) {
257  this.tpos.push(this.tstack.length);
258  this.tstack.push([TJSONProtocol.Type[elemType], size]);
259};
260
261/**
262 * Serializes the end of a list.
263 */
264TJSONProtocol.prototype.writeListEnd = function() {
265  var p = this.tpos.pop();
266
267  while (this.tstack.length > p + 1) {
268    var tmpVal = this.tstack[p + 1];
269    this.tstack.splice(p + 1, 1);
270    this.tstack[p].push(tmpVal);
271  }
272
273  this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
274
275  this.writeToTransportIfStackIsFlushable();
276};
277
278/**
279 * Serializes the beginning of a set collection.
280 * @param {Thrift.Type} elemType - The data type of the elements.
281 * @param {number} size - The number of elements in the list.
282 */
283TJSONProtocol.prototype.writeSetBegin = function(elemType, size) {
284    this.tpos.push(this.tstack.length);
285    this.tstack.push([TJSONProtocol.Type[elemType], size]);
286};
287
288/**
289 * Serializes the end of a set.
290 */
291TJSONProtocol.prototype.writeSetEnd = function() {
292  var p = this.tpos.pop();
293
294  while (this.tstack.length > p + 1) {
295    var tmpVal = this.tstack[p + 1];
296    this.tstack.splice(p + 1, 1);
297    this.tstack[p].push(tmpVal);
298  }
299
300  this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
301
302  this.writeToTransportIfStackIsFlushable();
303};
304
305/** Serializes a boolean */
306TJSONProtocol.prototype.writeBool = function(bool) {
307  this.tstack.push(bool ? 1 : 0);
308};
309
310/** Serializes a number */
311TJSONProtocol.prototype.writeByte = function(byte) {
312  this.tstack.push(byte);
313};
314
315/** Serializes a number */
316TJSONProtocol.prototype.writeI16 = function(i16) {
317  this.tstack.push(i16);
318};
319
320/** Serializes a number */
321TJSONProtocol.prototype.writeI32 = function(i32) {
322  this.tstack.push(i32);
323};
324
325/** Serializes a number */
326TJSONProtocol.prototype.writeI64 = function(i64) {
327  if (i64 instanceof Int64) {
328    this.tstack.push(Int64Util.toDecimalString(i64));
329  } else {
330    this.tstack.push(i64);
331  }
332};
333
334/** Serializes a number */
335TJSONProtocol.prototype.writeDouble = function(dub) {
336  this.tstack.push(dub);
337};
338
339/** Serializes a string */
340TJSONProtocol.prototype.writeString = function(arg) {
341  // We do not encode uri components for wire transfer:
342  if (arg === null) {
343      this.tstack.push(null);
344  } else {
345      if (typeof arg === 'string') {
346        var str = arg;
347      } else if (arg instanceof Buffer) {
348        var str = arg.toString('utf8');
349      } else {
350        throw new Error('writeString called without a string/Buffer argument: ' + arg);
351      }
352
353      // concat may be slower than building a byte buffer
354      var escapedString = '';
355      for (var i = 0; i < str.length; i++) {
356          var ch = str.charAt(i);      // a single double quote: "
357          if (ch === '\"') {
358              escapedString += '\\\"'; // write out as: \"
359          } else if (ch === '\\') {    // a single backslash: \
360              escapedString += '\\\\'; // write out as: \\
361          /* Currently escaped forward slashes break TJSONProtocol.
362           * As it stands, we can simply pass forward slashes into
363           * our strings across the wire without being escaped.
364           * I think this is the protocol's bug, not thrift.js
365           * } else if(ch === '/') {   // a single forward slash: /
366           *  escapedString += '\\/';  // write out as \/
367           * }
368           */
369          } else if (ch === '\b') {    // a single backspace: invisible
370              escapedString += '\\b';  // write out as: \b"
371          } else if (ch === '\f') {    // a single formfeed: invisible
372              escapedString += '\\f';  // write out as: \f"
373          } else if (ch === '\n') {    // a single newline: invisible
374              escapedString += '\\n';  // write out as: \n"
375          } else if (ch === '\r') {    // a single return: invisible
376              escapedString += '\\r';  // write out as: \r"
377          } else if (ch === '\t') {    // a single tab: invisible
378              escapedString += '\\t';  // write out as: \t"
379          } else {
380              escapedString += ch;     // Else it need not be escaped
381          }
382      }
383      this.tstack.push('"' + escapedString + '"');
384  }
385};
386
387/** Serializes a string */
388TJSONProtocol.prototype.writeBinary = function(arg) {
389  if (typeof arg === 'string') {
390    var buf = new Buffer(arg, 'binary');
391  } else if (arg instanceof Buffer ||
392             Object.prototype.toString.call(arg) == '[object Uint8Array]')  {
393    var buf = arg;
394  } else {
395    throw new Error('writeBinary called without a string/Buffer argument: ' + arg);
396  }
397  this.tstack.push('"' + buf.toString('base64') + '"');
398};
399
400/**
401 * @class
402 * @name AnonReadMessageBeginReturn
403 * @property {string} fname - The name of the service method.
404 * @property {Thrift.MessageType} mtype - The type of message call.
405 * @property {number} rseqid - The sequence number of the message (0 in Thrift RPC).
406 */
407/**
408 * Deserializes the beginning of a message.
409 * @returns {AnonReadMessageBeginReturn}
410 */
411TJSONProtocol.prototype.readMessageBegin = function() {
412  this.rstack = [];
413  this.rpos = [];
414
415  //Borrow the inbound transport buffer and ensure data is present/consistent
416  var transBuf = this.trans.borrow();
417  if (transBuf.readIndex >= transBuf.writeIndex) {
418    throw new InputBufferUnderrunError();
419  }
420  var cursor = transBuf.readIndex;
421
422  if (transBuf.buf[cursor] !== 0x5B) { //[
423    throw new Error("Malformed JSON input, no opening bracket");
424  }
425
426  //Parse a single message (there may be several in the buffer)
427  //  TODO: Handle characters using multiple code units
428  cursor++;
429  var openBracketCount = 1;
430  var inString = false;
431  for (; cursor < transBuf.writeIndex; cursor++) {
432    var chr = transBuf.buf[cursor];
433    //we use hexa charcode here because data[i] returns an int and not a char
434    if (inString) {
435      if (chr === 0x22) { //"
436        inString = false;
437      } else if (chr === 0x5C) { //\
438        //escaped character, skip
439        cursor += 1;
440      }
441    } else {
442      if (chr === 0x5B) { //[
443        openBracketCount += 1;
444      } else if (chr === 0x5D) { //]
445        openBracketCount -= 1;
446        if (openBracketCount === 0) {
447          //end of json message detected
448          break;
449        }
450      } else if (chr === 0x22) { //"
451        inString = true;
452      }
453    }
454  }
455
456  if (openBracketCount !== 0) {
457    // Missing closing bracket. Can be buffer underrun.
458    throw new InputBufferUnderrunError();
459  }
460
461  //Reconstitute the JSON object and conume the necessary bytes
462  this.robj = json_parse(transBuf.buf.slice(transBuf.readIndex, cursor+1).toString());
463  this.trans.consume(cursor + 1 - transBuf.readIndex);
464
465  //Verify the protocol version
466  var version = this.robj.shift();
467  if (version != TJSONProtocol.Version) {
468    throw new Error('Wrong thrift protocol version: ' + version);
469  }
470
471  //Objectify the thrift message {name/type/sequence-number} for return
472  // and then save the JSON object in rstack
473  var r = {};
474  r.fname = this.robj.shift();
475  r.mtype = this.robj.shift();
476  r.rseqid = this.robj.shift();
477  this.rstack.push(this.robj.shift());
478  return r;
479};
480
481/** Deserializes the end of a message. */
482TJSONProtocol.prototype.readMessageEnd = function() {
483};
484
485/**
486 * Deserializes the beginning of a struct.
487 * @param {string} [name] - The name of the struct (ignored)
488 * @returns {object} - An object with an empty string fname property
489 */
490TJSONProtocol.prototype.readStructBegin = function() {
491  var r = {};
492  r.fname = '';
493
494  //incase this is an array of structs
495  if (this.rstack[this.rstack.length - 1] instanceof Array) {
496    this.rstack.push(this.rstack[this.rstack.length - 1].shift());
497  }
498
499  return r;
500};
501
502/** Deserializes the end of a struct. */
503TJSONProtocol.prototype.readStructEnd = function() {
504  this.rstack.pop();
505};
506
507/**
508 * @class
509 * @name AnonReadFieldBeginReturn
510 * @property {string} fname - The name of the field (always '').
511 * @property {Thrift.Type} ftype - The data type of the field.
512 * @property {number} fid - The unique identifier of the field.
513 */
514/**
515 * Deserializes the beginning of a field.
516 * @returns {AnonReadFieldBeginReturn}
517 */
518TJSONProtocol.prototype.readFieldBegin = function() {
519  var r = {};
520
521  var fid = -1;
522  var ftype = Type.STOP;
523
524  //get a fieldId
525  for (var f in (this.rstack[this.rstack.length - 1])) {
526    if (f === null) {
527      continue;
528    }
529
530    fid = parseInt(f, 10);
531    this.rpos.push(this.rstack.length);
532
533    var field = this.rstack[this.rstack.length - 1][fid];
534
535    //remove so we don't see it again
536    delete this.rstack[this.rstack.length - 1][fid];
537
538    this.rstack.push(field);
539
540    break;
541  }
542
543  if (fid != -1) {
544    //should only be 1 of these but this is the only
545    //way to match a key
546    for (var i in (this.rstack[this.rstack.length - 1])) {
547      if (TJSONProtocol.RType[i] === null) {
548        continue;
549      }
550
551      ftype = TJSONProtocol.RType[i];
552      this.rstack[this.rstack.length - 1] = this.rstack[this.rstack.length - 1][i];
553    }
554  }
555
556  r.fname = '';
557  r.ftype = ftype;
558  r.fid = fid;
559
560  return r;
561};
562
563/** Deserializes the end of a field. */
564TJSONProtocol.prototype.readFieldEnd = function() {
565  var pos = this.rpos.pop();
566
567  //get back to the right place in the stack
568  while (this.rstack.length > pos) {
569    this.rstack.pop();
570  }
571};
572
573/**
574 * @class
575 * @name AnonReadMapBeginReturn
576 * @property {Thrift.Type} ktype - The data type of the key.
577 * @property {Thrift.Type} vtype - The data type of the value.
578 * @property {number} size - The number of elements in the map.
579 */
580/**
581 * Deserializes the beginning of a map.
582 * @returns {AnonReadMapBeginReturn}
583 */
584TJSONProtocol.prototype.readMapBegin = function() {
585  var map = this.rstack.pop();
586  var first = map.shift();
587  if (first instanceof Array) {
588    this.rstack.push(map);
589    map = first;
590    first = map.shift();
591  }
592
593  var r = {};
594  r.ktype = TJSONProtocol.RType[first];
595  r.vtype = TJSONProtocol.RType[map.shift()];
596  r.size = map.shift();
597
598
599  this.rpos.push(this.rstack.length);
600  this.rstack.push(map.shift());
601
602  return r;
603};
604
605/** Deserializes the end of a map. */
606TJSONProtocol.prototype.readMapEnd = function() {
607  this.readFieldEnd();
608};
609
610/**
611 * @class
612 * @name AnonReadColBeginReturn
613 * @property {Thrift.Type} etype - The data type of the element.
614 * @property {number} size - The number of elements in the collection.
615 */
616/**
617 * Deserializes the beginning of a list.
618 * @returns {AnonReadColBeginReturn}
619 */
620TJSONProtocol.prototype.readListBegin = function() {
621  var list = this.rstack[this.rstack.length - 1];
622
623  var r = {};
624  r.etype = TJSONProtocol.RType[list.shift()];
625  r.size = list.shift();
626
627  this.rpos.push(this.rstack.length);
628  this.rstack.push(list.shift());
629
630  return r;
631};
632
633/** Deserializes the end of a list. */
634TJSONProtocol.prototype.readListEnd = function() {
635  var pos = this.rpos.pop() - 2;
636  var st = this.rstack;
637  st.pop();
638  if (st instanceof Array && st.length > pos && st[pos].length > 0) {
639    st.push(st[pos].shift());
640  }
641};
642
643/**
644 * Deserializes the beginning of a set.
645 * @returns {AnonReadColBeginReturn}
646 */
647TJSONProtocol.prototype.readSetBegin = function() {
648  return this.readListBegin();
649};
650
651/** Deserializes the end of a set. */
652TJSONProtocol.prototype.readSetEnd = function() {
653  return this.readListEnd();
654};
655
656TJSONProtocol.prototype.readBool = function() {
657  return this.readValue() == '1';
658};
659
660TJSONProtocol.prototype.readByte = function() {
661  return this.readI32();
662};
663
664TJSONProtocol.prototype.readI16 = function() {
665  return this.readI32();
666};
667
668TJSONProtocol.prototype.readI32 = function(f) {
669  return +this.readValue();
670}
671
672/** Returns the next value found in the protocol buffer */
673TJSONProtocol.prototype.readValue = function(f) {
674  if (f === undefined) {
675    f = this.rstack[this.rstack.length - 1];
676  }
677
678  var r = {};
679
680  if (f instanceof Array) {
681    if (f.length === 0) {
682      r.value = undefined;
683    } else {
684      r.value = f.shift();
685    }
686  } else if (!(f instanceof Int64) && f instanceof Object) {
687    for (var i in f) {
688      if (i === null) {
689        continue;
690      }
691      this.rstack.push(f[i]);
692      delete f[i];
693
694      r.value = i;
695      break;
696    }
697  } else {
698    r.value = f;
699    this.rstack.pop();
700  }
701
702  return r.value;
703};
704
705TJSONProtocol.prototype.readI64 = function() {
706  var n = this.readValue()
707  if (typeof n === 'string') {
708    // Assuming no one is sending in 1.11111e+33 format
709    return Int64Util.fromDecimalString(n);
710  } else {
711    return new Int64(n);
712  }
713};
714
715TJSONProtocol.prototype.readDouble = function() {
716  return this.readI32();
717};
718
719TJSONProtocol.prototype.readBinary = function() {
720  return new Buffer(this.readValue(), 'base64');
721};
722
723TJSONProtocol.prototype.readString = function() {
724  return this.readValue();
725};
726
727/**
728 * Returns the underlying transport.
729 * @readonly
730 * @returns {Thrift.Transport} The underlying transport.
731 */
732TJSONProtocol.prototype.getTransport = function() {
733  return this.trans;
734};
735
736/**
737 * Method to arbitrarily skip over data
738 */
739TJSONProtocol.prototype.skip = function(type) {
740    switch (type) {
741    case Type.BOOL:
742      this.readBool();
743      break;
744    case Type.BYTE:
745      this.readByte();
746      break;
747    case Type.I16:
748      this.readI16();
749      break;
750    case Type.I32:
751      this.readI32();
752      break;
753    case Type.I64:
754      this.readI64();
755      break;
756    case Type.DOUBLE:
757      this.readDouble();
758      break;
759    case Type.STRING:
760      this.readString();
761      break;
762    case Type.STRUCT:
763      this.readStructBegin();
764      while (true) {
765        var r = this.readFieldBegin();
766        if (r.ftype === Type.STOP) {
767          break;
768        }
769        this.skip(r.ftype);
770        this.readFieldEnd();
771      }
772      this.readStructEnd();
773      break;
774    case Type.MAP:
775      var mapBegin = this.readMapBegin();
776      for (var i = 0; i < mapBegin.size; ++i) {
777        this.skip(mapBegin.ktype);
778        this.skip(mapBegin.vtype);
779      }
780      this.readMapEnd();
781      break;
782    case Type.SET:
783      var setBegin = this.readSetBegin();
784      for (var i2 = 0; i2 < setBegin.size; ++i2) {
785        this.skip(setBegin.etype);
786      }
787      this.readSetEnd();
788      break;
789    case Type.LIST:
790      var listBegin = this.readListBegin();
791      for (var i3 = 0; i3 < listBegin.size; ++i3) {
792        this.skip(listBegin.etype);
793      }
794      this.readListEnd();
795      break;
796    default:
797      throw new  Error("Invalid type: " + type);
798  }
799};
800