diff --git a/.jshintrc b/.jshintrc index 924dce6..73acee7 100644 --- a/.jshintrc +++ b/.jshintrc @@ -34,7 +34,7 @@ "maxlen" : false, // {int} Max number of characters per line // Relaxing - "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) + "asi" : true, // true: Tolerate Automatic Semicolon Insertion (no semicolons) "boss" : true, // true: Tolerate assignments where comparisons would be expected "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. "eqnull" : false, // true: Tolerate use of `== null` diff --git a/lib/input.js b/lib/input.js index f664ae3..4916626 100644 --- a/lib/input.js +++ b/lib/input.js @@ -28,7 +28,7 @@ function InputStream(buf, withType) { this.in = ByteBuffer.wrap(buf); this._refs = []; this._readHeader(); -}; +} InputStream.addObject = function (classname, convertor) { // convertor must impl `readObject(io, obj, withType)` or `writeObject(io, obj, withType)` @@ -36,11 +36,11 @@ InputStream.addObject = function (classname, convertor) { throw new Error('Convertor must implement readObject() or writeObject()'); } objects[classname] = convertor; -}; +} InputStream.read = InputStream.readObject = function (buf, withType) { return new InputStream(buf, withType)._readContent(); -}; +} var proto = InputStream.prototype; @@ -51,7 +51,7 @@ proto.read = proto.readObject = proto._readContent = function () { debug('> _readContent'); var la = this.lookAhead(); - if (la === undefined) return; + if (la === undefined) { return } if (la === cons.TC_BLOCKDATA) { return this._readBlockDataShort(); @@ -60,19 +60,19 @@ proto.read = proto.readObject = proto._readContent = function () { } else { return this._readObject(); } -}; +} proto._readBlockDataShort = function() { // blockdatashort: // TC_BLOCKDATA (unsigned byte) (byte)[size] throw new Error('Not implement _readBlockDataShort()'); -}; +} proto._readBlockDataLong = function() { // blockdatalong: // TC_BLOCKDATALONG (int) (byte)[size] throw new Error('Not implement _readBlockDataLong()'); -}; +} proto._readObject = function () { // object: @@ -124,7 +124,7 @@ proto._readObject = function () { } else { throw new Error('Illegal lookahead: 0x' + la.toString(16)); } -}; +} proto._readNewObject = function () { // newObject: @@ -141,14 +141,14 @@ proto._readNewObject = function () { var ret = normalize(obj, this.withType); debug('<< _readNewObject | obj = %j', ret); return ret; -}; +} proto._readNewClass = function() { // newClass: // TC_CLASS classDesc newHandle throw new Error('Not implement _readNewClass()'); -}; +} proto._readNewArray = function () { // newArray: @@ -165,7 +165,7 @@ proto._readNewArray = function () { debug('<< _readNewArray | obj = %j', obj); return this.withType ? obj : obj.$; -}; +} proto._readNewString = function () { // newString: @@ -176,7 +176,7 @@ proto._readNewString = function () { this._newHandle(str); debug('< _readNewString | str = %s', str); return str; -}; +} proto._readNewEnum = function () { // newEnum: @@ -211,7 +211,7 @@ proto._readNewClassDesc = function () { } else { throw new Error('Illegal lookahead: 0x' + la.toString(16)); } -}; +} proto._readNonProxyDesc = function() { @@ -242,7 +242,7 @@ proto._readPrevObject = function () { var obj = this._refs[id - cons.baseWireHandle]; debug('< _readRrevObject | id = %d, obj = %j', id, obj); return obj; -}; +} proto._readNull = function() { this.in.get(); @@ -282,12 +282,12 @@ proto._readClassDesc = function () { } else { throw new Error('Illegal lookahead: 0x' + la.toString(16)); } -}; +} proto._newHandle = function (o) { debug('> _newHandle | index = %d, obj = %j', this._refs.length, o); this._refs.push(o); -}; +} proto._readClassData = function (obj) { // classdata: @@ -306,12 +306,17 @@ proto._readClassData = function (obj) { if (flags & cons.SC_SERIALIZABLE) { if (flags & cons.SC_WRITE_METHOD) { - debug('>> _readClassData | production: wrclass objectAnnotation') - //this._readNowrclass(obj); - this._readObjectAnnotation(obj); + debug('>> _readClassData | production: wrclass objectAnnotation'); + + var name = obj.$class.name; + objects[name].readObject(this, obj); + + // TC_ENDBLOCKDATA + assert.equal(this.in.get(), cons.TC_ENDBLOCKDATA, + 'SC_WRITE_METHOD object should end with TC_ENDBLOCKDATA'); } else { - debug('>> _readClassData | production: nowrclass') + debug('>> _readClassData | production: nowrclass'); this._readNowrclass(obj); } } else if (flags & cons.SC_EXTERNALIZABLE) { @@ -320,39 +325,42 @@ proto._readClassData = function (obj) { this._readObjectAnnotation(obj); } else { - debug('>> _readClassData | production: externalContents') + debug('>> _readClassData | production: externalContents'); this._readExternalContents(); } } else { - throw new Error('Illegal _readClassData') + throw new Error('Illegal _readClassData'); } -}; +} proto._readNowrclass = function (obj) { // nowrclass: // values // fields in order of class descriptor debug('> _readNowrclass'); - this.defaultReadFields(obj); -}; + this._defaultReadFields(obj); +} -proto.defaultReadFields = function(obj) { - debug('> defaultReadFields'); +proto._defaultReadFields = function(obj) { + debug('> _defaultReadFields'); var $fields = concatFields(obj.$class); for (var i = 0; i < $fields.length; i++) { var field = $fields[i]; var val = this._readFieldValue(field); obj.$[field.name] = val; } +} - // try to detect TC_BLOCKDATA - var type = this.in.get(this.in.position()); - if (type === cons.TC_BLOCKDATA) { - this.in.skip(1); - var size = this.in.get(); - } -}; + +proto.defaultReadObject = function(obj) { + this._defaultReadFields(obj); +} + + +proto.readBlockHeader = function(len) { + this.in.skip(2); +} proto._readObjectAnnotation = function (obj) { @@ -360,30 +368,8 @@ proto._readObjectAnnotation = function (obj) { // endBlockData // contents endBlockData // contents written by writeObject // // or writeExternal PROTOCOL_VERSION_2. - debug('> _readObjectAnnotation'); - var type = this.in.get(this.in.position()); - if (type === cons.TC_BLOCKDATA) { - // TC_BLOCKDATA - this.in.skip(1); - - // blockdata: - // blockdatashort - // blockdatalong - // blockdatashort: - // TC_BLOCKDATA (unsigned byte) (byte)[size] - // blockdatalong: - // TC_BLOCKDATALONG (int) (byte)[size] - var size = this.in.get(); - debug('TC_BLOCKDATA start, size = %d bytes', size); - } - - var name = obj.$class.name; - objects[name].readObject(this, obj); - - // TC_ENDBLOCKDATA - assert.equal(this.in.get(), cons.TC_ENDBLOCKDATA, - 'SC_WRITE_METHOD object should end with TC_ENDBLOCKDATA'); -}; + throw new Error('Not implement _readObjectAnnotation()'); +} proto._readExternalContents = function() { // externalContents: // externalContent written by @@ -406,7 +392,7 @@ proto._readArrayItems = function (obj) { debug('< _readArrayItems | size = %d, arr = %j', size, obj.$); return obj; -}; +} proto._readFieldValue = function (field) { @@ -454,14 +440,14 @@ proto._readFieldValue = function (field) { default: throw new Error('Illegal field type: ' + JSON.stringify(field)); } -}; +} proto._readClassDescFlags = function () { // classDescFlags: // (byte) // Defined in Terminal Symbols and // // Constants return this.readByte(); -}; +} proto._readFields = function () { // fields: @@ -474,7 +460,7 @@ proto._readFields = function () { } debug('< _readFields | count = %d, fieldsDesc = %j', count, fieldsDesc); return fieldsDesc; -}; +} proto._readClassAnnotation = function () { // classAnnotation: @@ -486,7 +472,7 @@ proto._readClassAnnotation = function () { } else { throw new Error('Illegal type: 0x' + type.toString(16)); } -}; +} proto._readSuperClassDesc = function () { // superClassDesc: @@ -494,7 +480,7 @@ proto._readSuperClassDesc = function () { var superClass = this._readClassDesc(); debug('< _readSuperClassDesc | desc = %j', superClass); return superClass; -}; +} proto._readClassDescInfo = function () { // classDescInfo: @@ -508,7 +494,7 @@ proto._readClassDescInfo = function () { debug('< _readClassDescInfo | obj = %j', obj); return obj -}; +} proto._readFieldDesc = function () { @@ -540,7 +526,7 @@ proto._readFieldDesc = function () { debug('<< _readFieldDesc | desc = %j', desc); return desc; -}; +} proto._readUTFString = function (isLong) { // Note that the symbol (utf) is used to designate a string written using 2-byte length information, @@ -562,31 +548,31 @@ proto._readHeader = function () { err.name = 'InvaildStreamHeaderError'; throw err; } -}; +} proto.lookAhead = function() { return this.in.get(this.in.position()); -}; +} proto.readBytes = function (size) { return this.in.read(size); -}; +} proto.readInt = function () { return this.in.getUInt(); -}; +} proto.readByte = function () { return this.in.get(); -}; +} proto.readShort = function () { return this.in.getUInt16(); -}; +} proto.readLong = function () { return this.in.getLong(); -}; +} function concatFields(classDesc) { @@ -595,11 +581,11 @@ function concatFields(classDesc) { } else { return concatFields(classDesc.superClass).concat(classDesc.fields); } -}; +} function normalize(obj, withType) { - if (withType || !obj || obj.$ === undefined) return obj; + if (withType || !obj || obj.$ === undefined) { return obj } var $class = obj.$class; var $fields = concatFields($class); @@ -607,4 +593,4 @@ function normalize(obj, withType) { && $fields.length === 1 && $fields[0].name === 'value'); return isPrimitive ? obj.$.value : ('_$' in obj ? obj._$ : obj.$); -}; +} diff --git a/lib/normalize.js b/lib/normalize.js index 96ea5bc..8adafde 100644 --- a/lib/normalize.js +++ b/lib/normalize.js @@ -1,3 +1,5 @@ +'use strict'; + var numCls = { name : 'java.lang.Number', serialVersionUID : '-8742448824652078965', @@ -188,19 +190,20 @@ var mapCls = { * @param [type] * @return standard object with whole info */ -module.exports = function(obj, type) { +function normalize(obj, type) { var ret = obj; if (obj === null || typeof obj === 'string') { - + ret = obj; } else if (typeof obj === 'boolean') { ret = { '$' : { value : obj }, - '$class' : primitiveClsDesc['boolean'] + '$class' : primitiveClsDesc.boolean } } else if (typeof obj === 'number') { var isInt = Math.ceil(obj) === Math.floor(obj); - if (!isInt && !type) + if (!isInt && !type) { throw new Error('Cannot dicide obj type'); + } type = type || 'int'; if (type in primitiveClsDesc) { @@ -213,8 +216,9 @@ module.exports = function(obj, type) { throw new Error('Illegal obj type'); } } else if (Array.isArray(obj)) { - if (!type) + if (!type) { throw new Error('Argument type is required when pass an array in'); + } if (type in arrayClsDesc) { ret = { @@ -225,13 +229,14 @@ module.exports = function(obj, type) { throw new Error('Cannot handle non-primitive array'); } } else if (obj.toString() === '[object Object]') { - if (!type) + if (!type) { throw new Error('Argument type is required when pass an object in'); + } if (type in primitiveClsDesc || type === 'string') { var values = {}; for (var i in obj) { - values[i] = arguments.callee(obj[i], type); + values[i] = normalize(obj[i], type); } ret = { '_$' : values, @@ -254,3 +259,5 @@ function getThreshold(obj) { var ret = Math.ceil(len / 8) * 6; //*Math.ceil(amount / 8) * 8 * 0.75*/ return ret < 12 ? 12 : ret } + +module.exports = normalize; diff --git a/lib/objects/array_list.js b/lib/objects/array_list.js index cbd93cf..df7d64d 100644 --- a/lib/objects/array_list.js +++ b/lib/objects/array_list.js @@ -60,10 +60,13 @@ exports.readObject = function (io, obj) { // } debug('>> readObject'); - io.defaultReadFields(obj); + io.defaultReadObject(obj); + + io.readBlockHeader(); var items = []; var capacity = obj.$.capacity = io.readInt(); var size = obj.$.size; + for (var i = 0; i < size; i++) { items.push(io._readContent()); } @@ -74,9 +77,12 @@ exports.readObject = function (io, obj) { }; exports.writeObject = function(io, obj) { - io.defaultWriteFields(obj); - io.out.put(new Buffer([0x77, 0x04])); - io.out.putInt(obj.$.capacity); + io.defaultWriteObject(obj); + + io.writeBlockHeader(4); + var capacity = obj.$.capacity || obj._$.length + io.out.putInt(capacity); + obj._$.forEach(function(el) { io.writeObject(el); }); diff --git a/lib/objects/hash_map.js b/lib/objects/hash_map.js index 86e4c6b..95c8091 100644 --- a/lib/objects/hash_map.js +++ b/lib/objects/hash_map.js @@ -74,7 +74,9 @@ exports.readObject = function (io, obj) { // putForCreate(key, value); // } debug('>> readObject'); - io.defaultReadFields(obj); + io.defaultReadObject(obj); + + io.readBlockHeader(); var numBuckets = io.readInt(); var size = io.readInt(); @@ -90,14 +92,25 @@ exports.readObject = function (io, obj) { }; exports.writeObject = function(io, obj) { - io.defaultWriteFields(obj); - io.out.put(new Buffer([0x77, 0x08])); - io.out.putInt(Math.round(obj.$.threshold / obj.$.loadFactor)); - io.out.putInt(Object.keys(obj._$).length); + io.defaultWriteObject(obj); + + io.writeBlockHeader(8); + var size = Object.keys(obj._$).length; + var capacity = 16; + if (obj.$.threshold && obj.$.loadFactor) { + capacity = Math.round(obj.$.threshold / obj.$.loadFactor); + } else { + while(capacity * 3 / 4 < size) { + capacity = capacity * 2; + } + } + io.out.putInt(capacity); + io.out.putInt(size); + for (var i in obj._$) { io.writeObject(i); io.writeObject(obj._$[i]); - }; + } } diff --git a/lib/output.js b/lib/output.js index c443f1a..6ac685c 100644 --- a/lib/output.js +++ b/lib/output.js @@ -63,7 +63,7 @@ proto.write = proto.writeObject = function(obj) { } -proto._writeObject = function(obj, unshared) { +proto._writeObject = function(obj) { // object: // newObject // newClass @@ -80,37 +80,37 @@ proto._writeObject = function(obj, unshared) { if (obj === null) { this._writeNull(); - } else if (!unshared && (handle = this._lookupHandle(obj)) !== -1) { + } else if ((handle = this._lookupHandle(obj)) !== -1) { this._writeHandle(handle); // } else if (obj instanceof Class) { - // writeClass((Class) obj, unshared); + // writeClass((Class) obj); // } else if (obj instanceof ObjectStreamClass) { - // writeClassDesc((ObjectStreamClass) obj, unshared); + // writeClassDesc((ObjectStreamClass) obj); } else if (typeof obj === 'string') { this._writeString(obj); } else if (isEnum(obj)) { - this._writeEnum(obj, unshared); + this._writeEnum(obj); } else if (isArray(obj)) { - this._writeArray(obj, unshared); + this._writeArray(obj); } else { - this._writeOrdinaryObject(obj, unshared); + this._writeOrdinaryObject(obj); } } -proto._writeOrdinaryObject = function(obj, unshared) { +proto._writeOrdinaryObject = function(obj) { // newObject: // TC_OBJECT classDesc newHandle classdata[] // data for each class var out = this.out; out.put(cons.TC_OBJECT); - this._writeClassDesc(obj.$class, false); - this._newHandle(unshared ? null : obj); + this._writeClassDesc(obj.$class); + this._newHandle(obj); this._writeSerialData(obj); } -proto._writeClassDesc = function(desc, unshared) { +proto._writeClassDesc = function(desc) { // classDesc: // newClassDesc // nullReference @@ -120,37 +120,37 @@ proto._writeClassDesc = function(desc, unshared) { if (!desc) { this._writeNull(); - } else if (!unshared && (handle = this._lookupHandle(desc)) !== -1) { + } else if ((handle = this._lookupHandle(desc)) !== -1) { this._writeHandle(handle); // } else if (desc.isProxy()) { - // writeProxyDesc(desc, unshared); + // writeProxyDesc(desc); } else { - this._writeNonProxyDesc(desc, unshared); + this._writeNonProxyDesc(desc); } } -proto._writeProxyDesc = function (desc, unshared) { +proto._writeProxyDesc = function (desc) { // newClassDesc: // TC_PROXYCLASSDESC newHandle proxyClassDescInfo throw new Error('Not implement _writeProxyDesc()'); } -proto._writeNonProxyDesc = function (desc, unshared) { +proto._writeNonProxyDesc = function (desc) { // newClassDesc: // TC_CLASSDESC className serialVersionUID newHandle classDescInfo var out = this.out out.put(cons.TC_CLASSDESC); - this._newHandle(unshared ? null : desc); + this._newHandle(desc); this._writeUTF(desc.name); out.putLong(desc.serialVersionUID); - this._writeClassDescInfo(desc, unshared); + this._writeClassDescInfo(desc); } -proto._writeClassDescInfo = function(desc, unshared) { +proto._writeClassDescInfo = function(desc) { // classDescInfo: // classDescFlags fields classAnnotation superClassDesc var that = this; @@ -169,7 +169,7 @@ proto._writeClassDescInfo = function(desc, unshared) { }); this._writeClassAnnotation(desc); - this._writeClassDesc(desc.superClass, false); + this._writeClassDesc(desc.superClass); } @@ -180,7 +180,7 @@ proto._writeTypeString = function(str) { } else if ((handle = this._lookupHandle(str)) !== -1) { this._writeHandle(handle); } else { - this._writeString(str, false); + this._writeString(str); } } @@ -200,18 +200,33 @@ proto._writeSerialData = function(obj) { // wrclass objectAnnotation // SC_SERIALIZABLE & classDescFlag && // SC_WRITE_METHOD & classDescFlags var out = this.out; + var flags = obj.$class.flags; var className = obj.$class.name; - if (className in objects) { - objects[className].writeObject(this, obj); - out.put(cons.TC_ENDBLOCKDATA); + if (flags & cons.SC_SERIALIZABLE) { + if (flags & cons.SC_WRITE_METHOD) { + objects[className].writeObject(this, obj); + out.put(cons.TC_ENDBLOCKDATA); + + } else { + this._defaultWriteFields(obj); + } + + } else if (flags & cons.SC_EXTERNALIZABLE) { + if (flags & cons.SC_BLOCKDATA) { + throw new Error('Not implement writeObjectAnnotation()'); + + } else { + throw new Error('Not implement writeExternalContents()'); + } } else { - this.defaultWriteFields(obj); + throw new Error('Illegal _writeClassData'); } + } -proto.defaultWriteFields = function(obj) { +proto._defaultWriteFields = function(obj) { var that = this; var out = this.out; var fieldsDesc = getFieldsDesc(obj.$class); @@ -227,7 +242,17 @@ proto.defaultWriteFields = function(obj) { } -proto._writeEnum = function(obj, unshared) { +proto.defaultWriteObject = function(obj) { + this._defaultWriteFields(obj); +} + + +proto.writeBlockHeader = function (len) { + this.out.put(new Buffer([0x77, len])); +} + + +proto._writeEnum = function(obj) { // newEnum: // TC_ENUM classDesc newHandle enumConstantName var out = this.out; @@ -236,19 +261,19 @@ proto._writeEnum = function(obj, unshared) { var desc = obj.$class; var sdesc = desc.superClass; this._writeClassDesc(sdesc.name === 'java.lang.Enum' ? desc : sdesc, false); - this._newHandle(unshared ? null : obj); - this._writeString(obj.$.name, false); + this._newHandle(obj); + this._writeString(obj.$.name); } -proto._writeArray = function(obj, unshared) { +proto._writeArray = function(obj) { // newArray: // TC_ARRAY classDesc newHandle (int) values[size] var that = this; var out = this.out; out.put(cons.TC_ARRAY); - this._writeClassDesc(obj.$class, false); - this._newHandle(unshared ? null : obj); + this._writeClassDesc(obj.$class); + this._newHandle(obj); var values = obj.$; out.putInt(values.length); @@ -258,7 +283,7 @@ proto._writeArray = function(obj, unshared) { }) } else { values.forEach(function(el) { - that._writeObject(el, false); + that._writeObject(el); }); } } @@ -271,7 +296,7 @@ proto._writeNull = function() { } -proto._writeString = function(str, unshared) { +proto._writeString = function(str) { // newString: // TC_STRING newHandle (utf) // TC_LONGSTRING newHandle (long-utf) @@ -280,7 +305,7 @@ proto._writeString = function(str, unshared) { var bf = new Buffer(str); var len = bf.length; - this._newHandle(unshared ? null : str); + this._newHandle(str); if (len <= 0xffff) { out.put(cons.TC_STRING); @@ -367,7 +392,7 @@ function getFieldsDesc(desc) { function isEnum(obj) { var desc = obj.$class; while(desc) { - if (desc.name === 'java.lang.Enum') return true; + if (desc.name === 'java.lang.Enum') { return true; } desc = desc.superClass } return false; diff --git a/test/input.test.js b/test/input.test.js index 6a72378..c92ce77 100644 --- a/test/input.test.js +++ b/test/input.test.js @@ -149,7 +149,7 @@ describe('input.test.js', function () { for (var i = 0; i < 26; i++) { var t = kvs[i]; map0[t] = t; - }; + } ObjectInputStream.read(utils.bytes('map/String')) .should.eql(map0); }); diff --git a/test/normalize.test.js b/test/normalize.test.js index c9bafb8..60f40ef 100644 --- a/test/normalize.test.js +++ b/test/normalize.test.js @@ -57,7 +57,7 @@ describe('normalize.test.js', function () { for (var i = 0; i < 26; i++) { var t = kvs[i]; map0[t] = t; - }; + } var normalizedMap0 = normalize(map0, 'string'); var map0Buf = OutputStream.write(normalizedMap0); var map0InputStream = new InputStream(map0Buf, true);