diff options
Diffstat (limited to 'js/src/Ice/BasicStream.js')
-rw-r--r-- | js/src/Ice/BasicStream.js | 2961 |
1 files changed, 2961 insertions, 0 deletions
diff --git a/js/src/Ice/BasicStream.js b/js/src/Ice/BasicStream.js new file mode 100644 index 00000000000..db423be4c91 --- /dev/null +++ b/js/src/Ice/BasicStream.js @@ -0,0 +1,2961 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Debug"); + require("Ice/ExUtil"); + require("Ice/FormatType"); + require("Ice/HashMap"); + require("Ice/Object"); + require("Ice/OptionalFormat"); + require("Ice/Protocol"); + require("Ice/TraceUtil"); + require("Ice/Buffer"); + require("Ice/Exception"); + require("Ice/LocalException"); + require("Ice/Version"); + require("Ice/CompactIdRegistry"); + require("Ice/ArrayUtil"); + require("Ice/UnknownSlicedObject"); + + var Ice = global.Ice || {}; + + var Debug = Ice.Debug; + var ExUtil = Ice.ExUtil; + var FormatType = Ice.FormatType; + var HashMap = Ice.HashMap; + var IceObject = Ice.Object; + var OptionalFormat = Ice.OptionalFormat; + var Protocol = Ice.Protocol; + var TraceUtil = Ice.TraceUtil; + var ArrayUtil = Ice.ArrayUtil; + var SlicedData = Ice.SlicedData; + + var SliceType = {}; + SliceType.NoSlice = 0; + SliceType.ObjectSlice = 1; + SliceType.ExceptionSlice = 2; + + var OPTIONAL_END_MARKER = 0xFF; + var FLAG_HAS_TYPE_ID_STRING = (1<<0); + var FLAG_HAS_TYPE_ID_INDEX = (1<<1); + var FLAG_HAS_TYPE_ID_COMPACT = (1<<1 | 1<<0); + var FLAG_HAS_OPTIONAL_MEMBERS = (1<<2); + var FLAG_HAS_INDIRECTION_TABLE = (1<<3); + var FLAG_HAS_SLICE_SIZE = (1<<4); + var FLAG_IS_LAST_SLICE = (1<<5); + + var IndirectPatchEntry = function(index, patcher) + { + this.index = index; + this.patcher = patcher; + }; + + var Class = Ice.Class; + + var EncapsDecoder = Class({ + __init__: function(stream, encaps, sliceObjects, f) + { + this._stream = stream; + this._encaps = encaps; + this._sliceObjects = sliceObjects; + this._servantFactoryManager = f; + this._patchMap = null; // Lazy initialized, HashMap<int, Patcher[] >() + this._unmarshaledMap = new HashMap(); // HashMap<int, Ice.Object>() + this._typeIdMap = null; // Lazy initialized, HashMap<int, String> + this._typeIdIndex = 0; + this._objectList = null; // Lazy initialized. Ice.Object[] + }, + readOpt: function() + { + return false; + }, + readPendingObjects: function() + { + }, + readTypeId: function(isIndex) + { + var typeId, index; + if(this._typeIdMap === null) // Lazy initialization + { + this._typeIdMap = new HashMap(); // Map<int, String>(); + } + + if(isIndex) + { + index = this._stream.readSize(); + typeId = this._typeIdMap.get(index); + if(typeId === undefined) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + else + { + typeId = this._stream.readString(); + this._typeIdMap.set(++this._typeIdIndex, typeId); + } + return typeId; + }, + newInstance: function(typeId) + { + // + // Try to find a factory registered for the specific type. + // + var userFactory = this._servantFactoryManager.find(typeId); + var v = null; + + if(userFactory !== undefined) + { + v = userFactory.create(typeId); + } + + // + // If that fails, invoke the default factory if one has been + // registered. + // + if(v === null || v === undefined) + { + userFactory = this._servantFactoryManager.find(""); + if(userFactory !== undefined) + { + v = userFactory.create(typeId); + } + } + + // + // Last chance: try to instantiate the class dynamically. + // + if(v === null || v === undefined) + { + v = this._stream.createObject(typeId); + } + + return v; + }, + addPatchEntry: function(index, patcher) + { + Debug.assert(index > 0); + // + // Check if already un-marshalled the object. If that's the case, + // just patch the object smart pointer and we're done. + // + var obj = this._unmarshaledMap.get(index); + if(obj !== undefined && obj !== null) + { + patcher.call(null, obj); + return; + } + + if(this._patchMap === null) // Lazy initialization + { + this._patchMap = new HashMap(); // HashMap<Integer, Patcher[] >(); + } + + // + // Add patch entry if the object isn't un-marshalled yet, + // the smart pointer will be patched when the instance is + // un-marshalled. + // + var l = this._patchMap.get(index); + if(l === undefined) + { + // + // We have no outstanding instances to be patched for this + // index, so make a new entry in the patch map. + // + l = []; // Patcher[]; + this._patchMap.set(index, l); + } + + // + // Append a patch entry for this instance. + // + l.push(patcher); + }, + unmarshal: function(index, v) + { + var i, length, l; + // + // Add the object to the map of un-marshalled objects, this must + // be done before reading the objects (for circular references). + // + this._unmarshaledMap.set(index, v); + + // + // Read the object. + // + v.__read(this._stream); + if(this._patchMap !== null) + { + // + // Patch all instances now that the object is un-marshalled. + // + l = this._patchMap.get(index); + if(l !== undefined) + { + Debug.assert(l.length > 0); + // + // Patch all pointers that refer to the instance. + // + for(i = 0, length = l.length; i < length; ++i) + { + l[i](v); + } + // + // Clear out the patch map for that index -- there is nothing left + // to patch for that index for the time being. + // + this._patchMap.delete(index); + } + } + + if((this._patchMap === null || this._patchMap.size === 0) && this._objectList === null) + { + try + { + v.ice_postUnmarshal(); + } + catch(ex) + { + this._stream.instance.initializationData().logger.warning("exception raised by ice_postUnmarshal:\n" + + ExUtil.toString(ex)); + } + } + else + { + if(this._objectList === null) // Lazy initialization + { + this._objectList = []; // Ice.Object[] + } + this._objectList.push(v); + + if(this._patchMap === null || this._patchMap.size === 0) + { + // + // Iterate over the object list and invoke ice_postUnmarshal on + // each object. We must do this after all objects have been + // unmarshaled in order to ensure that any object data members + // have been properly patched. + // + for(i = 0, length = this._objectList.length; i < length; i++) + { + try + { + this._objectList[i].ice_postUnmarshal(); + } + catch(ex) + { + this._stream.instance.initializationData().logger.warning( + "exception raised by ice_postUnmarshal:\n" + ExUtil.toString(ex)); + } + } + this._objectList = []; + } + } + } + }); + + var EncapsDecoder10 = Class(EncapsDecoder, { + __init__: function(stream, encaps, sliceObjects, f) + { + EncapsDecoder.call(this, stream, encaps, sliceObjects, f); + this._sliceType = SliceType.NoSlice; + }, + readObject: function(patcher) + { + Debug.assert(patcher !== null); + + // + // Object references are encoded as a negative integer in 1.0. + // + var index = this._stream.readInt(); + if(index > 0) + { + throw new Ice.MarshalException("invalid object id"); + } + index = -index; + + if(index === 0) + { + patcher.call(null, null); + } + else + { + this.addPatchEntry(index, patcher); + } + }, + throwException: function() + { + Debug.assert(this._sliceType === SliceType.NoSlice); + + // + // User exception with the 1.0 encoding start with a boolean flag + // that indicates whether or not the exception has classes. + // + // This allows reading the pending objects even if some part of + // the exception was sliced. + // + var usesClasses = this._stream.readBool(); + this._sliceType = SliceType.ExceptionSlice; + this._skipFirstSlice = false; + + // + // Read the first slice header. + // + this.startSlice(); + var mostDerivedId = this._typeId; + while(true) + { + var userEx = this._stream.createUserException(this._typeId); + + // + // We found the exception. + // + if(userEx !== null) + { + userEx.__read(this._stream); + if(usesClasses) + { + this.readPendingObjects(); + } + throw userEx; + + // Never reached. + } + + // + // Slice off what we don't understand. + // + this.skipSlice(); + try + { + this.startSlice(); + } + catch(ex) + { + // + // An oversight in the 1.0 encoding means there is no marker to indicate + // the last slice of an exception. As a result, we just try to read the + // next type ID, which raises UnmarshalOutOfBoundsException when the + // input buffer underflows. + // + // Set the reason member to a more helpful message. + // + if(ex instanceof Ice.UnmarshalOutOfBoundsException) + { + ex.reason = "unknown exception type `" + mostDerivedId + "'"; + } + throw ex; + } + } + }, + startInstance: function(sliceType) + { + Debug.assert(this._sliceType === sliceType); + this._skipFirstSlice = true; + }, + endInstance: function(/*preserve*/) + { + var sz; + // + // Read the Ice::Object slice. + // + if(this._sliceType === SliceType.ObjectSlice) + { + this.startSlice(); + sz = this._stream.readSize(); // For compatibility with the old AFM. + if(sz !== 0) + { + throw new Ice.MarshalException("invalid Object slice"); + } + this.endSlice(); + } + + this._sliceType = SliceType.NoSlice; + return null; + }, + startSlice: function() + { + var isIndex; + // + // If first slice, don't read the header, it was already read in + // readInstance or throwException to find the factory. + // + if(this._skipFirstSlice) + { + this._skipFirstSlice = false; + return this._typeId; + } + + // + // For objects, first read the type ID boolean which indicates + // whether or not the type ID is encoded as a string or as an + // index. For exceptions, the type ID is always encoded as a + // string. + // + if(this._sliceType === SliceType.ObjectSlice) // For exceptions, the type ID is always encoded as a string + { + isIndex = this._stream.readBool(); + this._typeId = this.readTypeId(isIndex); + } + else + { + this._typeId = this._stream.readString(); + } + + this._sliceSize = this._stream.readInt(); + if(this._sliceSize < 4) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + return this._typeId; + }, + endSlice: function() + { + }, + skipSlice: function() + { + if(this._stream.instance.traceLevels().slicing > 0) + { + var logger = this._stream.instance.initializationData().logger; + if(this._sliceType === SliceType.ObjectSlice) + { + TraceUtil.traceSlicing("object", this._typeId, this._stream.instance.traceLevels().slicingCat, logger); + } + else + { + TraceUtil.traceSlicing("exception", this._typeId, this._stream.instance.traceLevels().slicingCat, logger); + } + } + Debug.assert(this._sliceSize >= 4); + this._stream.skip(this._sliceSize - 4); + }, + readPendingObjects: function() + { + var k, num; + do + { + num = this._stream.readSize(); + for(k = num; k > 0; --k) + { + this.readInstance(); + } + } + while(num > 0); + + if(this._patchMap !== null && this._patchMap.size !== 0) + { + // + // If any entries remain in the patch map, the sender has sent an index for an object, but failed + // to supply the object. + // + throw new Ice.MarshalException("index for class received, but no instance"); + } + }, + readInstance: function() + { + var index = this._stream.readInt(), + mostDerivedId, + v = null; + + if(index <= 0) + { + throw new Ice.MarshalException("invalid object id"); + } + + this._sliceType = SliceType.ObjectSlice; + this._skipFirstSlice = false; + + // + // Read the first slice header. + // + this.startSlice(); + mostDerivedId = this._typeId; + while(true) + { + // + // For the 1.0 encoding, the type ID for the base Object class + // marks the last slice. + // + if(this._typeId == IceObject.ice_staticId()) + { + throw new Ice.NoObjectFactoryException("", mostDerivedId); + } + + v = this.newInstance(this._typeId); + + // + // We found a factory, we get out of this loop. + // + if(v) + { + break; + } + + // + // If object slicing is disabled, stop un-marshalling. + // + if(!this._sliceObjects) + { + throw new Ice.NoObjectFactoryException("object slicing is disabled", this._typeId); + } + + // + // Slice off what we don't understand. + // + this.skipSlice(); + this.startSlice(); // Read next Slice header for next iteration. + } + + // + // Un-marshal the object and add-it to the map of un-marshaled objects. + // + this.unmarshal(index, v); + } + }); + + var EncapsDecoder11 = Class(EncapsDecoder, { + __init__: function(stream, encaps, sliceObjects, f) + { + EncapsDecoder.call(this, stream, encaps, sliceObjects, f); + this._current = null; + this._objectIdIndex = 1; + }, + readObject: function(patcher) + { + Debug.assert(patcher !== undefined); + var index = this._stream.readSize(); + + if(index < 0) + { + throw new Ice.MarshalException("invalid object id"); + } + + if(index === 0) + { + if(patcher !== null) + { + patcher.call(null, null); + } + } + else if(this._current !== null && (this._current.sliceFlags & FLAG_HAS_INDIRECTION_TABLE) !== 0) + { + // + // When reading an object within a slice and there's an + // indirect object table, always read an indirect reference + // that points to an object from the indirect object table + // marshaled at the end of the Slice. + // + // Maintain a list of indirect references. Note that the + // indirect index starts at 1, so we decrement it by one to + // derive an index into the indirection table that we'll read + // at the end of the slice. + // + if(patcher !== null) + { + if(this._current.indirectPatchList === null) // Lazy initialization + { + this._current.indirectPatchList = []; // IndirectPatchEntry[] + } + var e = new IndirectPatchEntry(); + e.index = index - 1; + e.patcher = patcher; + this._current.indirectPatchList.push(e); + } + } + else + { + this.readInstance(index, patcher); + } + }, + throwException: function() + { + Debug.assert(this._current === null); + this.push(SliceType.ExceptionSlice); + + // + // Read the first slice header. + // + this.startSlice(); + var mostDerivedId = this._current.typeId; + while(true) + { + + var userEx = this._stream.createUserException(this._current.typeId); + + // + // We found the exception. + // + if(userEx !== null) + { + userEx.__read(this._stream); + throw userEx; + + // Never reached. + } + + // + // Slice off what we don't understand. + // + this.skipSlice(); + + if((this._current.sliceFlags & FLAG_IS_LAST_SLICE) !== 0) + { + if(mostDerivedId.indexOf("::") === 0) + { + throw new Ice.UnknownUserException(mostDerivedId.substr(2)); + } + throw new Ice.UnknownUserException(mostDerivedId); + } + + this.startSlice(); + } + }, + startInstance: function(sliceType) + { + Debug.assert(sliceType !== undefined); + Debug.assert(this._current.sliceType !== null && this._current.sliceType === sliceType); + this._current.skipFirstSlice = true; + }, + endInstance: function(preserve) + { + var slicedData = null; + if(preserve) + { + slicedData = this.readSlicedData(); + } + if(this._current.slices !== null) + { + this._current.slices.length = 0; // Clear the array. + this._current.indirectionTables.length = 0; // Clear the array. + } + this._current = this._current.previous; + return slicedData; + }, + startSlice: function() + { + // + // If first slice, don't read the header, it was already read in + // readInstance or throwException to find the factory. + // + if(this._current.skipFirstSlice) + { + this._current.skipFirstSlice = false; + return this._current.typeId; + } + + this._current.sliceFlags = this._stream.readByte(); + + // + // Read the type ID, for object slices the type ID is encoded as a + // string or as an index, for exceptions it's always encoded as a + // string. + // + if(this._current.sliceType === SliceType.ObjectSlice) + { + if((this._current.sliceFlags & FLAG_HAS_TYPE_ID_COMPACT) === FLAG_HAS_TYPE_ID_COMPACT) // Must be checked 1st! + { + this._current.typeId = ""; + this._current.compactId = this._stream.readSize(); + } + else if((this._current.sliceFlags & (FLAG_HAS_TYPE_ID_INDEX | FLAG_HAS_TYPE_ID_STRING)) !== 0) + { + this._current.typeId = this.readTypeId((this._current.sliceFlags & FLAG_HAS_TYPE_ID_INDEX) !== 0); + this._current.compactId = -1; + } + else + { + // Only the most derived slice encodes the type ID for the compact format. + this._current.typeId = ""; + this._current.compactId = -1; + } + } + else + { + this._current.typeId = this._stream.readString(); + this._current.compactId = -1; + } + + // + // Read the slice size if necessary. + // + if((this._current.sliceFlags & FLAG_HAS_SLICE_SIZE) !== 0) + { + this._current.sliceSize = this._stream.readInt(); + if(this._current.sliceSize < 4) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + else + { + this._current.sliceSize = 0; + } + return this._current.typeId; + }, + endSlice: function() + { + var e, + i, + indirectionTable = [], + length; + + if((this._current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) !== 0) + { + this._stream.skipOpts(); + } + + // + // Read the indirection table if one is present and transform the + // indirect patch list into patch entries with direct references. + // + if((this._current.sliceFlags & FLAG_HAS_INDIRECTION_TABLE) !== 0) + { + // + // The table is written as a sequence<size> to conserve space. + // + length = this._stream.readAndCheckSeqSize(1); + for(i = 0; i < length; ++i) + { + indirectionTable[i] = this.readInstance(this._stream.readSize(), null); + } + + // + // Sanity checks. If there are optional members, it's possible + // that not all object references were read if they are from + // unknown optional data members. + // + if(indirectionTable.length === 0) + { + throw new Ice.MarshalException("empty indirection table"); + } + if((this._current.indirectPatchList === null || this._current.indirectPatchList.length === 0) && + (this._current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) === 0) + { + throw new Ice.MarshalException("no references to indirection table"); + } + + // + // Convert indirect references into direct references. + // + if(this._current.indirectPatchList !== null) + { + for(i = 0, length = this._current.indirectPatchList.length; i < length; ++i) + { + e = this._current.indirectPatchList[i]; + Debug.assert(e.index >= 0); + if(e.index >= indirectionTable.length) + { + throw new Ice.MarshalException("indirection out of range"); + } + this.addPatchEntry(indirectionTable[e.index], e.patcher); + } + this._current.indirectPatchList.length = 0; + } + } + }, + skipSlice: function() + { + if(this._stream.instance.traceLevels().slicing > 0) + { + var logger = this._stream.instance.initializationData().logger; + var slicingCat = this._stream.instance.traceLevels().slicingCat; + if(this._current.sliceType === SliceType.ExceptionSlice) + { + TraceUtil.traceSlicing("exception", this._current.typeId, slicingCat, logger); + } + else + { + TraceUtil.traceSlicing("object", this._current.typeId, slicingCat, logger); + } + } + + var start = this._stream.pos; + + if((this._current.sliceFlags & FLAG_HAS_SLICE_SIZE) !== 0) + { + Debug.assert(this._current.sliceSize >= 4); + this._stream.skip(this._current.sliceSize - 4); + } + else + { + if(this._current.sliceType === SliceType.ObjectSlice) + { + throw new Ice.NoObjectFactoryException( + "compact format prevents slicing (the sender should use the sliced format instead)", + this._current.typeId); + } + + if(this._current.typeId.indexOf("::") === 0) + { + throw new Ice.UnknownUserException(this._current.typeId.substring(2)); + } + + throw new Ice.UnknownUserException(this._current.typeId); + } + + // + // Preserve this slice. + // + var info = new Ice.SliceInfo(); + info.typeId = this._current.typeId; + info.compactId = this._current.compactId; + info.hasOptionalMembers = (this._current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) !== 0; + info.isLastSlice = (this._current.sliceFlags & FLAG_IS_LAST_SLICE) !== 0; + + var b = this._stream._buf; + var end = b.position; + var dataEnd = end; + if(info.hasOptionalMembers) + { + // + // Don't include the optional member end marker. It will be re-written by + // endSlice when the sliced data is re-written. + // + --dataEnd; + } + + b.position = start; + info.bytes = b.getArray(dataEnd - start); + b.position = end; + + if(this._current.slices === null) // Lazy initialization + { + this._current.slices = []; // Ice.SliceInfo[] + this._current.indirectionTables = []; // int[] + } + + // + // Read the indirect object table. We read the instances or their + // IDs if the instance is a reference to an already un-marhsaled + // object. + // + // The SliceInfo object sequence is initialized only if + // readSlicedData is called. + // + + if((this._current.sliceFlags & FLAG_HAS_INDIRECTION_TABLE) !== 0) + { + var length = this._stream.readAndCheckSeqSize(1); + var indirectionTable = []; + for(var i = 0; i < length; ++i) + { + indirectionTable[i] = this.readInstance(this._stream.readSize(), null); + } + this._current.indirectionTables.push(indirectionTable); + } + else + { + this._current.indirectionTables.push(null); + } + this._current.slices.push(info); + }, + readOpt: function(readTag, expectedFormat) + { + if(this._current === null) + { + return this._stream.readOptImpl(readTag, expectedFormat); + } + + if((this._current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) !== 0) + { + return this._stream.readOptImpl(readTag, expectedFormat); + } + return false; + }, + readInstance: function(index, patcher) + { + Debug.assert(index > 0); + + var mostDerivedId, + v = null; + + if(index > 1) + { + if(patcher !== null) + { + this.addPatchEntry(index, patcher); + } + return index; + } + + this.push(SliceType.ObjectSlice); + + // + // Get the object ID before we start reading slices. If some + // slices are skiped, the indirect object table are still read and + // might read other objects. + // + index = ++this._objectIdIndex; + + // + // Read the first slice header. + // + this.startSlice(); + mostDerivedId = this._current.typeId; + while(true) + { + if(this._current.compactId >= 0) + { + // + // Translate a compact (numeric) type ID into a string type ID. + // + this._current.typeId = ""; + if(this._current.typeId.length === 0) + { + this._current.typeId = this._stream.getTypeId(this._current.compactId); + } + } + + if(this._current.typeId.length > 0) + { + v = this.newInstance(this._current.typeId); + // + // We found a factory, we get out of this loop. + // + if(v !== null && v !== undefined) + { + break; + } + } + + // + // If object slicing is disabled, stop un-marshalling. + // + if(!this._sliceObjects) + { + throw new Ice.NoObjectFactoryException("object slicing is disabled", this._current.typeId); + } + + // + // Slice off what we don't understand. + // + this.skipSlice(); + // + // If this is the last slice, keep the object as an opaque + // UnknownSlicedData object. + // + if((this._current.sliceFlags & FLAG_IS_LAST_SLICE) !== 0) + { + v = new Ice.UnknownSlicedObject(mostDerivedId); + break; + } + + this.startSlice(); // Read next Slice header for next iteration. + } + + // + // Un-marshal the object + // + this.unmarshal(index, v); + if(this._current === null && this._patchMap !== null && this._patchMap.size !== 0) + { + // + // If any entries remain in the patch map, the sender has sent an index for an object, but failed + // to supply the object. + // + throw new Ice.MarshalException("index for class received, but no instance"); + } + + if(patcher !== null) + { + patcher.call(null, v); + } + return index; + }, + readSlicedData: function() + { + var i, ii, table, info, j, jj; + + if(this._current.slices === null) // No preserved slices. + { + return null; + } + // + // The _indirectionTables member holds the indirection table for each slice + // in _slices. + // + Debug.assert(this._current.slices.length === this._current.indirectionTables.length); + for(i = 0, ii = this._current.slices.length; i < ii; ++i) + { + // + // We use the "objects" list in SliceInfo to hold references + // to the target objects. Note that the objects might not have + // been read yet in the case of a circular reference to an + // enclosing object. + // + table = this._current.indirectionTables[i]; + info = this._current.slices[i]; + info.objects = []; + jj = table ? table.length : 0; + for(j = 0; j < jj; ++j) + { + this.addPatchEntry(table[j], sequencePatcher(info.objects, j, IceObject)); + } + } + return new SlicedData(ArrayUtil.clone(this._current.slices)); + }, + push: function(sliceType) + { + if(this._current === null) + { + this._current = new EncapsDecoder11.InstanceData(null); + } + else + { + this._current = !this._current.next ? new EncapsDecoder11.InstanceData(this._current) : this._current.next; + } + this._current.sliceType = sliceType; + this._current.skipFirstSlice = false; + } + }); + + EncapsDecoder11.InstanceData = function(previous) + { + if(previous !== null) + { + previous.next = this; + } + this.previous = previous; + this.next = null; + + // Instance attributes + this.sliceType = null; + this.skipFirstSlice = false; + this.slices = null; // Preserved slices. Ice.SliceInfo[] + this.indirectionTables = null; // int[] + + // Slice attributes + this.sliceFlags = 0; + this.sliceSize = 0; + this.typeId = null; + this.compactId = 0; + this.indirectPatchList = null; // Lazy initialized, IndirectPatchEntry[] + }; + + var sequencePatcher = function(seq, index, T){ + return function(v) + { + if(v !== null && !(v instanceof T)) + { + ExUtil.throwUOE(T.ice_staticId(), v); + } + seq[index] = v; + }; + }; + + var EncapsEncoder = Class({ + __init__: function(stream, encaps) + { + this._stream = stream; + this._encaps = encaps; + this._marshaledMap = new HashMap(); // HashMap<Ice.Object, int>; + this._typeIdMap = null; // Lazy initialized. HashMap<String, int> + this._typeIdIndex = 0; + }, + writeOpt: function() + { + return false; + }, + writePendingObjects: function() + { + return undefined; + }, + registerTypeId: function(typeId) + { + if(this._typeIdMap === null) // Lazy initialization + { + this._typeIdMap = new HashMap(); // HashMap<String, int> + } + + var p = this._typeIdMap.get(typeId); + if(p !== undefined) + { + return p; + } + this._typeIdMap.set(typeId, ++this._typeIdIndex); + return -1; + } + }); + + var EncapsEncoder10 = Class(EncapsEncoder, { + __init__: function(stream, encaps) + { + EncapsEncoder.call(this, stream, encaps); + // Instance attributes + this._sliceType = SliceType.NoSlice; + this._writeSlice = 0; // Position of the slice data members + // Encapsulation attributes for object marshalling. + this._objectIdIndex = 0; + this._toBeMarshaledMap = new HashMap(); // HashMap<Ice.Object, Integer>(); + }, + writeObject: function(v) + { + Debug.assert(v !== undefined); + // + // Object references are encoded as a negative integer in 1.0. + // + if(v !== null) + { + this._stream.writeInt(-this.registerObject(v)); + } + else + { + this._stream.writeInt(0); + } + }, + writeUserException: function(v) + { + Debug.assert(v !== null && v !== undefined); + // + // User exception with the 1.0 encoding start with a boolean + // flag that indicates whether or not the exception uses + // classes. + // + // This allows reading the pending objects even if some part of + // the exception was sliced. + // + var usesClasses = v.__usesClasses(); + this._stream.writeBool(usesClasses); + v.__write(this._stream); + if(usesClasses) + { + this.writePendingObjects(); + } + }, + startInstance: function(sliceType) + { + this._sliceType = sliceType; + }, + endInstance: function() + { + if(this._sliceType === SliceType.ObjectSlice) + { + // + // Write the Object slice. + // + this.startSlice(IceObject.ice_staticId(), -1, true); + this._stream.writeSize(0); // For compatibility with the old AFM. + this.endSlice(); + } + this._sliceType = SliceType.NoSlice; + }, + startSlice: function(typeId) + { + // + // For object slices, encode a boolean to indicate how the type ID + // is encoded and the type ID either as a string or index. For + // exception slices, always encode the type ID as a string. + // + if(this._sliceType === SliceType.ObjectSlice) + { + var index = this.registerTypeId(typeId); + if(index < 0) + { + this._stream.writeBool(false); + this._stream.writeString(typeId); + } + else + { + this._stream.writeBool(true); + this._stream.writeSize(index); + } + } + else + { + this._stream.writeString(typeId); + } + + this._stream.writeInt(0); // Placeholder for the slice length. + + this._writeSlice = this._stream.pos; + }, + endSlice: function() + { + // + // Write the slice length. + // + var sz = this._stream.pos - this._writeSlice + 4; + this._stream.rewriteInt(sz, this._writeSlice - 4); + }, + writePendingObjects: function() + { + var self = this, + writeCB = function(key, value) + { + // + // Ask the instance to marshal itself. Any new class + // instances that are triggered by the classes marshaled + // are added to toBeMarshaledMap. + // + self._stream.writeInt(value); + + try + { + key.ice_preMarshal(); + } + catch(ex) + { + self._stream.instance.initializationData().logger.warning( + "exception raised by ice_preMarshal:\n" + ExUtil.toString(ex)); + } + + key.__write(self._stream); + }, + savedMap; + + while(this._toBeMarshaledMap.size > 0) + { + // + // Consider the to be marshalled objects as marshalled now, + // this is necessary to avoid adding again the "to be + // marshalled objects" into _toBeMarshaledMap while writing + // objects. + // + this._marshaledMap.merge(this._toBeMarshaledMap); + + savedMap = this._toBeMarshaledMap; + this._toBeMarshaledMap = new HashMap(); // HashMap<Ice.Object, int>(); + this._stream.writeSize(savedMap.size); + savedMap.forEach(writeCB); + } + this._stream.writeSize(0); // Zero marker indicates end of sequence of sequences of instances. + }, + registerObject: function(v) + { + Debug.assert(v !== null); + + // + // Look for this instance in the to-be-marshaled map. + // + var p = this._toBeMarshaledMap.get(v); + if(p !== undefined) + { + return p; + } + + // + // Didn't find it, try the marshaled map next. + // + p = this._marshaledMap.get(v); + if(p !== undefined) + { + return p; + } + + // + // We haven't seen this instance previously, create a new + // index, and insert it into the to-be-marshaled map. + // + this._toBeMarshaledMap.set(v, ++this._objectIdIndex); + return this._objectIdIndex; + } + }); + + var EncapsEncoder11 = Class(EncapsEncoder, { + __init__: function(stream, encaps) + { + EncapsEncoder.call(this, stream, encaps); + this._current = null; + this._objectIdIndex = 1; + }, + writeObject: function(v) + { + Debug.assert(v !== undefined); + var index, idx; + if(v === null) + { + this._stream.writeSize(0); + } + else if(this._current !== null && this._encaps.format === FormatType.SlicedFormat) + { + if(this._current.indirectionTable === null) // Lazy initialization + { + this._current.indirectionTable = []; // Ice.Object[] + this._current.indirectionMap = new HashMap(); // HashMap<Ice.Object, int> + } + + // + // If writting an object within a slice and using the sliced + // format, write an index from the object indirection + // table. The indirect object table is encoded at the end of + // each slice and is always read (even if the Slice is + // unknown). + // + index = this._current.indirectionMap.get(v); + if(index === undefined) + { + this._current.indirectionTable.push(v); + idx = this._current.indirectionTable.length; // Position + 1 (0 is reserved for nil) + this._current.indirectionMap.set(v, idx); + this._stream.writeSize(idx); + } + else + { + this._stream.writeSize(index); + } + } + else + { + this.writeInstance(v); // Write the instance or a reference if already marshaled. + } + }, + writePendingObjects: function() + { + return undefined; + }, + writeUserException: function(v) + { + Debug.assert(v !== null && v !== undefined); + v.__write(this._stream); + }, + startInstance: function(sliceType, data) + { + if(this._current === null) + { + this._current = new EncapsEncoder11.InstanceData(null); + } + else + { + this._current = (this._current.next === null) ? new EncapsEncoder11.InstanceData(this._current) : this._current.next; + } + this._current.sliceType = sliceType; + this._current.firstSlice = true; + + if(data !== null && data !== undefined) + { + this.writeSlicedData(data); + } + }, + endInstance: function() + { + this._current = this._current.previous; + }, + startSlice: function(typeId, compactId, last) + { + Debug.assert((this._current.indirectionTable === null || this._current.indirectionTable.length === 0) && + (this._current.indirectionMap === null || this._current.indirectionMap.size === 0)); + + this._current.sliceFlagsPos = this._stream.pos; + + this._current.sliceFlags = 0; + if(this._encaps.format === FormatType.SlicedFormat) + { + this._current.sliceFlags |= FLAG_HAS_SLICE_SIZE; // Encode the slice size if using the sliced format. + } + if(last) + { + this._current.sliceFlags |= FLAG_IS_LAST_SLICE; // This is the last slice. + } + + this._stream.writeByte(0); // Placeholder for the slice flags + + // + // For object slices, encode the flag and the type ID either as a + // string or index. For exception slices, always encode the type + // ID a string. + // + if(this._current.sliceType === SliceType.ObjectSlice) + { + // + // Encode the type ID (only in the first slice for the compact + // encoding). + // + if(this._encaps.format === FormatType.SlicedFormat || this._current.firstSlice) + { + if(compactId >= 0) + { + this._current.sliceFlags |= FLAG_HAS_TYPE_ID_COMPACT; + this._stream.writeSize(compactId); + } + else + { + var index = this.registerTypeId(typeId); + if(index < 0) + { + this._current.sliceFlags |= FLAG_HAS_TYPE_ID_STRING; + this._stream.writeString(typeId); + } + else + { + this._current.sliceFlags |= FLAG_HAS_TYPE_ID_INDEX; + this._stream.writeSize(index); + } + } + } + } + else + { + this._stream.writeString(typeId); + } + + if((this._current.sliceFlags & FLAG_HAS_SLICE_SIZE) !== 0) + { + this._stream.writeInt(0); // Placeholder for the slice length. + } + + this._current.writeSlice = this._stream.pos; + this._current.firstSlice = false; + }, + endSlice: function() + { + var sz, i, length; + + // + // Write the optional member end marker if some optional members + // were encoded. Note that the optional members are encoded before + // the indirection table and are included in the slice size. + // + if((this._current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) !== 0) + { + this._stream.writeByte(OPTIONAL_END_MARKER); + } + + // + // Write the slice length if necessary. + // + if((this._current.sliceFlags & FLAG_HAS_SLICE_SIZE) !== 0) + { + sz = this._stream.pos - this._current.writeSlice + 4; + this._stream.rewriteInt(sz, this._current.writeSlice - 4); + } + + // + // Only write the indirection table if it contains entries. + // + if(this._current.indirectionTable !== null && this._current.indirectionTable.length !== 0) + { + Debug.assert(this._encaps.format === FormatType.SlicedFormat); + this._current.sliceFlags |= FLAG_HAS_INDIRECTION_TABLE; + + // + // Write the indirection object table. + // + this._stream.writeSize(this._current.indirectionTable.length); + for(i = 0, length = this._current.indirectionTable.length; i < length; ++i) + { + this.writeInstance(this._current.indirectionTable[i]); + } + this._current.indirectionTable.length = 0; // Faster way to clean array in JavaScript + this._current.indirectionMap.clear(); + } + + // + // Finally, update the slice flags. + // + this._stream.rewriteByte(this._current.sliceFlags, this._current.sliceFlagsPos); + }, + writeOpt: function(tag, format) + { + if(this._current === null) + { + return this._stream.writeOptImpl(tag, format); + } + + if(this._stream.writeOptImpl(tag, format)) + { + this._current.sliceFlags |= FLAG_HAS_OPTIONAL_MEMBERS; + return true; + } + + return false; + }, + writeSlicedData: function(slicedData) + { + Debug.assert(slicedData !== null && slicedData !== undefined); + + // + // We only remarshal preserved slices if we are using the sliced + // format. Otherwise, we ignore the preserved slices, which + // essentially "slices" the object into the most-derived type + // known by the sender. + // + if(this._encaps.format !== FormatType.SlicedFormat) + { + return; + } + + var i, ii, info, + j, jj; + + for(i = 0, ii = slicedData.slices.length; i < ii; ++i) + { + info = slicedData.slices[i]; + this.startSlice(info.typeId, info.compactId, info.isLastSlice); + + // + // Write the bytes associated with this slice. + // + this._stream.writeBlob(info.bytes); + + if(info.hasOptionalMembers) + { + this._current.sliceFlags |= FLAG_HAS_OPTIONAL_MEMBERS; + } + + // + // Make sure to also re-write the object indirection table. + // + if(info.objects !== null && info.objects.length > 0) + { + if(this._current.indirectionTable === null) // Lazy initialization + { + this._current.indirectionTable = []; // Ice.Object[] + this._current.indirectionMap = new HashMap(); // HashMap<Ice.Object, int> + } + + for(j = 0, jj = info.objects.length; j < jj; ++j) + { + this._current.indirectionTable.push(info.objects[j]); + } + } + + this.endSlice(); + } + }, + writeInstance: function(v) + { + Debug.assert(v !== null && v !== undefined); + + // + // If the instance was already marshaled, just write it's ID. + // + var p = this._marshaledMap.get(v); + if(p !== undefined) + { + this._stream.writeSize(p); + return; + } + + // + // We haven't seen this instance previously, create a new ID, + // insert it into the marshaled map, and write the instance. + // + this._marshaledMap.set(v, ++this._objectIdIndex); + + try + { + v.ice_preMarshal(); + } + catch(ex) + { + this._stream.instance.initializationData().logger.warning("exception raised by ice_preMarshal:\n" + + ExUtil.toString(ex)); + } + + this._stream.writeSize(1); // Object instance marker. + v.__write(this._stream); + } + }); + + EncapsEncoder11.InstanceData = function(previous) + { + Debug.assert(previous !== undefined); + if(previous !== null) + { + previous.next = this; + } + this.previous = previous; + this.next = null; + + // Instance attributes + this.sliceType = null; + this.firstSlice = false; + + // Slice attributes + this.sliceFlags = 0; + this.writeSlice = 0; // Position of the slice data members + this.sliceFlagsPos = 0; // Position of the slice flags + this.indirectionTable = null; // Ice.Object[] + this.indirectionMap = null; // HashMap<Ice.Object, int> + }; + + var ReadEncaps = Class({ + __init__: function() + { + this.start = 0; + this.sz = 0; + this.encoding = null; + this.encoding_1_0 = false; + this.decoder = null; + this.next = null; + }, + reset: function() + { + this.decoder = null; + }, + setEncoding: function(encoding) + { + this.encoding = encoding; + this.encoding_1_0 = encoding.equals(Ice.Encoding_1_0); + } + }); + + var WriteEncaps = Class({ + __init__: function() + { + this.start = 0; + this.format = FormatType.DefaultFormat; + this.encoding = null; + this.encoding_1_0 = false; + this.encoder = null; + this.next = null; + }, + reset: function() + { + this.encoder = null; + }, + setEncoding: function(encoding) + { + this.encoding = encoding; + this.encoding_1_0 = encoding.equals(Ice.Encoding_1_0); + } + }); + + var BasicStream = Class({ + __init__: function(instance, encoding, unlimited, data) + { + this._instance = instance; + this._closure = null; + this._encoding = encoding; + + this._readEncapsStack = null; + this._writeEncapsStack = null; + this._readEncapsCache = null; + this._writeEncapsCache = null; + + this._sliceObjects = true; + + this._messageSizeMax = this._instance.messageSizeMax(); // Cached for efficiency. + this._unlimited = unlimited !== undefined ? unlimited : false; + + this._startSeq = -1; + this._sizePos = -1; + + if(data !== undefined) + { + this._buf = new Ice.Buffer(data); + } + else + { + this._buf = new Ice.Buffer(); + } + }, + // + // This function allows this object to be reused, rather than + // reallocated. + // + reset: function() + { + this._buf.reset(); + this.clear(); + }, + clear: function() + { + if(this._readEncapsStack !== null) + { + Debug.assert(this._readEncapsStack.next); + this._readEncapsStack.next = this._readEncapsCache; + this._readEncapsCache = this._readEncapsStack; + this._readEncapsCache.reset(); + this._readEncapsStack = null; + } + + if(this._writeEncapsStack !== null) + { + Debug.assert(this._writeEncapsStack.next); + this._writeEncapsStack.next = this._writeEncapsCache; + this._writeEncapsCache = this._writeEncapsStack; + this._writeEncapsCache.reset(); + this._writeEncapsStack = null; + } + this._startSeq = -1; + this._sliceObjects = true; + }, + swap: function(other) + { + Debug.assert(this._instance === other._instance); + + var tmpBuf, tmpClosure, tmpUnlimited, tmpStartSeq, tmpMinSeqSize, tmpSizePos; + + tmpBuf = other._buf; + other._buf = this._buf; + this._buf = tmpBuf; + + tmpClosure = other._closure; + other._closure = this._closure; + this._closure = tmpClosure; + + // + // Swap is never called for BasicStreams that have encapsulations being read/write. However, + // encapsulations might still be set in case marshalling or un-marshalling failed. We just + // reset the encapsulations if there are still some set. + // + this.resetEncaps(); + other.resetEncaps(); + + tmpUnlimited = other._unlimited; + other._unlimited = this._unlimited; + this._unlimited = tmpUnlimited; + + tmpStartSeq = other._startSeq; + other._startSeq = this._startSeq; + this._startSeq = tmpStartSeq; + + tmpMinSeqSize = other._minSeqSize; + other._minSeqSize = this._minSeqSize; + this._minSeqSize = tmpMinSeqSize; + + tmpSizePos = other._sizePos; + other._sizePos = this._sizePos; + this._sizePos = tmpSizePos; + }, + resetEncaps: function() + { + this._readEncapsStack = null; + this._writeEncapsStack = null; + }, + resize: function(sz) + { + // + // Check memory limit if stream is not unlimited. + // + if(!this._unlimited && sz > this._messageSizeMax) + { + ExUtil.throwMemoryLimitException(sz, this._messageSizeMax); + } + + this._buf.resize(sz); + this._buf.position = sz; + }, + prepareWrite: function() + { + this._buf.position = 0; + return this._buf; + }, + startWriteObject: function(data) + { + Debug.assert(this._writeEncapsStack !== null && this._writeEncapsStack.encoder !== null); + this._writeEncapsStack.encoder.startInstance(SliceType.ObjectSlice, data); + }, + endWriteObject: function() + { + Debug.assert(this._writeEncapsStack !== null && this._writeEncapsStack.encoder !== null); + this._writeEncapsStack.encoder.endInstance(); + }, + startReadObject: function() + { + Debug.assert(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null); + this._readEncapsStack.decoder.startInstance(SliceType.ObjectSlice); + }, + endReadObject: function(preserve) + { + Debug.assert(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null); + return this._readEncapsStack.decoder.endInstance(preserve); + }, + startWriteException: function(data) + { + Debug.assert(this._writeEncapsStack !== null && this._writeEncapsStack.encoder !== null); + this._writeEncapsStack.encoder.startInstance(SliceType.ExceptionSlice, data); + }, + endWriteException: function() + { + Debug.assert(this._writeEncapsStack !== null && this._writeEncapsStack.encoder !== null); + this._writeEncapsStack.encoder.endInstance(); + }, + startReadException: function() + { + Debug.assert(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null); + this._readEncapsStack.decoder.startInstance(SliceType.ExceptionSlice); + }, + endReadException: function(preserve) + { + Debug.assert(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null); + return this._readEncapsStack.decoder.endInstance(preserve); + }, + startWriteEncaps: function(encoding, format) + { + // + // If no encoding version is specified, use the current write + // encapsulation encoding version if there's a current write + // encapsulation, otherwise, use the stream encoding version. + // + + if(encoding === undefined) + { + if(this._writeEncapsStack !== null) + { + encoding = this._writeEncapsStack.encoding; + format = this._writeEncapsStack.format; + } + else + { + encoding = this._encoding; + format = FormatType.DefaultFormat; + } + } + + Protocol.checkSupportedEncoding(encoding); + + var curr = this._writeEncapsCache; + if(curr !== null) + { + curr.reset(); + this._writeEncapsCache = this._writeEncapsCache.next; + } + else + { + curr = new WriteEncaps(); + } + curr.next = this._writeEncapsStack; + this._writeEncapsStack = curr; + + this._writeEncapsStack.format = format; + this._writeEncapsStack.setEncoding(encoding); + this._writeEncapsStack.start = this._buf.limit; + + this.writeInt(0); // Placeholder for the encapsulation length. + this._writeEncapsStack.encoding.__write(this); + }, + endWriteEncaps: function() + { + Debug.assert(this._writeEncapsStack); + + // Size includes size and version. + var start = this._writeEncapsStack.start; + + var sz = this._buf.limit - start; + this._buf.putIntAt(start, sz); + + var curr = this._writeEncapsStack; + this._writeEncapsStack = curr.next; + curr.next = this._writeEncapsCache; + this._writeEncapsCache = curr; + this._writeEncapsCache.reset(); + }, + endWriteEncapsChecked: function() // Used by public stream API. + { + if(this._writeEncapsStack === null) + { + throw new Ice.EncapsulationException("not in an encapsulation"); + } + this.endWriteEncaps(); + }, + writeEmptyEncaps: function(encoding) + { + Protocol.checkSupportedEncoding(encoding); + this.writeInt(6); // Size + encoding.__write(this); + }, + writeEncaps: function(v) + { + if(v.length < 6) + { + throw new Ice.EncapsulationException(); + } + this.expand(v.length); + this._buf.putArray(v); + }, + getWriteEncoding: function() + { + return this._writeEncapsStack !== null ? this._writeEncapsStack.encoding : this._encoding; + }, + startReadEncaps: function() + { + var curr = this._readEncapsCache; + if(curr !== null) + { + curr.reset(); + this._readEncapsCache = this._readEncapsCache.next; + } + else + { + curr = new ReadEncaps(); + } + curr.next = this._readEncapsStack; + this._readEncapsStack = curr; + + this._readEncapsStack.start = this._buf.position; + + // + // I don't use readSize() and writeSize() for encapsulations, + // because when creating an encapsulation, I must know in advance + // how many bytes the size information will require in the data + // stream. If I use an Int, it is always 4 bytes. For + // readSize()/writeSize(), it could be 1 or 5 bytes. + // + var sz = this.readInt(); + if(sz < 6) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + if(sz - 4 > this._buf.remaining) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + this._readEncapsStack.sz = sz; + + var encoding = new Ice.EncodingVersion(); + encoding.__read(this); + Protocol.checkSupportedEncoding(encoding); // Make sure the encoding is supported. + this._readEncapsStack.setEncoding(encoding); + + return encoding; + }, + endReadEncaps: function() + { + Debug.assert(this._readEncapsStack !== null); + + if(!this._readEncapsStack.encoding_1_0) + { + this.skipOpts(); + if(this._buf.position !== this._readEncapsStack.start + this._readEncapsStack.sz) + { + throw new Ice.EncapsulationException(); + } + } + else if(this._buf.position !== this._readEncapsStack.start + this._readEncapsStack.sz) + { + if(this._buf.position + 1 !== this._readEncapsStack.start + this._readEncapsStack.sz) + { + throw new Ice.EncapsulationException(); + } + + // + // Ice version < 3.3 had a bug where user exceptions with + // class members could be encoded with a trailing byte + // when dispatched with AMD. So we tolerate an extra byte + // in the encapsulation. + // + + try + { + this._buf.get(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + var curr = this._readEncapsStack; + this._readEncapsStack = curr.next; + curr.next = this._readEncapsCache; + this._readEncapsCache = curr; + this._readEncapsCache.reset(); + }, + skipEmptyEncaps: function(encoding) + { + Debug.assert(encoding !== undefined); + var sz = this.readInt(); + if(sz !== 6) + { + throw new Ice.EncapsulationException(); + } + + var pos = this._buf.position; + if(pos + 2 > this._buf.limit) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + if(encoding !== null) + { + encoding.__read(this); + } + else + { + this._buf.position = pos + 2; + } + }, + endReadEncapsChecked: function() // Used by public stream API. + { + if(this._readEncapsStack === null) + { + throw new Ice.EncapsulationException("not in an encapsulation"); + } + this.endReadEncaps(); + }, + readEncaps: function(encoding) + { + Debug.assert(encoding !== undefined); + var sz = this.readInt(); + if(sz < 6) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + if(sz - 4 > this._buf.remaining) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + if(encoding !== null) + { + encoding.__read(this); + this._buf.position = this._buf.position - 6; + } + else + { + this._buf.position = this._buf.position - 4; + } + + try + { + return this._buf.getArray(sz); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + getReadEncoding: function() + { + return this._readEncapsStack !== null ? this._readEncapsStack.encoding : this._encoding; + }, + getReadEncapsSize: function() + { + Debug.assert(this._readEncapsStack !== null); + return this._readEncapsStack.sz - 6; + }, + skipEncaps: function() + { + var sz = this.readInt(); + if(sz < 6) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + var encoding = new Ice.EncodingVersion(); + encoding.__read(this); + try + { + this._buf.position = this._buf.position + sz - 6; + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + return encoding; + }, + startWriteSlice: function(typeId, compactId, last) + { + Debug.assert(this._writeEncapsStack !== null && this._writeEncapsStack.encoder !== null); + this._writeEncapsStack.encoder.startSlice(typeId, compactId, last); + }, + endWriteSlice: function() + { + Debug.assert(this._writeEncapsStack !== null && this._writeEncapsStack.encoder !== null); + this._writeEncapsStack.encoder.endSlice(); + }, + startReadSlice: function() // Returns type ID of next slice + { + Debug.assert(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null); + return this._readEncapsStack.decoder.startSlice(); + }, + endReadSlice: function() + { + Debug.assert(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null); + this._readEncapsStack.decoder.endSlice(); + }, + skipSlice: function() + { + Debug.assert(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null); + this._readEncapsStack.decoder.skipSlice(); + }, + readPendingObjects: function() + { + if(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null) + { + this._readEncapsStack.decoder.readPendingObjects(); + } + else if((this._readEncapsStack !== null && this._readEncapsStack.encoding_1_0) || + (this._readEncapsStack === null && this._encoding.equals(Ice.Encoding_1_0))) + { + // + // If using the 1.0 encoding and no objects were read, we + // still read an empty sequence of pending objects if + // requested (i.e.: if this is called). + // + // This is required by the 1.0 encoding, even if no objects + // are written we do marshal an empty sequence if marshaled + // data types use classes. + // + this.skipSize(); + } + }, + writePendingObjects: function() + { + if(this._writeEncapsStack !== null && this._writeEncapsStack.encoder !== null) + { + this._writeEncapsStack.encoder.writePendingObjects(); + } + else if((this._writeEncapsStack !== null && this._writeEncapsStack.encoding_1_0) || + (this._writeEncapsStack === null && this._encoding.equals(Ice.Encoding_1_0))) + { + // + // If using the 1.0 encoding and no objects were written, we + // still write an empty sequence for pending objects if + // requested (i.e.: if this is called). + // + // This is required by the 1.0 encoding, even if no objects + // are written we do marshal an empty sequence if marshaled + // data types use classes. + // + this.writeSize(0); + } + }, + writeSize: function(v) + { + if(v > 254) + { + this.expand(5); + this._buf.put(255); + this._buf.putInt(v); + } + else + { + this.expand(1); + this._buf.put(v); + } + }, + readSize: function() + { + try + { + var b = this._buf.get(); + if(b === 255) + { + var v = this._buf.getInt(); + if(v < 0) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + return v; + } + return b; + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + readAndCheckSeqSize: function(minSize) + { + var sz = this.readSize(); + + if(sz === 0) + { + return sz; + } + + // + // The _startSeq variable points to the start of the sequence for which + // we expect to read at least _minSeqSize bytes from the stream. + // + // If not initialized or if we already read more data than _minSeqSize, + // we reset _startSeq and _minSeqSize for this sequence (possibly a + // top-level sequence or enclosed sequence it doesn't really matter). + // + // Otherwise, we are reading an enclosed sequence and we have to bump + // _minSeqSize by the minimum size that this sequence will require on + // the stream. + // + // The goal of this check is to ensure that when we start un-marshalling + // a new sequence, we check the minimal size of this new sequence against + // the estimated remaining buffer size. This estimatation is based on + // the minimum size of the enclosing sequences, it's _minSeqSize. + // + if(this._startSeq === -1 || this._buf.position > (this._startSeq + this._minSeqSize)) + { + this._startSeq = this._buf.position; + this._minSeqSize = sz * minSize; + } + else + { + this._minSeqSize += sz * minSize; + } + + // + // If there isn't enough data to read on the stream for the sequence (and + // possibly enclosed sequences), something is wrong with the marshalled + // data: it's claiming having more data that what is possible to read. + // + if(this._startSeq + this._minSeqSize > this._buf.limit) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + return sz; + }, + startSize: function() + { + var pos = this._buf.position; + this.writeInt(0); // Placeholder for 32-bit size + return pos; + }, + endSize: function(pos) + { + Debug.assert(pos >= 0); + this.rewriteInt(this._buf.position - pos - 4, pos); + }, + writeBlob: function(v) + { + if(v === null) + { + return; + } + this.expand(v.length); + this._buf.putArray(v); + }, + readBlob: function(sz) + { + if(this._buf.remaining < sz) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + try + { + return this._buf.getArray(sz); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + // Read/write format and tag for optionals + writeOpt: function(tag, format) + { + Debug.assert(this._writeEncapsStack !== null); + if(this._writeEncapsStack.encoder !== null) + { + return this._writeEncapsStack.encoder.writeOpt(tag, format); + } + return this.writeOptImpl(tag, format); + }, + readOpt: function(tag, expectedFormat) + { + Debug.assert(this._readEncapsStack !== null); + if(this._readEncapsStack.decoder !== null) + { + return this._readEncapsStack.decoder.readOpt(tag, expectedFormat); + } + return this.readOptImpl(tag, expectedFormat); + }, + writeOptValue: function(tag, format, write, v) + { + if(v !== undefined) + { + if(this.writeOpt(tag, format)) + { + write.call(this, v); + } + } + }, + readOptValue: function(tag, format, read) + { + if(this.readOpt(tag, format)) + { + return read.call(this); + } + else + { + return undefined; + } + }, + writeByte: function(v) + { + this.expand(1); + this._buf.put(v); + }, + rewriteByte: function(v, dest) + { + this._buf.putAt(dest, v); + }, + readByte: function() + { + try + { + return this._buf.get(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeByteSeq: function(v) + { + if(v === null || v.length === 0) + { + this.writeSize(0); + } + else + { + this.writeSize(v.length); + this.expand(v.length); + this._buf.putArray(v); + } + }, + readByteSeq: function() + { + return this._buf.getArray(this.readAndCheckSeqSize(1)); + }, + writeBool: function(v) + { + this.expand(1); + this._buf.put(v ? 1 : 0); + }, + rewriteBool: function(v, dest) + { + this._buf.putAt(dest, v ? 1 : 0); + }, + readBool: function() + { + try + { + return this._buf.get() === 1; + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeShort: function(v) + { + this.expand(2); + this._buf.putShort(v); + }, + readShort: function() + { + try + { + return this._buf.getShort(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeInt: function(v) + { + this.expand(4); + this._buf.putInt(v); + }, + rewriteInt: function(v, dest) + { + this._buf.putIntAt(dest, v); + }, + readInt: function() + { + try + { + return this._buf.getInt(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeLong: function(v) + { + this.expand(8); + this._buf.putLong(v); + }, + readLong: function() + { + try + { + return this._buf.getLong(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeFloat: function(v) + { + this.expand(4); + this._buf.putFloat(v); + }, + readFloat: function() + { + try + { + return this._buf.getFloat(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeDouble: function(v) + { + this.expand(8); + this._buf.putDouble(v); + }, + readDouble: function() + { + try + { + return this._buf.getDouble(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeString: function(v) + { + if(v === null || v.length === 0) + { + this.writeSize(0); + } + else + { + this._buf.writeString(this, v); + } + }, + readString: function() + { + var len = this.readSize(); + if(len === 0) + { + return ""; + } + // + // Check the buffer has enough bytes to read. + // + if(this._buf.remaining < len) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + try + { + return this._buf.getString(len); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeProxy: function(v) + { + this._instance.proxyFactory().proxyToStream(v, this); + }, + writeOptProxy: function(tag, v) + { + if(v !== undefined) + { + if(this.writeOpt(tag, OptionalFormat.FSize)) + { + var pos = this.startSize(); + this.writeProxy(v); + this.endSize(pos); + } + } + }, + readProxy: function(type) + { + return this._instance.proxyFactory().streamToProxy(this, type); + }, + readOptProxy: function(tag, type) + { + if(this.readOpt(tag, OptionalFormat.FSize)) + { + this.skip(4); + return this.readProxy(type); + } + else + { + return undefined; + } + }, + writeEnum: function(v) + { + if(this.isWriteEncoding_1_0()) + { + if(v.maxValue < 127) + { + this.writeByte(v.value); + } + else if(v.maxValue < 32767) + { + this.writeShort(v.value); + } + else + { + this.writeInt(v.value); + } + } + else + { + this.writeSize(v.value); + } + }, + readEnum: function(T) + { + var v; + if(this.getReadEncoding().equals(Ice.Encoding_1_0)) + { + if(T.maxValue < 127) + { + v = this.readByte(); + } + else if(T.maxValue < 32767) + { + v = this.readShort(); + } + else + { + v = this.readInt(); + } + } + else + { + v = this.readSize(); + } + + var e = T.valueOf(v); + if(e === undefined) + { + throw new Ice.MarshalException("enumerator value " + v + " is out of range"); + } + return e; + }, + readOptEnum: function(tag, T) + { + if(this.readOpt(tag, OptionalFormat.Size)) + { + return this.readEnum(T); + } + else + { + return undefined; + } + }, + writeObject: function(v) + { + this.initWriteEncaps(); + this._writeEncapsStack.encoder.writeObject(v); + }, + writeOptObject: function(tag, v) + { + if(v !== undefined) + { + if(this.writeOpt(tag, OptionalFormat.Class)) + { + this.writeObject(v); + } + } + }, + readObject: function(patcher, T) + { + this.initReadEncaps(); + // + // BUGFIX: + // With Chrome linux the invokation of readObject on the decoder some times + // calls BasicStream.readObject with the decoder object as this param. + // Use call instead of directly invoke the method to workaround this bug. + // + this._readEncapsStack.decoder.readObject.call( + this._readEncapsStack.decoder, + function(obj){ + if(obj !== null && !(obj.ice_instanceof(T))) + { + ExUtil.throwUOE(T.ice_staticId(), obj); + } + patcher(obj); + }); + }, + readOptObject: function(tag, patcher, T) + { + if(this.readOpt(tag, OptionalFormat.Class)) + { + this.readObject(patcher, T); + } + else + { + patcher(undefined); + } + }, + writeUserException: function(e) + { + this.initWriteEncaps(); + this._writeEncapsStack.encoder.writeUserException(e); + }, + throwException: function() + { + this.initReadEncaps(); + this._readEncapsStack.decoder.throwException(); + }, + sliceObjects: function(b) + { + this._sliceObjects = b; + }, + readOptImpl: function(readTag, expectedFormat) + { + var b, v, format, tag, offset; + + if(this.isReadEncoding_1_0()) + { + return false; // Optional members aren't supported with the 1.0 encoding. + } + + while(true) + { + if(this._buf.position >= this._readEncapsStack.start + this._readEncapsStack.sz) + { + return false; // End of encapsulation also indicates end of optionals. + } + + v = this.readByte(); + + if(v === OPTIONAL_END_MARKER) + { + this._buf.position -= 1; // Rewind. + return false; + } + + format = OptionalFormat.valueOf(v & 0x07); // First 3 bits. + tag = v >> 3; + if(tag === 30) + { + tag = this.readSize(); + } + + if(tag > readTag) + { + offset = tag < 30 ? 1 : (tag < 255 ? 2 : 6); // Rewind + this._buf.position -= offset; + return false; // No optional data members with the requested tag. + } + + if(tag < readTag) + { + this.skipOpt(format); // Skip optional data members + } + else + { + if(format !== expectedFormat) + { + throw new Ice.MarshalException("invalid optional data member `" + tag + "': unexpected format"); + } + return true; + } + } + }, + writeOptImpl: function(tag, format) + { + if(this.isWriteEncoding_1_0()) + { + return false; // Optional members aren't supported with the 1.0 encoding. + } + + var v = format.value; + if(tag < 30) + { + v |= tag << 3; + this.writeByte(v); + } + else + { + v |= 0x0F0; // tag = 30 + this.writeByte(v); + this.writeSize(tag); + } + return true; + }, + skipOpt: function(format) + { + switch(format) + { + case OptionalFormat.F1: + this.skip(1); + break; + case OptionalFormat.F2: + this.skip(2); + break; + case OptionalFormat.F4: + this.skip(4); + break; + case OptionalFormat.F8: + this.skip(8); + break; + case OptionalFormat.Size: + this.skipSize(); + break; + case OptionalFormat.VSize: + this.skip(this.readSize()); + break; + case OptionalFormat.FSize: + this.skip(this.readInt()); + break; + case OptionalFormat.Class: + this.readObject(null, Ice.Object); + break; + } + }, + skipOpts: function() + { + var b, v, format; + // + // Skip remaining un-read optional members. + // + while(true) + { + if(this._buf.position >= this._readEncapsStack.start + this._readEncapsStack.sz) + { + return; // End of encapsulation also indicates end of optionals. + } + + b = this.readByte(); + v = b < 0 ? b + 256 : b; + if(v === OPTIONAL_END_MARKER) + { + return; + } + + format = OptionalFormat.valueOf(v & 0x07); // Read first 3 bits. + if((v >> 3) === 30) + { + this.skipSize(); + } + this.skipOpt(format); + } + }, + skip: function(size) + { + if(size > this._buf.remaining) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + this._buf.position += size; + }, + skipSize: function() + { + var b = this.readByte(); + if(b === 255) + { + this.skip(4); + } + }, + isEmpty: function() + { + return this._buf.empty(); + }, + expand: function(n) + { + if(!this._unlimited && this._buf && this._buf.position + n > this._messageSizeMax) + { + ExUtil.throwMemoryLimitException(this._buf.position + n, this._messageSizeMax); + } + this._buf.expand(n); + }, + createObject: function(id) + { + var obj = null, Class; + try + { + var typeId = id.length > 2 ? id.substr(2).replace("::", ".") : ""; + /*jshint -W061 */ + Class = eval(typeId); + /*jshint +W061 */ + if(Class !== undefined) + { + obj = new Class(); + } + } + catch(ex) + { + throw new Ice.NoObjectFactoryException("no object factory", id, ex); + } + + return obj; + }, + getTypeId: function(compactId) + { + var typeId = Ice.CompactIdRegistry.get(compactId); + return typeId === undefined ? "" : typeId; + }, + isReadEncoding_1_0: function() + { + return this._readEncapsStack !== null ? this._readEncapsStack.encoding_1_0 : this._encoding.equals(Ice.Encoding_1_0); + }, + isWriteEncoding_1_0: function() + { + return this._writeEncapsStack ? this._writeEncapsStack.encoding_1_0 : this._encoding.equals(Ice.Encoding_1_0); + }, + initReadEncaps: function() + { + if(this._readEncapsStack === null) // Lazy initialization + { + this._readEncapsStack = this._readEncapsCache; + if(this._readEncapsStack !== null) + { + this._readEncapsCache = this._readEncapsCache.next; + } + else + { + this._readEncapsStack = new ReadEncaps(); + } + this._readEncapsStack.setEncoding(this._encoding); + this._readEncapsStack.sz = this._buf.limit; + } + + if(this._readEncapsStack.decoder === null) // Lazy initialization. + { + var factoryManager = this._instance.servantFactoryManager(); + if(this._readEncapsStack.encoding_1_0) + { + this._readEncapsStack.decoder = new EncapsDecoder10(this, this._readEncapsStack, this._sliceObjects, factoryManager); + } + else + { + this._readEncapsStack.decoder = new EncapsDecoder11(this, this._readEncapsStack, this._sliceObjects, factoryManager); + } + } + }, + initWriteEncaps: function() + { + if(!this._writeEncapsStack) // Lazy initialization + { + this._writeEncapsStack = this._writeEncapsCache; + if(this._writeEncapsStack) + { + this._writeEncapsCache = this._writeEncapsCache.next; + } + else + { + this._writeEncapsStack = new WriteEncaps(); + } + this._writeEncapsStack.setEncoding(this._encoding); + } + + if(this._writeEncapsStack.format === FormatType.DefaultFormat) + { + this._writeEncapsStack.format = this._instance.defaultsAndOverrides().defaultFormat; + } + + if(!this._writeEncapsStack.encoder) // Lazy initialization. + { + if(this._writeEncapsStack.encoding_1_0) + { + this._writeEncapsStack.encoder = new EncapsEncoder10(this, this._writeEncapsStack); + } + else + { + this._writeEncapsStack.encoder = new EncapsEncoder11(this, this._writeEncapsStack); + } + } + }, + createUserException: function(id) + { + var userEx = null, Class; + + try + { + var typeId = id.length > 2 ? id.substr(2).replace("::", ".") : ""; + /*jshint -W061 */ + Class = eval(typeId); + /*jshint +W061 */ + if(Class !== undefined) + { + userEx = new Class(); + } + } + catch(ex) + { + throw new Ice.MarshalException(ex); + } + + return userEx; + } + }); + + var defineProperty = Object.defineProperty; + + defineProperty(BasicStream.prototype, "pos", { + get: function() { return this._buf.position; }, + set: function(n) { this._buf.position = n; } + }); + + defineProperty(BasicStream.prototype, "size", { + get: function() { return this._buf.limit; } + }); + + defineProperty(BasicStream.prototype, "instance", { + get: function() { return this._instance; } + }); + + defineProperty(BasicStream.prototype, "closure", { + get: function() { return this._type; }, + set: function(type) { this._type = type; } + }); + + defineProperty(BasicStream.prototype, "buffer", { + get: function() { return this._buf; } + }); + + var defineBuiltinHelper = function(write, read, sz, format) + { + var helper = { + write: function(os, v) { return write.call(os, v); }, + read: function(is) { return read.call(is); }, + writeOpt: function(os, tag, v) { os.writeOptValue(tag, format, write, v); }, + readOpt: function(is, tag) { return is.readOptValue(tag, format, read); }, + }; + defineProperty(helper, "minWireSize", { + get: function() { return sz; } + }); + return helper; + }; + + var stream = BasicStream.prototype; + Ice.ByteHelper = defineBuiltinHelper(stream.writeByte, stream.readByte, 1, Ice.OptionalFormat.F1); + Ice.BoolHelper = defineBuiltinHelper(stream.writeBool, stream.readBool, 1, Ice.OptionalFormat.F1); + Ice.ShortHelper = defineBuiltinHelper(stream.writeShort, stream.readShort, 2, Ice.OptionalFormat.F2); + Ice.IntHelper = defineBuiltinHelper(stream.writeInt, stream.readInt, 4, Ice.OptionalFormat.F4); + Ice.LongHelper = defineBuiltinHelper(stream.writeLong, stream.readLong, 8, Ice.OptionalFormat.F8); + Ice.FloatHelper = defineBuiltinHelper(stream.writeFloat, stream.readFloat, 4, Ice.OptionalFormat.F4); + Ice.DoubleHelper = defineBuiltinHelper(stream.writeDouble, stream.readDouble, 8, Ice.OptionalFormat.F8); + Ice.StringHelper = defineBuiltinHelper(stream.writeString, stream.readString, 1, Ice.OptionalFormat.VSize); + + Ice.ObjectHelper = { + write: function(os, v) + { + os.writeObject(v); + }, + read: function(is) + { + var o; + is.readObject(function(v) { o = v; }, Ice.Object); + return o; + }, + writeOpt: function(os, tag, v) + { + os.writeOptValue(tag, Ice.OptionalFormat.Class, stream.writeObject, v); + }, + readOpt: function(is, tag) + { + var o; + is.readOptObject(tag, function(v) { o = v; }, Ice.Object); + return o; + }, + }; + + defineProperty(Ice.ObjectHelper, "minWireSize", { + get: function() { return 1; } + }); + + Ice.BasicStream = BasicStream; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); |