object-input-stream 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -98,9 +98,9 @@ class CustomSerializable implements Serializable {
98
98
  ois.registerSerializable("com.mypackage.CustomSerializable", CustomSerializable);
99
99
  ```
100
100
 
101
- > Note: if you recreate and register an entire inheritence chain of serializable classes, their `readObject` methods will be called in order, same as in Java. For every class in the chain that doesn't have a JavaScript handler / where the handler class doesn't have a `readObject` method, `ois.defaultReadObject` is called, again, same as in Java.
101
+ > Warning: Java serializable classes that don't write their fields before writing anything else to stream MUST have a custom handler class that replicates that behavior. Not doing that could lead to undefined behavior.
102
102
 
103
- > Warning: Java serializable classes that write their fields before writing anything else to stream MUST have a custom handler class that replicates this behavior. Not doing that would lead to undefined behavior.
103
+ > Note: if you recreate and register an entire inheritence chain of serializable classes, their `readObject` methods will be called in order, same as in Java. For every class in the chain that doesn't have a JavaScript handler / where the handler class doesn't have a `readObject` method, `ois.defaultReadObject` is called, again, same as in Java.
104
104
 
105
105
  ##### Externalizable Classes
106
106
 
@@ -122,7 +122,7 @@ class CustomExternalizable implements Externalizable {
122
122
  ois.registerExternalizable("com.mypackage.CustomExternalizable", CustomExternalizable);
123
123
  ```
124
124
 
125
- > Warning: Java externalizable objects written using `PROTOCOL_VERSION_1` MUST have a custom handler class that reads all written data to stream. Not doing that would lead to undefined behavior.
125
+ > Warning: Java externalizable objects written using `PROTOCOL_VERSION_1` MUST have a custom handler class that reads all written data to stream. Not doing that could lead to undefined behavior.
126
126
 
127
127
  ##### Enum Objects
128
128
 
@@ -223,7 +223,7 @@ Dynamically generated general class handlers are of the following structure:
223
223
  ```ts
224
224
  class ExampleProxy {
225
225
  // A list of the proxy interface names associated with the class
226
- static readonly proxyInterfaces: string[] = []
226
+ static readonly $proxyInterfaces: string[] = []
227
227
  // The proxy handler class / lambda from Java
228
228
  h?: InvocationHandler
229
229
  // Creates a proxy object that calls this.h on property access
@@ -235,6 +235,8 @@ interface InvocationHandler {
235
235
  }
236
236
  ```
237
237
 
238
+ To use a proxy object in JavaScript, you will need to register a class to correspond to its handler's class.
239
+
238
240
  #### Built-in Class Handlers
239
241
 
240
242
  Primitive wrapper types have built-in handler classes that implement `readResolve` to replace instances with their primitive values. E.g. if the next object on stream is an `Integer` of value 5 and you call `readObject`, it will return the primitive value `5`.
@@ -283,21 +285,21 @@ type OisOptions = {
283
285
  ## AST
284
286
 
285
287
  ```js
286
- // TODO
288
+ import { ObjectInputStreamAST } from 'object-input-stream';
289
+
290
+ const data = new Uint8Array( /* Java object serialization stream data */ );
291
+ const ois = new ObjectInputStream(data);
292
+
293
+ // Read everything
294
+
295
+ const ast = ois.getAST();
287
296
  ```
288
297
 
298
+ > WARNING: The AST structure and API are unstable, and may change at any time.
299
+
300
+ > Note: an AST can be reliably produced only for a stream that has been parsed fully and without errors.
301
+
289
302
  ## Limitations
290
303
 
291
304
  - Requires a runtime that supports `bigint` (all modern runtimes)
292
305
  - Doesn't support strings over 9 petabytes in size (`Number.MAX_SAFE_INTEGER` bytes)
293
-
294
- ## TODO
295
-
296
- - [ ] ObjectInputStreamAST class: emit AST after parsing
297
- - [ ] Complete existing tests
298
- - [ ] Expand tests
299
- - [ ] Classes
300
- - [ ] Class descriptors
301
- - [ ] Proxy classes
302
- - [ ] Enums
303
- - [ ] Sudden death: a brazillian randomly generated primitives and objects with a complex reference graph and readObject/readExternal
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "object-input-stream",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "ObjectInputStream for JavaScript. Read Java serialized objects in Node and the browser.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.js",
@@ -34,6 +34,7 @@
34
34
  "cross-env": "^10.1.0",
35
35
  "jest": "^30.2.0",
36
36
  "ts-jest": "^29.4.6",
37
+ "ts-node": "^10.9.2",
37
38
  "typescript": "^5.9.3"
38
39
  }
39
40
  }
package/src/ast.ts CHANGED
@@ -22,8 +22,10 @@ export default Ast
22
22
  export type Node =
23
23
  RootNode | MagicNode | VersionNode | ContentsNode
24
24
  | BlockDataSequenceNode | BlockDataNode
25
- | PrimitiveNode | UtfNode | LongUtfNode | UtfBodyNode | BytesNode | SkippedNode
25
+ | PrimitiveNode | UtfNode | LongUtfNode | UtfBodyNode
26
26
  | ObjectNode | TCNode | ClassDescInfoNode | ProxyClassDescInfoNode
27
+ | SerialDataNode | ExternalDataNode | SerialClassDataNode | FieldsNode | ValuesNode | FieldDescNode
28
+ | AnnotationNode
27
29
 
28
30
  export interface RootNode extends BaseNode<[MagicNode, VersionNode, ContentsNode]> {
29
31
  type: "root"
@@ -36,7 +38,7 @@ export interface ContentsNode extends BaseNode<(BlockDataSequenceNode | ObjectNo
36
38
  type: "contents"
37
39
  }
38
40
 
39
- interface BaseNode<C extends BaseNode<any>[] | null> {
41
+ interface BaseNode<C extends Node[] | null> {
40
42
  type: string
41
43
  span: {start: number, end: number}
42
44
  children: C
@@ -46,12 +48,12 @@ interface BaseNode<C extends BaseNode<any>[] | null> {
46
48
 
47
49
  // ========== Primitives ==========
48
50
 
49
- export type PrimitiveNode = ByteNode | UnsignedByteNode | ShortNode | UnsignedShortNode | IntNode | FloatNode | DoubleNode | CharNode | BooleanNode | LongNode
51
+ export type PrimitiveNode = ByteNode | UnsignedByteNode | ShortNode | UnsignedShortNode | IntNode | FloatNode | DoubleNode | CharNode | BooleanNode | LongNode | BytesNode
50
52
 
51
53
  interface BasePrimitiveNode extends BaseNode<null> {
52
54
  type: "primitive"
53
55
  dataType: string
54
- value: number | string | boolean | bigint
56
+ value: number | string | boolean | bigint | null
55
57
  }
56
58
  interface NumberNode extends BasePrimitiveNode {
57
59
  dataType: string
@@ -77,33 +79,30 @@ export interface LongNode extends BasePrimitiveNode {
77
79
  dataType: "long"
78
80
  value: bigint
79
81
  }
82
+ export interface BytesNode extends BasePrimitiveNode {
83
+ dataType: "bytes"
84
+ value: null
85
+ }
80
86
 
81
87
  export interface UtfNode extends BaseNode<[UnsignedShortNode, UtfBodyNode]> {
82
88
  type: "utf"
89
+ value: string
83
90
  }
84
91
  export interface LongUtfNode extends BaseNode<[LongNode, UtfBodyNode]> {
85
92
  type: "long-utf"
93
+ value: string
86
94
  }
87
95
  export interface UtfBodyNode extends BaseNode<null> {
88
96
  type: "utf-body"
89
97
  value: string
90
98
  }
91
99
 
92
- export interface BytesNode extends BaseNode<null> {
93
- type: "bytes"
94
- }
95
-
96
- export interface SkippedNode extends BaseNode<null> {
97
- type: "skipped"
98
- reason: string
99
- }
100
-
101
100
 
102
101
  // ========== Block Data ==========
103
102
 
104
- export interface BlockDataSequenceNode extends BaseNode<BlockDataNode[]> {
103
+ export interface BlockDataSequenceNode extends BaseNode<(BlockDataNode | ResetNode)[]> {
105
104
  type: "blockdata-sequence"
106
- values: (PrimitiveNode | UtfNode | SkippedNode)[]
105
+ values: (PrimitiveNode | UtfNode)[]
107
106
  }
108
107
 
109
108
  export interface BlockDataNode extends BaseNode<
@@ -119,13 +118,13 @@ export interface BlockDataNode extends BaseNode<
119
118
  export type ObjectNode = NewObjectNode | NewClassNode | NewArrayNode | NewStringNode | NewEnumNode | NewClassDescNode | PrevObjectNode | NullNode | ExceptionNode | ResetNode
120
119
  export type ClassDescNode = NewClassDescNode | NullNode | PrevObjectNode
121
120
 
122
- interface BaseObjectNode<C extends [TCNode, ...BaseNode<any>[]]> extends BaseNode<C> {
121
+ interface BaseObjectNode<C extends [TCNode, ...Node[]]> extends BaseNode<C> {
123
122
  type: "object"
124
123
  objectType: string
125
124
  }
126
125
 
127
126
  export interface NewObjectNode extends BaseObjectNode<
128
- [TC_OBJECT_Node, ClassDescNode, ...ClassDataNode[]]
127
+ [TC_OBJECT_Node, ClassDescNode, (ExternalDataNode | SerialDataNode)]
129
128
  > {
130
129
  objectType: "new-object"
131
130
  handle: Handle
@@ -143,9 +142,10 @@ export interface NewStringNode extends BaseObjectNode<
143
142
  [TC_LONGSTRING_Node, LongUtfNode]
144
143
  > {
145
144
  objectType: "new-string"
145
+ value: string
146
146
  handle: Handle
147
147
  }
148
- export interface NewEnumNode extends BaseObjectNode<[TC_ENUM_Node, ClassDescNode, ObjectNode]> {
148
+ export interface NewEnumNode extends BaseObjectNode<[TC_ENUM_Node, ClassDescNode, StringNode]> {
149
149
  objectType: "new-enum"
150
150
  handle: Handle
151
151
  }
@@ -163,7 +163,7 @@ export interface PrevObjectNode extends BaseObjectNode<[TC_REFERENCE_Node, IntNo
163
163
  export interface NullNode extends BaseObjectNode<[TC_NULL_Node]> {
164
164
  objectType: "null"
165
165
  }
166
- export interface ExceptionNode extends BaseObjectNode<[TC_EXCEPTION_Node, ObjectNode]> {
166
+ export interface ExceptionNode extends BaseObjectNode<[TC_EXCEPTION_Node, (NewObjectNode | PrevObjectNode)]> {
167
167
  objectType: "exception"
168
168
  exceptionEpoch: number
169
169
  newEpoch: number
@@ -173,44 +173,47 @@ export interface ResetNode extends BaseObjectNode<[TC_RESET_Node]> {
173
173
  newEpoch: number
174
174
  }
175
175
 
176
- export interface ClassDescInfoNode extends BaseNode<[ByteNode, FieldsNode, ContentsNode, TC_ENDBLOCKDATA_Node, ClassDescNode]> {
176
+ export interface ClassDescInfoNode extends BaseNode<[UnsignedByteNode, FieldsNode, AnnotationNode, ClassDescNode]> {
177
177
  type: "class-desc-info"
178
178
  }
179
- export interface ProxyClassDescInfoNode extends BaseNode<[IntNode, ...UtfNode[], ContentsNode, TC_ENDBLOCKDATA_Node, ClassDescNode]> {
179
+ export interface ProxyClassDescInfoNode extends BaseNode<[IntNode, ...UtfNode[], AnnotationNode, ClassDescNode]> {
180
180
  type: "proxy-class-desc-info"
181
181
  }
182
+ export interface AnnotationNode extends BaseNode<[ContentsNode, TC_ENDBLOCKDATA_Node]> {
183
+ type: "annotation"
184
+ }
182
185
 
186
+ export type StringNode = NewStringNode | PrevObjectNode;
183
187
 
184
188
  // ========== Fields & Values ==========
185
189
 
186
- export type ClassDataNode = NoWrClassNode | WrClassNode | ExternalClassNode | OldExternalClassNode
187
-
188
- interface BaseClassDataNode<C extends BaseNode<any>[] | null> extends BaseNode<C> {
189
- type: "class-data"
190
- classType: string
190
+ export interface SerialDataNode extends BaseNode<SerialClassDataNode[]> {
191
+ type: "serial-data"
191
192
  }
192
- export interface NoWrClassNode extends BaseClassDataNode<[ValuesNode]> {
193
- classType: "serializable"
193
+ export type SerialClassDataNode = NoWrClassNode | WrClassNode;
194
+ export interface NoWrClassNode extends BaseNode<
195
+ // Compliant signature is [ValuesNode],
196
+ // but read methods can ignore this constraint
197
+ [ContentsNode, ...([ValuesNode]|[])]
198
+ > {
199
+ type: "class-data"
194
200
  writeMethod: false
195
201
  }
196
- export interface WrClassNode extends BaseClassDataNode<
197
- [ValuesNode, ContentsNode] |
198
-
199
- // A write method can choose to violate the spec and write its values late, or not at all.
200
- // Why? Because fuck you that's why.
201
- [ContentsNode, ValuesNode, ContentsNode] |
202
- [ContentsNode, ValuesNode] |
203
- [ContentsNode]
202
+ export interface WrClassNode extends BaseNode<
203
+ // Compliant signature is [ValuesNode, AnnotationNode],
204
+ // but read methods can ignore this constraint
205
+ [...([ContentsNode, ValuesNode]|[]), AnnotationNode]
204
206
  > {
205
- classType: "serializable"
207
+ type: "class-data"
206
208
  writeMethod: true
207
209
  }
208
- export interface ExternalClassNode extends BaseClassDataNode<[ContentsNode]> {
209
- classType: "externalizable"
210
+ export type ExternalDataNode = ExternalClassDataNode | OldExternalClassDataNode;
211
+ export interface ExternalClassDataNode extends BaseNode<[AnnotationNode]> {
212
+ type: "external-data"
210
213
  protocolVersion: 2
211
214
  }
212
- export interface OldExternalClassNode extends BaseClassDataNode<(ObjectNode | PrimitiveNode | UtfNode | BytesNode)[]> {
213
- classType: "externalizable"
215
+ export interface OldExternalClassDataNode extends BaseNode<(ObjectNode | PrimitiveNode | UtfNode)[]> {
216
+ type: "external-data"
214
217
  protocolVersion: 1
215
218
  }
216
219
 
@@ -226,7 +229,7 @@ export interface PrimitiveDescNode extends BaseNode<[UnsignedByteNode, UtfNode]>
226
229
  type: "field-desc"
227
230
  fieldType: "primitive"
228
231
  }
229
- export interface ObjectDescNode extends BaseNode<[UnsignedByteNode, UtfNode, ObjectNode]> {
232
+ export interface ObjectDescNode extends BaseNode<[UnsignedByteNode, UtfNode, StringNode]> {
230
233
  type: "field-desc"
231
234
  fieldType: "object"
232
235
  }
@@ -239,7 +242,7 @@ interface BaseTCNode extends BaseNode<null> {
239
242
  value: number
240
243
  }
241
244
 
242
- type TCNode = TC_NULL_Node | TC_REFERENCE_Node | TC_CLASSDESC_Node | TC_OBJECT_Node | TC_STRING_Node | TC_ARRAY_Node | TC_CLASS_Node | TC_BLOCKDATA_Node | TC_ENDBLOCKDATA_Node | TC_RESET_Node | TC_BLOCKDATALONG_Node | TC_EXCEPTION_Node | TC_LONGSTRING_Node | TC_PROXYCLASSDESC_Node | TC_ENUM_Node
245
+ export type TCNode = TC_NULL_Node | TC_REFERENCE_Node | TC_CLASSDESC_Node | TC_OBJECT_Node | TC_STRING_Node | TC_ARRAY_Node | TC_CLASS_Node | TC_BLOCKDATA_Node | TC_ENDBLOCKDATA_Node | TC_RESET_Node | TC_BLOCKDATALONG_Node | TC_EXCEPTION_Node | TC_LONGSTRING_Node | TC_PROXYCLASSDESC_Node | TC_ENUM_Node
243
246
 
244
247
  export type TC_NULL_Node = BaseTCNode & {value: typeof ObjectInputStream.TC_NULL}
245
248
  export type TC_REFERENCE_Node = BaseTCNode & {value: typeof ObjectInputStream.TC_REFERENCE}
package/src/index.ts CHANGED
@@ -13,6 +13,8 @@ export {
13
13
  FieldDesc,
14
14
  } from './object-input-stream';
15
15
 
16
+ export { ObjectInputStreamAST } from './ois-ast';
17
+
16
18
  export * as exceptions from './exceptions';
17
19
  export * as ast from './ast';
18
20
  export * as classes from './classes';
package/src/internal.ts CHANGED
@@ -7,4 +7,6 @@ export {
7
7
  BaseFallbackSerializable,
8
8
  BaseFallbackExternalizable,
9
9
  BaseFallbackEnum,
10
+ ReadMethodT,
11
+ EnumProxyHandler,
10
12
  } from './object-input-stream';