object-input-stream 0.1.0 → 0.2.1
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 +18 -16
- package/dist/ast.d.ts +42 -44
- package/dist/ast.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -1
- package/dist/internal.js.map +1 -1
- package/dist/object-input-stream.d.ts +15 -5
- package/dist/object-input-stream.d.ts.map +1 -1
- package/dist/object-input-stream.js +161 -112
- package/dist/object-input-stream.js.map +1 -1
- package/dist/ois-ast.d.ts +53 -5
- package/dist/ois-ast.d.ts.map +1 -1
- package/dist/ois-ast.js +878 -88
- package/dist/ois-ast.js.map +1 -1
- package/package.json +2 -1
- package/src/ast.ts +46 -43
- package/src/index.ts +2 -0
- package/src/internal.ts +2 -0
- package/src/object-input-stream.ts +179 -119
- package/src/ois-ast.ts +885 -91
- package/dist/example.d.ts +0 -2
- package/dist/example.d.ts.map +0 -1
- package/dist/example.js +0 -36
- package/dist/example.js.map +0 -1
package/src/ois-ast.ts
CHANGED
|
@@ -1,147 +1,941 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Forgive me father, for I have sinned.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import ObjectInputStream, { ast, Externalizable, FieldDesc, ObjectStreamClass, Serializable, type OisOptions, internal } from ".";
|
|
3
6
|
import * as exc from "./exceptions";
|
|
4
7
|
|
|
8
|
+
type CSTNode = {
|
|
9
|
+
type: string,
|
|
10
|
+
parent: CSTNode | null,
|
|
11
|
+
span: {start: number, end: number},
|
|
12
|
+
value: unknown,
|
|
13
|
+
error?: any,
|
|
14
|
+
handle?: {epoch: number, handle: number},
|
|
15
|
+
children: CSTNode[],
|
|
16
|
+
}
|
|
17
|
+
|
|
5
18
|
|
|
6
19
|
export class ObjectInputStreamAST extends ObjectInputStream {
|
|
7
|
-
protected
|
|
8
|
-
protected
|
|
20
|
+
protected cst: CSTNode;
|
|
21
|
+
protected cstStack: CSTNode[];
|
|
22
|
+
protected finalized = false;
|
|
23
|
+
protected ast: ast.Ast | null = null;
|
|
24
|
+
// How many resets happened so far
|
|
25
|
+
protected epoch: number = 0;
|
|
9
26
|
|
|
10
27
|
constructor(data: Uint8Array, options?: OisOptions) {
|
|
11
28
|
super(data, options);
|
|
12
29
|
|
|
13
|
-
|
|
30
|
+
const rootNode: CSTNode = {
|
|
14
31
|
type: "root",
|
|
15
|
-
span: {start: 0, end:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
span: {start: 0, end: data.length},
|
|
33
|
+
value: null,
|
|
34
|
+
parent: null,
|
|
35
|
+
children: [],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
rootNode.children = [{
|
|
39
|
+
type: "magic",
|
|
40
|
+
span: {start: 0, end: 2},
|
|
41
|
+
value: this.STREAM_MAGIC,
|
|
42
|
+
parent: rootNode,
|
|
43
|
+
children: [],
|
|
44
|
+
}, {
|
|
45
|
+
type: "version",
|
|
46
|
+
span: {start: 2, end: 4},
|
|
47
|
+
value: this.STREAM_VERSION,
|
|
48
|
+
parent: rootNode,
|
|
49
|
+
children: [],
|
|
50
|
+
}, {
|
|
51
|
+
type: "contents",
|
|
52
|
+
span: {start: 4, end: data.length},
|
|
53
|
+
value: null,
|
|
54
|
+
parent: rootNode,
|
|
55
|
+
children: [],
|
|
56
|
+
}]
|
|
24
57
|
|
|
25
|
-
|
|
26
|
-
|
|
58
|
+
this.cst = rootNode;
|
|
59
|
+
this.cstStack = [this.cst, this.cst.children[2]];
|
|
27
60
|
}
|
|
28
61
|
|
|
29
|
-
getAST()
|
|
30
|
-
|
|
62
|
+
getAST() {
|
|
63
|
+
if (this.offset < this.data.length) {
|
|
64
|
+
throw new Error("Not done reading");
|
|
65
|
+
}
|
|
66
|
+
if (this.finalized) {
|
|
67
|
+
assert(this.ast !== null);
|
|
68
|
+
return this.ast;
|
|
69
|
+
}
|
|
70
|
+
this.finalized = true;
|
|
71
|
+
return cstToAst(this.cst, this.data);
|
|
31
72
|
}
|
|
32
73
|
|
|
33
|
-
@
|
|
74
|
+
@traceMethod("primitive/boolean", {keep: true})
|
|
34
75
|
readBoolean(): boolean { return super.readBoolean(); }
|
|
35
|
-
@
|
|
76
|
+
@traceMethod("primitive/byte", {keep: true})
|
|
36
77
|
readByte(): number { return super.readByte(); }
|
|
37
|
-
@
|
|
78
|
+
@traceMethod("primitive/unsigned-byte", {keep: true})
|
|
38
79
|
readUnsignedByte(): number { return super.readUnsignedByte(); }
|
|
39
|
-
@
|
|
80
|
+
@traceMethod("primitive/char", {keep: true})
|
|
40
81
|
readChar(): string { return super.readChar(); }
|
|
41
|
-
@
|
|
82
|
+
@traceMethod("primitive/short", {keep: true})
|
|
42
83
|
readShort(): number { return super.readShort(); }
|
|
43
|
-
@
|
|
84
|
+
@traceMethod("primitive/unsigned-short", {keep: true})
|
|
44
85
|
readUnsignedShort(): number { return super.readUnsignedShort(); }
|
|
45
|
-
@
|
|
86
|
+
@traceMethod("primitive/int", {keep: true})
|
|
46
87
|
readInt(): number { return super.readInt(); }
|
|
47
|
-
@
|
|
88
|
+
@traceMethod("primitive/long", {keep: true})
|
|
48
89
|
readLong(): bigint { return super.readLong(); }
|
|
49
|
-
@
|
|
90
|
+
@traceMethod("primitive/float", {keep: true})
|
|
50
91
|
readFloat(): number { return super.readFloat(); }
|
|
51
|
-
@
|
|
92
|
+
@traceMethod("primitive/double", {keep: true})
|
|
52
93
|
readDouble(): number { return super.readDouble(); }
|
|
53
94
|
|
|
54
|
-
@
|
|
95
|
+
@traceMethod("tc", {keep: true})
|
|
55
96
|
protected readTC(): number { return super.readTC(); }
|
|
97
|
+
|
|
98
|
+
@traceMethod("read1", {keep: true})
|
|
99
|
+
read1() { return super.read1(); }
|
|
100
|
+
@traceMethod("read")
|
|
101
|
+
read(len: number) { return super.read(len); }
|
|
102
|
+
@traceMethod("block-header", {keep: true})
|
|
103
|
+
protected readBlockHeader(): number { return super.readBlockHeader(); }
|
|
104
|
+
|
|
105
|
+
@traceMethod("utf", {keep: true})
|
|
106
|
+
readUTF() { return super.readUTF(); }
|
|
107
|
+
@traceMethod("long-utf", {keep: true})
|
|
108
|
+
protected readLongUTF() { return super.readLongUTF(); }
|
|
109
|
+
@traceMethod("utf-body", {keep: true})
|
|
110
|
+
protected readUTFBody(byteLength: number) { return super.readUTFBody(byteLength); }
|
|
111
|
+
|
|
112
|
+
protected reset(): void {
|
|
113
|
+
assert(this.cstStack.length > 0);
|
|
114
|
+
const currNode = this.cstStack[this.cstStack.length-1];
|
|
115
|
+
assert(currNode.type === "object/reset" || currNode.type === "object/exception");
|
|
116
|
+
currNode.value = ++this.epoch;
|
|
117
|
+
return super.reset();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
protected newHandle(obj: any): number {
|
|
121
|
+
assert(this.cstStack.length > 0);
|
|
122
|
+
const currNode = this.cstStack[this.cstStack.length-1];
|
|
123
|
+
assert(
|
|
124
|
+
currNode.type === "object/class"
|
|
125
|
+
|| currNode.type === "proxy-desc"
|
|
126
|
+
|| currNode.type === "non-proxy-desc"
|
|
127
|
+
|| currNode.type === "object/string"
|
|
128
|
+
|| currNode.type === "object/array"
|
|
129
|
+
|| currNode.type === "object/enum"
|
|
130
|
+
|| currNode.type === "object/instance"
|
|
131
|
+
);
|
|
132
|
+
const handle = super.newHandle(obj);
|
|
133
|
+
currNode.handle = {epoch: this.epoch, handle: handle};
|
|
134
|
+
return handle;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@traceMethod("object/reset")
|
|
138
|
+
protected readReset() { return super.readReset(); }
|
|
139
|
+
@traceMethod("object/null")
|
|
140
|
+
protected readNull() { return super.readNull(); }
|
|
141
|
+
@traceMethod("object/handle")
|
|
142
|
+
protected readHandle() {
|
|
143
|
+
assert(this.cstStack.length > 0);
|
|
144
|
+
const currNode = this.cstStack[this.cstStack.length-1];
|
|
145
|
+
assert(currNode.type === "object/handle");
|
|
146
|
+
currNode.value = this.epoch;
|
|
147
|
+
return super.readHandle();
|
|
148
|
+
}
|
|
149
|
+
@traceMethod("object/class")
|
|
150
|
+
protected readClass() { return super.readClass(); }
|
|
151
|
+
@traceMethod("object/class-desc")
|
|
152
|
+
protected readClassDesc() { return super.readClassDesc(); }
|
|
153
|
+
@traceMethod("proxy-desc")
|
|
154
|
+
protected readProxyDesc() { return super.readProxyDesc(); }
|
|
155
|
+
@traceMethod("non-proxy-desc")
|
|
156
|
+
protected readNonProxyDesc() { return super.readNonProxyDesc(); }
|
|
157
|
+
@traceMethod("object/string", {keep: true})
|
|
158
|
+
protected readString() { return super.readString(); }
|
|
159
|
+
@traceMethod("object/array")
|
|
160
|
+
protected readArray() { return super.readArray(); }
|
|
161
|
+
@traceMethod("object/enum")
|
|
162
|
+
protected readEnum() { return super.readEnum(); }
|
|
163
|
+
@traceMethod("object/instance")
|
|
164
|
+
protected readOrdinaryObject() { return super.readOrdinaryObject(); }
|
|
165
|
+
@traceMethod("external-data")
|
|
166
|
+
protected readExternalData(obj: Externalizable, desc: ObjectStreamClass) { return super.readExternalData(obj, desc); }
|
|
167
|
+
@traceMethod("serial-data")
|
|
168
|
+
protected readSerialData(obj: Serializable, desc: ObjectStreamClass) { return super.readSerialData(obj, desc); }
|
|
169
|
+
@traceMethod("class-data-wr")
|
|
170
|
+
protected readClassDataWr(obj: Serializable, desc: ObjectStreamClass, readMethod: internal.ReadMethodT): void {
|
|
171
|
+
return super.readClassDataWr(obj, desc, readMethod);
|
|
172
|
+
}
|
|
173
|
+
@traceMethod("class-data-no-wr")
|
|
174
|
+
protected readClassDataNoWr(obj: Serializable, desc: ObjectStreamClass, readMethod: internal.ReadMethodT): void { return super.readClassDataNoWr(obj, desc, readMethod); }
|
|
175
|
+
@traceMethod("object/exception")
|
|
176
|
+
protected readFatalException() { return super.readFatalException(); }
|
|
177
|
+
@traceMethod("annotation")
|
|
178
|
+
protected readAnnotation() { return super.readAnnotation(); }
|
|
179
|
+
|
|
180
|
+
@traceMethod("values")
|
|
181
|
+
readFields() { return super.readFields(); }
|
|
182
|
+
@traceMethod("fields")
|
|
183
|
+
protected readFieldDescs(className: string): FieldDesc[] { return super.readFieldDescs(className); }
|
|
184
|
+
@traceMethod("field")
|
|
185
|
+
protected readFieldDesc(className: string): FieldDesc { return super.readFieldDesc(className); }
|
|
56
186
|
}
|
|
57
187
|
|
|
58
|
-
|
|
59
|
-
|
|
188
|
+
|
|
189
|
+
function traceMethod<T, ARGS extends any[]>(type: string, options?: {
|
|
190
|
+
keep?: boolean,
|
|
191
|
+
}) {return (method: (this: ObjectInputStreamAST, ...args: ARGS) => T) => {
|
|
192
|
+
return function decorated(this: ObjectInputStreamAST, ...args: ARGS): T {
|
|
60
193
|
if (!(this instanceof ObjectInputStreamAST))
|
|
61
194
|
throw new exc.InternalError();
|
|
62
195
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
196
|
+
if (this.finalized)
|
|
197
|
+
throw new exc.NotActiveException();
|
|
198
|
+
|
|
199
|
+
const parent = (this.cstStack?.length > 0) ? this.cstStack[this.cstStack.length-1] : null;
|
|
200
|
+
|
|
201
|
+
const node: CSTNode = {
|
|
202
|
+
type: type,
|
|
203
|
+
span: {start: this.offset, end: this.offset},
|
|
204
|
+
value: null,
|
|
205
|
+
parent: parent,
|
|
206
|
+
children: [],
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (parent !== null) {
|
|
210
|
+
parent.children.push(node);
|
|
211
|
+
this.cstStack.push(node);
|
|
212
|
+
}
|
|
66
213
|
|
|
67
|
-
|
|
214
|
+
let result: T;
|
|
215
|
+
try {
|
|
216
|
+
result = method.apply(this, args);
|
|
217
|
+
if (options?.keep)
|
|
218
|
+
node.value = result;
|
|
68
219
|
return result;
|
|
220
|
+
} catch (e) {
|
|
221
|
+
node.error = e;
|
|
222
|
+
throw e;
|
|
223
|
+
} finally {
|
|
224
|
+
node.span.end = this.offset;
|
|
225
|
+
if (parent !== null) {
|
|
226
|
+
const popped = this.cstStack.pop();
|
|
227
|
+
assert(popped === node);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}}
|
|
232
|
+
|
|
233
|
+
function assert(predicate: boolean, message="assertion error"): asserts predicate {
|
|
234
|
+
if (!predicate)
|
|
235
|
+
throw new exc.InternalError(message);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Convert a concrete-syntax-tree to an abstract-syntax-tree.
|
|
240
|
+
* This function is destructive to the CST.
|
|
241
|
+
*/
|
|
242
|
+
function cstToAst(cst: CSTNode, data: Uint8Array): ast.Ast {
|
|
243
|
+
hoistBlockHeaders(cst);
|
|
244
|
+
hoistResets(cst);
|
|
69
245
|
|
|
70
|
-
|
|
246
|
+
// Remove calls to "read" from primitives and strings
|
|
247
|
+
removeNodesWhere(cst, node => node.type === "read" && node.parent !== null && (
|
|
248
|
+
node.parent.type.startsWith("primitive/") || node.parent.type === "utf-body"
|
|
249
|
+
))
|
|
250
|
+
// Remove calls to "readByte" from tc
|
|
251
|
+
removeNodesWhere(cst, node => node.type === "primitive/byte" && node.parent?.type === "tc");
|
|
252
|
+
// Remove empty nodes that shouldn't be empty
|
|
253
|
+
removeNodesWhere(cst, node => {
|
|
254
|
+
if (node.span.start < node.span.end) return false;
|
|
255
|
+
return (
|
|
256
|
+
node.type !== "contents"
|
|
257
|
+
&& node.type !== "serial-data"
|
|
258
|
+
&& node.type !== "external-data"
|
|
259
|
+
&& node.type !== "class-data-no-wr"
|
|
260
|
+
)
|
|
261
|
+
}, {recursive: true});
|
|
71
262
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
263
|
+
handleBlocks(cst);
|
|
264
|
+
|
|
265
|
+
const root = trasformCSTBottomUp(cst, cstToAstNode);
|
|
266
|
+
assert(root.type === "root");
|
|
267
|
+
return {root, data};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Instead of headers being children of primitives, make them their siblings.
|
|
272
|
+
* They will later become their parents.
|
|
273
|
+
*/
|
|
274
|
+
function hoistBlockHeaders(cst: CSTNode): void {
|
|
275
|
+
const headers = [...traverseCST(cst)].filter(node => node.type === "block-header");
|
|
276
|
+
for (const header of headers) {
|
|
277
|
+
while (header.parent !== null && !canContainContent(header.parent)) {
|
|
278
|
+
assert(header.parent.children[0] === header);
|
|
279
|
+
hoistFirstChild(header);
|
|
76
280
|
}
|
|
281
|
+
assert(header.parent !== null);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
77
284
|
|
|
78
|
-
|
|
79
|
-
|
|
285
|
+
function pp(node: ast.Node, indent=0) {
|
|
286
|
+
let res = " ".repeat(indent) + node.type;
|
|
287
|
+
if (node.type === "object") res += "/" + node.objectType;
|
|
288
|
+
if (node.type === "primitive") res += "/" + node.dataType;
|
|
289
|
+
if ((node as any).value !== null) res += ": " + (node as any).value;
|
|
290
|
+
if (node.children !== null && node.children.length > 0)
|
|
291
|
+
res += "\n" + node.children.map(c => pp(c,indent+1)).join("\n")
|
|
292
|
+
return res;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Instead of resets being children of objects / block headers, make them their siblings/
|
|
297
|
+
*/
|
|
298
|
+
function hoistResets(cst: CSTNode): void {
|
|
299
|
+
const resets = [...traverseCST(cst)].filter(node => node.type === "object/reset");
|
|
300
|
+
for (const reset of resets) {
|
|
301
|
+
while (reset.parent !== null && !canContainContent(reset.parent)) {
|
|
302
|
+
assert(reset.parent.children[0] === reset);
|
|
303
|
+
hoistFirstChild(reset);
|
|
304
|
+
}
|
|
305
|
+
assert(reset.parent !== null);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function canContainContent(node: CSTNode) {
|
|
310
|
+
if (node === null) return false;
|
|
311
|
+
const type = node.type;
|
|
312
|
+
return type === "class-data-no-wr" || type === "class-data-wr" || type === "contents" || type === "external-data";
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Manipulate the CST s.t `child` becomes its parent's sibling.
|
|
317
|
+
* `child` must be its parent's first child.
|
|
318
|
+
*/
|
|
319
|
+
function hoistFirstChild(child: CSTNode) {
|
|
320
|
+
const parent = child.parent;
|
|
321
|
+
assert(parent !== null);
|
|
322
|
+
assert(parent.children.indexOf(child) === 0);
|
|
323
|
+
const grandParent = parent.parent;
|
|
324
|
+
assert(grandParent !== null);
|
|
325
|
+
const parentIndex = grandParent.children.indexOf(parent);
|
|
326
|
+
assert(parentIndex !== -1);
|
|
327
|
+
|
|
328
|
+
parent.children.shift();
|
|
329
|
+
parent.span.start = child.span.end;
|
|
330
|
+
|
|
331
|
+
grandParent.children.splice(parentIndex, 0, child);
|
|
332
|
+
child.parent = grandParent;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function handleBlocks(cst: CSTNode) {
|
|
336
|
+
// resets that are in between block headers
|
|
337
|
+
const blockResets = removeNodesWhere(cst, node => {
|
|
338
|
+
if (node.type !== "object/reset") return false;
|
|
339
|
+
assert(node.parent !== null);
|
|
340
|
+
const siblings = node.parent.children;
|
|
341
|
+
const index = siblings.indexOf(node);
|
|
342
|
+
assert(0 <= index);
|
|
343
|
+
|
|
344
|
+
const nextRight = siblings.slice(index+1).find(node => node.type !== "object/reset");
|
|
345
|
+
const nextLeft = siblings.slice(0,index).reverse().find(node => node.type !== "object/reset");
|
|
346
|
+
|
|
347
|
+
return (
|
|
348
|
+
nextRight !== undefined && nextRight.type === "block-header"
|
|
349
|
+
&& nextLeft !== undefined && nextLeft.type === "block-header"
|
|
350
|
+
)
|
|
351
|
+
}, {recursive: true})
|
|
352
|
+
|
|
353
|
+
const blocks =
|
|
354
|
+
removeNodesWhere(cst, node => node.type === "block-header", {recursive: true})
|
|
355
|
+
.filter(header => header.span.start < header.span.end)
|
|
356
|
+
.map(header => {
|
|
357
|
+
assert(header.value !== -1);
|
|
358
|
+
let parent = header.parent;
|
|
359
|
+
assert(parent !== null);
|
|
360
|
+
assert(typeof header.value === "number");
|
|
361
|
+
const span = {start: header.span.start, end: header.span.end + header.value};
|
|
362
|
+
return {header, parent, span};
|
|
363
|
+
});
|
|
80
364
|
|
|
81
|
-
|
|
82
|
-
|
|
365
|
+
if (blocks.length === 0)
|
|
366
|
+
return;
|
|
83
367
|
|
|
84
|
-
|
|
368
|
+
// Group blocks into contiguous sequences
|
|
369
|
+
let currSequence: (typeof blocks[0] | typeof blockResets[0])[] = [blocks[0]];
|
|
370
|
+
const blockSequences: (typeof currSequence)[] = [currSequence];
|
|
371
|
+
for (let i=1; i<blocks.length; i++) {
|
|
372
|
+
let prev = currSequence[currSequence.length-1];
|
|
373
|
+
|
|
374
|
+
while (blockResets.length > 0 && blockResets[0].span.start === prev.span.end) {
|
|
375
|
+
const reset = blockResets.shift();
|
|
376
|
+
assert(reset !== undefined);
|
|
377
|
+
currSequence.push(reset);
|
|
378
|
+
prev = reset;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const curr = blocks[i];
|
|
382
|
+
if (prev.span.end === curr.span.start) {
|
|
383
|
+
currSequence.push(curr);
|
|
384
|
+
} else {
|
|
385
|
+
currSequence = [curr];
|
|
386
|
+
blockSequences.push(currSequence);
|
|
387
|
+
}
|
|
85
388
|
}
|
|
86
|
-
}}
|
|
87
389
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
390
|
+
assert(blockResets.length === 0);
|
|
391
|
+
|
|
392
|
+
for (const sequence of blockSequences) {
|
|
393
|
+
assert(sequence.length > 0);
|
|
394
|
+
assert(sequence.every(block => block.parent === sequence[0].parent));
|
|
92
395
|
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
const
|
|
396
|
+
const parent = sequence[0].parent;
|
|
397
|
+
assert(parent !== null);
|
|
398
|
+
const start = sequence[0].span.start;
|
|
399
|
+
const end = sequence[sequence.length-1].span.end;
|
|
400
|
+
|
|
401
|
+
// Get the values stored in each sequence
|
|
402
|
+
const values = removeNodesWhere(cst, node => (
|
|
403
|
+
node.parent === parent && start <= node.span.start && node.span.end <= end
|
|
404
|
+
), {recursive: true});
|
|
405
|
+
|
|
406
|
+
assert(values.every(v => v.type.startsWith("primitive/") || v.type === "utf" || v.type === "read"));
|
|
407
|
+
values.forEach(v => removeNodesWhere(v, node => node.type === "block-header", {recursive: true}))
|
|
408
|
+
|
|
409
|
+
const sequenceNode: CSTNode = {
|
|
410
|
+
type: "blockdata-sequence",
|
|
411
|
+
span: {start, end},
|
|
412
|
+
parent: null,
|
|
413
|
+
value: values,
|
|
414
|
+
children: [],
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
sequenceNode.children = sequence.map((seqItem) => {
|
|
418
|
+
if ("type" in seqItem && seqItem.type === "object/reset") {
|
|
419
|
+
return seqItem;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const {parent, header, span} = seqItem as typeof blocks[0];
|
|
423
|
+
const result = {
|
|
424
|
+
type: "blockdata",
|
|
425
|
+
span: span,
|
|
426
|
+
parent: sequenceNode,
|
|
427
|
+
value: null,
|
|
428
|
+
children: [...header.children],
|
|
429
|
+
};
|
|
430
|
+
result.children.push({
|
|
431
|
+
type: "read",
|
|
432
|
+
span: {start: header.span.end, end: span.end},
|
|
433
|
+
value: null,
|
|
434
|
+
parent: result,
|
|
435
|
+
children: [],
|
|
436
|
+
});
|
|
96
437
|
|
|
97
|
-
if (this.nodeStack === undefined)
|
|
98
438
|
return result;
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
insertNode(parent, sequenceNode);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
99
444
|
|
|
100
|
-
|
|
445
|
+
function removeNodesWhere(cst: CSTNode, condition: (node: CSTNode) => boolean, options?: {recursive?: boolean}): CSTNode[] {
|
|
446
|
+
const toRemove: CSTNode[] = [];
|
|
101
447
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
dataType: dataType,
|
|
106
|
-
value: result,
|
|
107
|
-
span: span,
|
|
108
|
-
children: null,
|
|
448
|
+
for (const node of traverseCST(cst)) {
|
|
449
|
+
if (condition(node)) {
|
|
450
|
+
toRemove.push(node);
|
|
109
451
|
}
|
|
452
|
+
}
|
|
110
453
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
454
|
+
for (const node of toRemove)
|
|
455
|
+
removeNode(node, options);
|
|
456
|
+
|
|
457
|
+
return toRemove;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function removeNode(node: CSTNode, options?: {recursive?: boolean}): void {
|
|
461
|
+
assert(node.parent !== null);
|
|
462
|
+
const nodeIndex = node.parent.children.indexOf(node);
|
|
463
|
+
assert(nodeIndex !== -1);
|
|
464
|
+
if (options?.recursive) {
|
|
465
|
+
node.parent.children.splice(nodeIndex, 1);
|
|
466
|
+
} else {
|
|
467
|
+
for (const child of node.children) {
|
|
468
|
+
child.parent = node.parent;
|
|
121
469
|
}
|
|
470
|
+
node.parent.children.splice(nodeIndex, 1, ...node.children);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function insertNode(parent: CSTNode, child: CSTNode): void {
|
|
475
|
+
assert(parent.span.start <= child.span.start && child.span.end <= parent.span.end);
|
|
122
476
|
|
|
123
|
-
|
|
477
|
+
child.parent = parent
|
|
478
|
+
|
|
479
|
+
if (parent.children.length === 0) {
|
|
480
|
+
parent.children.push(child);
|
|
481
|
+
return;
|
|
124
482
|
}
|
|
125
|
-
}}
|
|
126
483
|
|
|
484
|
+
let inserted = false;
|
|
485
|
+
for (let i=0; i<=parent.children.length; i++) {
|
|
486
|
+
const left = parent.children[i-1] as CSTNode | undefined;
|
|
487
|
+
const right = parent.children[i] as CSTNode | undefined;
|
|
127
488
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
]));
|
|
489
|
+
// Not overlapping
|
|
490
|
+
if (left !== undefined)
|
|
491
|
+
assert(child.span.end <= left.span.start || left.span.end <= child.span.start);
|
|
492
|
+
if (right !== undefined)
|
|
493
|
+
assert(child.span.end <= right.span.start || right.span.end <= child.span.start);
|
|
134
494
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
495
|
+
const leftGood = (left === undefined) || (left.span.end <= child.span.start);
|
|
496
|
+
const rightGood = (right === undefined) || (child.span.end <= right.span.start);
|
|
497
|
+
if (leftGood && rightGood) {
|
|
498
|
+
parent.children.splice(i, 0, child);
|
|
499
|
+
inserted = true;
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
assert(inserted);
|
|
504
|
+
}
|
|
143
505
|
|
|
506
|
+
function *traverseCST(cst: CSTNode): Generator<CSTNode, undefined, undefined> {
|
|
507
|
+
yield cst;
|
|
508
|
+
for (const child of cst.children)
|
|
509
|
+
yield* traverseCST(child);
|
|
510
|
+
}
|
|
144
511
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
512
|
+
|
|
513
|
+
function trasformCSTBottomUp<T>(cst: CSTNode, transform: (node: CSTNode, transformedChildren: T[]) => T): T {
|
|
514
|
+
const children = cst.children.map(c => trasformCSTBottomUp(c, transform));
|
|
515
|
+
return transform(cst, children);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function cstToAstNode(node: CSTNode, children: ast.Node[]): ast.Node {
|
|
519
|
+
if (node.type.startsWith("primitive/")) {
|
|
520
|
+
assert(children.length === 0);
|
|
521
|
+
const dataType = node.type.split("/", 2)[1];
|
|
522
|
+
const value = node.value;
|
|
523
|
+
const span = node.span;
|
|
524
|
+
switch (dataType) {
|
|
525
|
+
case "boolean":
|
|
526
|
+
assert(typeof value === "boolean");
|
|
527
|
+
return {type: "primitive", dataType, value, span, children: null};
|
|
528
|
+
case "char":
|
|
529
|
+
assert(typeof value === "string");
|
|
530
|
+
return {type: "primitive", dataType, value, span, children: null};
|
|
531
|
+
case "long":
|
|
532
|
+
assert(typeof value === "bigint");
|
|
533
|
+
return {type: "primitive", dataType, value, span, children: null};
|
|
534
|
+
case "byte":
|
|
535
|
+
case "unsigned-byte":
|
|
536
|
+
case "short":
|
|
537
|
+
case "unsigned-short":
|
|
538
|
+
case "int":
|
|
539
|
+
case "float":
|
|
540
|
+
case "double":
|
|
541
|
+
assert(typeof value === "number");
|
|
542
|
+
return {type: "primitive", dataType, value, span, children: null};
|
|
543
|
+
default:
|
|
544
|
+
throw new exc.InternalError(dataType);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
switch (node.type) {
|
|
549
|
+
case "root": {
|
|
550
|
+
assert(children.length === 3);
|
|
551
|
+
const magic = children[0];
|
|
552
|
+
const version = children[1];
|
|
553
|
+
const contents = children[2];
|
|
554
|
+
assert(magic.type === "magic");
|
|
555
|
+
assert(version.type === "version");
|
|
556
|
+
assert(contents.type === "contents");
|
|
557
|
+
return {type: "root", span: node.span, children: [magic, version, contents]};
|
|
558
|
+
}
|
|
559
|
+
case "magic": {
|
|
560
|
+
assert(children.length === 0);
|
|
561
|
+
return {type: "magic", span: node.span, value: ObjectInputStream.STREAM_MAGIC, children: null}
|
|
562
|
+
}
|
|
563
|
+
case "version": {
|
|
564
|
+
assert(children.length === 0);
|
|
565
|
+
return {type: "version", span: node.span, value: ObjectInputStream.STREAM_VERSION, children: null}
|
|
566
|
+
}
|
|
567
|
+
case "contents": {
|
|
568
|
+
assert(children.every(c => c.type === "blockdata-sequence" || c.type === "object"));
|
|
569
|
+
return {type: "contents", span: node.span, children: children}
|
|
570
|
+
}
|
|
571
|
+
case "blockdata-sequence": {
|
|
572
|
+
assert(children.every(c => c.type === "blockdata" || (c.type === "object" && c.objectType === "reset")));
|
|
573
|
+
const rawValues = node.value as CSTNode[];
|
|
574
|
+
const values: ast.Node[] = rawValues.map(v => trasformCSTBottomUp(v, cstToAstNode));
|
|
575
|
+
assert(values.every(v => v.type === "primitive" || v.type === "utf"));
|
|
576
|
+
return {type: "blockdata-sequence", span: node.span, values, children}
|
|
577
|
+
}
|
|
578
|
+
case "blockdata": {
|
|
579
|
+
assert(children.length === 3);
|
|
580
|
+
const tc = children[0];
|
|
581
|
+
const length = children[1];
|
|
582
|
+
const bytes = children[2];
|
|
583
|
+
assert(tc.type === "tc")
|
|
584
|
+
assert(length.type === "primitive");
|
|
585
|
+
assert(bytes.type === "primitive" && bytes.dataType === "bytes");
|
|
586
|
+
if (tc.value === ObjectInputStream.TC_BLOCKDATA && length.dataType === "unsigned-byte") {
|
|
587
|
+
return {type: "blockdata", span: node.span, children: [tc, length, bytes]}
|
|
588
|
+
} else if (tc.value === ObjectInputStream.TC_BLOCKDATALONG && length.dataType === "long") {
|
|
589
|
+
return {type: "blockdata", span: node.span, children: [tc, length, bytes]}
|
|
590
|
+
} else {
|
|
591
|
+
throw new exc.InternalError();
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
case "tc": {
|
|
595
|
+
assert(children.length === 0);
|
|
596
|
+
assert(typeof node.value === "number" && ObjectInputStream.TC_BASE <= node.value && node.value <= ObjectInputStream.TC_MAX);
|
|
597
|
+
return {type: "tc", span: node.span, value: node.value as ast.TCNode["value"], children: null}
|
|
598
|
+
}
|
|
599
|
+
case "read1": {
|
|
600
|
+
assert(children.length === 0);
|
|
601
|
+
assert(typeof node.value === "number");
|
|
602
|
+
return {type: "primitive", span: node.span, dataType: "unsigned-byte", value: node.value, children: null}
|
|
603
|
+
}
|
|
604
|
+
case "read": {
|
|
605
|
+
assert(children.length === 0);
|
|
606
|
+
return {type: "primitive", dataType: "bytes", span: node.span, value: null, children: null}
|
|
607
|
+
}
|
|
608
|
+
case "utf": {
|
|
609
|
+
assert(children.length === 2);
|
|
610
|
+
const length = children[0];
|
|
611
|
+
const body = children[1];
|
|
612
|
+
assert(length.type === "primitive" && length.dataType === "unsigned-short");
|
|
613
|
+
assert(body.type === "utf-body");
|
|
614
|
+
return {type: "utf", value: body.value, span: node.span, children: [length, body]}
|
|
615
|
+
}
|
|
616
|
+
case "long-utf": {
|
|
617
|
+
assert(children.length === 2);
|
|
618
|
+
const length = children[0];
|
|
619
|
+
const body = children[1];
|
|
620
|
+
assert(length.type === "primitive" && length.dataType === "long");
|
|
621
|
+
assert(body.type === "utf-body");
|
|
622
|
+
return {type: "long-utf", value: body.value, span: node.span, children: [length, body]}
|
|
623
|
+
}
|
|
624
|
+
case "utf-body": {
|
|
625
|
+
assert(children.length === 0);
|
|
626
|
+
assert(typeof node.value === "string");
|
|
627
|
+
return {type: "utf-body", value: node.value, span: node.span, children: null}
|
|
628
|
+
}
|
|
629
|
+
case "object/reset": {
|
|
630
|
+
assert(children.length === 1);
|
|
631
|
+
const tc = children[0];
|
|
632
|
+
assert(tc.type === "tc" && tc.value === ObjectInputStream.TC_RESET);
|
|
633
|
+
assert(typeof node.value === "number");
|
|
634
|
+
return {type: "object", objectType: "reset", newEpoch: node.value, span: node.span, children: [tc]}
|
|
635
|
+
}
|
|
636
|
+
case "object/null": {
|
|
637
|
+
assert(children.length === 1);
|
|
638
|
+
const tc = children[0];
|
|
639
|
+
assert(tc.type === "tc" && tc.value === ObjectInputStream.TC_NULL);
|
|
640
|
+
return {type: "object", objectType: "null", span: node.span, children: [tc]}
|
|
641
|
+
}
|
|
642
|
+
case "object/handle": {
|
|
643
|
+
assert(children.length === 2);
|
|
644
|
+
const tc = children[0];
|
|
645
|
+
const ref = children[1];
|
|
646
|
+
assert(tc.type === "tc" && tc.value === ObjectInputStream.TC_REFERENCE);
|
|
647
|
+
assert(ref.type === "primitive" && ref.dataType === "int");
|
|
648
|
+
assert(typeof node.value === "number")
|
|
649
|
+
return {type: "object", objectType: "prev-object", span: node.span, value: {epoch: node.value, handle: ref.value}, children: [tc, ref]}
|
|
650
|
+
}
|
|
651
|
+
case "object/class": {
|
|
652
|
+
assert(children.length === 1);
|
|
653
|
+
const tc = children[0];
|
|
654
|
+
const desc = children[0];
|
|
655
|
+
assert(tc.type === "tc" && tc.value === ObjectInputStream.TC_CLASS);
|
|
656
|
+
assertDescNode(desc);
|
|
657
|
+
assert(node.handle !== undefined);
|
|
658
|
+
return {type: "object", objectType: "new-class", span: node.span, handle: node.handle, children: [tc, desc]}
|
|
659
|
+
}
|
|
660
|
+
case "object/class-desc": {
|
|
661
|
+
assert(children.length === 1);
|
|
662
|
+
const first = children[0];
|
|
663
|
+
assertDescNode(first);
|
|
664
|
+
return first;
|
|
665
|
+
}
|
|
666
|
+
case "proxy-desc": {
|
|
667
|
+
assert(children.length >= 2);
|
|
668
|
+
const tc = children[0];
|
|
669
|
+
const numIfaces = children[1];
|
|
670
|
+
assert(tc.type === "tc" && tc.value === ObjectInputStream.TC_PROXYCLASSDESC);
|
|
671
|
+
assert(numIfaces.type === "primitive" && numIfaces.dataType === "int");
|
|
672
|
+
assert(children.length >= 2 + numIfaces.value);
|
|
673
|
+
const ifaces = children.slice(2, 2 + numIfaces.value);
|
|
674
|
+
assert(ifaces.every(x => x.type === "utf"));
|
|
675
|
+
assert(children.length === 2 + numIfaces.value + 2);
|
|
676
|
+
const annotation = children[2 + numIfaces.value];
|
|
677
|
+
const superDesc = children[2 + numIfaces.value + 1];
|
|
678
|
+
assert(annotation.type === "annotation");
|
|
679
|
+
assertDescNode(superDesc);
|
|
680
|
+
assert(node.handle !== undefined);
|
|
681
|
+
return {type: "object", objectType: "new-class-desc", handle: node.handle, span: node.span, children: [tc, {
|
|
682
|
+
type: "proxy-class-desc-info",
|
|
683
|
+
span: {start: node.span.start+1, end: node.span.end},
|
|
684
|
+
children: [numIfaces, ...ifaces, annotation, superDesc],
|
|
685
|
+
}]}
|
|
686
|
+
}
|
|
687
|
+
case "non-proxy-desc": {
|
|
688
|
+
assert(children.length === 7);
|
|
689
|
+
const tc = children[0];
|
|
690
|
+
const name = children[1];
|
|
691
|
+
const suid = children[2];
|
|
692
|
+
const flags = children[3];
|
|
693
|
+
const fields = children[4];
|
|
694
|
+
const annotation = children[5];
|
|
695
|
+
const superDesc = children[6];
|
|
696
|
+
assert(tc.type === "tc" && tc.value === ObjectInputStream.TC_CLASSDESC);
|
|
697
|
+
assert(name.type === "utf");
|
|
698
|
+
assert(suid.type === "primitive" && suid.dataType === "long");
|
|
699
|
+
assert(flags.type === "primitive" && flags.dataType === "unsigned-byte");
|
|
700
|
+
assert(fields.type === "fields");
|
|
701
|
+
assert(annotation.type === "annotation");
|
|
702
|
+
assertDescNode(superDesc);
|
|
703
|
+
assert(node.handle !== undefined);
|
|
704
|
+
return {type: "object", objectType: "new-class-desc", handle: node.handle, span: node.span, children: [
|
|
705
|
+
tc, name, suid, {
|
|
706
|
+
type: "class-desc-info",
|
|
707
|
+
span: {start: suid.span.end, end: node.span.end},
|
|
708
|
+
children: [flags, fields, annotation, superDesc],
|
|
709
|
+
}
|
|
710
|
+
]}
|
|
711
|
+
}
|
|
712
|
+
case "object/string": {
|
|
713
|
+
assert(children.length > 0);
|
|
714
|
+
const first = children[0];
|
|
715
|
+
if (first.type === "object" && first.objectType === "prev-object")
|
|
716
|
+
return first;
|
|
717
|
+
|
|
718
|
+
assert(children.length === 2);
|
|
719
|
+
const tc = children[0];
|
|
720
|
+
const utf = children[1];
|
|
721
|
+
assert(tc.type === "tc");
|
|
722
|
+
if (tc.value === ObjectInputStream.TC_STRING) {
|
|
723
|
+
assert(utf.type === "utf");
|
|
724
|
+
assert(node.handle !== undefined);
|
|
725
|
+
return {type: "object", objectType: "new-string", value: utf.value, handle: node.handle, span: node.span, children: [tc, utf]};
|
|
726
|
+
} else if (tc.value === ObjectInputStream.TC_LONGSTRING) {
|
|
727
|
+
assert(utf.type === "long-utf");
|
|
728
|
+
assert(node.handle !== undefined);
|
|
729
|
+
return {type: "object", objectType: "new-string", value: utf.value, handle: node.handle, span: node.span, children: [tc, utf]};
|
|
730
|
+
} else {
|
|
731
|
+
throw new exc.InternalError();
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
case "object/array": {
|
|
735
|
+
assert(children.length >= 3);
|
|
736
|
+
const tc = children[0];
|
|
737
|
+
const desc = children[1];
|
|
738
|
+
const len = children[2];
|
|
739
|
+
const values = children.slice(3);
|
|
740
|
+
assert(tc.type === "tc" && tc.value === ObjectInputStream.TC_ARRAY);
|
|
741
|
+
assertDescNode(desc);
|
|
742
|
+
assert(len.type === "primitive" && len.dataType === "int");
|
|
743
|
+
assert(len.value === values.length);
|
|
744
|
+
assert(values.every(v => v.type === "primitive" || v.type === "object"));
|
|
745
|
+
const firstItem = values.length > 0 ? values[0] : null;
|
|
746
|
+
if (firstItem?.type === "primitive") {
|
|
747
|
+
assert(values.every(v => v.type === "primitive" && v.dataType === firstItem.dataType));
|
|
748
|
+
} else {
|
|
749
|
+
assert(values.every(v => v.type === "object"));
|
|
750
|
+
}
|
|
751
|
+
assert(node.handle !== undefined);
|
|
752
|
+
return {type: "object", objectType: "new-array", handle: node.handle, span: node.span, children: [tc, desc, len, {
|
|
753
|
+
type: "values", span: {start: len.span.end, end: node.span.end}, children: values
|
|
754
|
+
}]}
|
|
755
|
+
}
|
|
756
|
+
case "object/enum": {
|
|
757
|
+
assert(children.length === 2);
|
|
758
|
+
const tc = children[0];
|
|
759
|
+
const desc = children[1];
|
|
760
|
+
const name = children[2];
|
|
761
|
+
assert(tc.type === "tc" && tc.value === ObjectInputStream.TC_ENUM);
|
|
762
|
+
assertDescNode(desc);
|
|
763
|
+
assertStringNode(name);
|
|
764
|
+
assert(node.handle !== undefined);
|
|
765
|
+
return {type: "object", objectType: "new-enum", span: node.span, handle: node.handle, children: [tc, desc, name]}
|
|
766
|
+
}
|
|
767
|
+
case "object/instance": {
|
|
768
|
+
assert(children.length === 3);
|
|
769
|
+
const tc = children[0];
|
|
770
|
+
const desc = children[1];
|
|
771
|
+
const data = children[2];
|
|
772
|
+
assert(tc.type === "tc" && tc.value === ObjectInputStream.TC_OBJECT);
|
|
773
|
+
assertDescNode(desc);
|
|
774
|
+
assert(data.type === "external-data" || data.type === "serial-data");
|
|
775
|
+
assert(node.handle !== undefined);
|
|
776
|
+
return {type: "object", objectType: "new-object", handle: node.handle, span: node.span, children: [tc, desc, data]}
|
|
777
|
+
}
|
|
778
|
+
case "external-data": {
|
|
779
|
+
// PROTOCOL_VERSION_1
|
|
780
|
+
if (children.every(c => c.type === "primitive" || c.type === "object" || c.type === "utf")) {
|
|
781
|
+
return {type: "external-data", protocolVersion: 1, span: node.span, children: children}
|
|
782
|
+
}
|
|
783
|
+
// PROTOCOL_VERSION_2
|
|
784
|
+
else {
|
|
785
|
+
assert(children.length > 0);
|
|
786
|
+
const annotation = children[children.length-1];
|
|
787
|
+
assert(annotation.type === "annotation");
|
|
788
|
+
const before = children.slice(0, children.length-1);
|
|
789
|
+
assert(before.every(c => c.type === "object" || c.type === "blockdata-sequence"));
|
|
790
|
+
const contents = annotation.children[0];
|
|
791
|
+
const endBlock = annotation.children[1];
|
|
792
|
+
const newContents: ast.ContentsNode = {
|
|
793
|
+
type: "contents",
|
|
794
|
+
span: {start: node.span.start, end: contents.span.end},
|
|
795
|
+
children: [...before, ...contents.children],
|
|
796
|
+
}
|
|
797
|
+
return {type: "external-data", protocolVersion: 2, span: node.span, children: [{
|
|
798
|
+
type: "annotation",
|
|
799
|
+
span: node.span,
|
|
800
|
+
children: [newContents, endBlock],
|
|
801
|
+
}]}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
case "serial-data": {
|
|
805
|
+
assert(children.every(c => c.type === "class-data"));
|
|
806
|
+
return {type: "serial-data", span: node.span, children: children}
|
|
807
|
+
}
|
|
808
|
+
case "class-data-wr": {
|
|
809
|
+
assert(children.length > 0);
|
|
810
|
+
const annotation = children[children.length-1];
|
|
811
|
+
assert(annotation.type === "annotation");
|
|
812
|
+
let beforeValues: (ast.BlockDataSequenceNode | ast.ObjectNode)[];
|
|
813
|
+
let values: ast.ValuesNode | null;
|
|
814
|
+
let afterValues: (ast.BlockDataSequenceNode | ast.ObjectNode)[];
|
|
815
|
+
const valuesIdx = children.findIndex(c => c.type === "values");
|
|
816
|
+
if (valuesIdx === -1) {
|
|
817
|
+
beforeValues = [];
|
|
818
|
+
values = null;
|
|
819
|
+
const tempAfter = children.slice(0, -1);
|
|
820
|
+
assert(tempAfter.every(c => c.type === "blockdata-sequence" || c.type === "object"));
|
|
821
|
+
afterValues = tempAfter;
|
|
822
|
+
} else {
|
|
823
|
+
const tempBefore = children.slice(0, valuesIdx);
|
|
824
|
+
const tempValues = children[valuesIdx];
|
|
825
|
+
const tempAfter = children.slice(valuesIdx+1, -1);
|
|
826
|
+
assert(tempBefore.every(c => c.type === "blockdata-sequence" || c.type === "object"));
|
|
827
|
+
assert(tempValues.type === "values");
|
|
828
|
+
assert(tempAfter.every(c => c.type === "blockdata-sequence" || c.type === "object"));
|
|
829
|
+
beforeValues = tempBefore;
|
|
830
|
+
values = tempValues;
|
|
831
|
+
afterValues = tempAfter;
|
|
832
|
+
}
|
|
833
|
+
const annotationSpan = {
|
|
834
|
+
start: afterValues.length > 0 ? afterValues[0].span.start : annotation.span.start,
|
|
835
|
+
end: annotation.span.end,
|
|
836
|
+
};
|
|
837
|
+
const annotationContentsSpan = {start: annotationSpan.start, end: annotationSpan.end-1};
|
|
838
|
+
const newAnnotation: ast.AnnotationNode = {
|
|
839
|
+
type: "annotation",
|
|
840
|
+
span: annotationSpan,
|
|
841
|
+
children: [{
|
|
842
|
+
type: "contents",
|
|
843
|
+
span: annotationContentsSpan,
|
|
844
|
+
children: [...afterValues, ...annotation.children[0].children],
|
|
845
|
+
}, annotation.children[1]],
|
|
846
|
+
}
|
|
847
|
+
const constentsSpan = {start: node.span.start, end: values !== null ? values.span.start : -1};
|
|
848
|
+
const contents: ast.ContentsNode = {
|
|
849
|
+
type: "contents",
|
|
850
|
+
span: constentsSpan,
|
|
851
|
+
children: [...beforeValues],
|
|
852
|
+
}
|
|
853
|
+
const beforeAnnotation = values !== null ? [contents, values] as const : [] as const;
|
|
854
|
+
return {type: "class-data", writeMethod: true, span: node.span, children: [...beforeAnnotation, newAnnotation]}
|
|
855
|
+
}
|
|
856
|
+
case "class-data-no-wr": {
|
|
857
|
+
const hasValues = children.length > 0 && children[children.length-1].type === "values";
|
|
858
|
+
const values = hasValues ? children[children.length-1] : null;
|
|
859
|
+
assert(values === null || values.type === "values");
|
|
860
|
+
const before = children.slice(0, hasValues ? -1 : undefined);
|
|
861
|
+
assert(before.every(c => c.type === "blockdata-sequence" || c.type === "object"));
|
|
862
|
+
const contentsStart = node.span.start;
|
|
863
|
+
const contentsEnd = before.length > 0 ? before[before.length-1].span.end : values !== null ? values.span.start : node.span.end;
|
|
864
|
+
const contents: ast.ContentsNode = {
|
|
865
|
+
type: "contents",
|
|
866
|
+
span: {start: contentsStart, end: contentsEnd},
|
|
867
|
+
children: before,
|
|
868
|
+
}
|
|
869
|
+
const valuesNodes = values !== null ? [values] as const : [] as const;
|
|
870
|
+
return {type: "class-data", writeMethod: false, span: node.span, children: [
|
|
871
|
+
contents, ...valuesNodes,
|
|
872
|
+
]}
|
|
873
|
+
}
|
|
874
|
+
case "object/exception": {
|
|
875
|
+
assert(node.error !== undefined);
|
|
876
|
+
assert(children.length === 2);
|
|
877
|
+
const tc = children[0];
|
|
878
|
+
const throwable = children[1];
|
|
879
|
+
assert(tc.type === "tc" && tc.value === ObjectInputStream.TC_EXCEPTION);
|
|
880
|
+
assert(throwable.type === "object" && (throwable.objectType === "new-object" || throwable.objectType === "prev-object"));
|
|
881
|
+
assert(typeof node.value === "number");
|
|
882
|
+
const newEpoch = node.value;
|
|
883
|
+
return {type: "object", objectType: "exception", exceptionEpoch: newEpoch-1, newEpoch: newEpoch, span: node.span, children: [tc, throwable]}
|
|
884
|
+
}
|
|
885
|
+
case "annotation": {
|
|
886
|
+
assert(children.length > 0);
|
|
887
|
+
const contents = children.slice(0, -1);
|
|
888
|
+
const tc = children[children.length-1];
|
|
889
|
+
assert(contents.every(c => c.type === "blockdata-sequence" || c.type === "object"));
|
|
890
|
+
assert(tc.type === "tc" && tc.value === ObjectInputStream.TC_ENDBLOCKDATA);
|
|
891
|
+
return {type: "annotation", span: node.span, children: [{
|
|
892
|
+
type: "contents", span: {start: node.span.start, end: tc.span.start}, children: contents,
|
|
893
|
+
}, tc]}
|
|
894
|
+
}
|
|
895
|
+
case "fields": {
|
|
896
|
+
assert(children.length > 0);
|
|
897
|
+
const length = children[0];
|
|
898
|
+
const fields = children.slice(1);
|
|
899
|
+
assert(length.type === "primitive" && length.dataType === "short");
|
|
900
|
+
assert(fields.every(f => f.type === "field-desc"));
|
|
901
|
+
return {type: "fields", span: node.span, children: [length, ...fields]}
|
|
902
|
+
}
|
|
903
|
+
case "field": {
|
|
904
|
+
assert(children.length >= 2);
|
|
905
|
+
const typecode = children[0];
|
|
906
|
+
const name = children[1];
|
|
907
|
+
assert(typecode.type === "primitive" && typecode.dataType === "unsigned-byte");
|
|
908
|
+
assert(name.type === "utf");
|
|
909
|
+
const tcStr = String.fromCharCode(typecode.value);
|
|
910
|
+
if (tcStr === "[" || tcStr === "L") {
|
|
911
|
+
assert(children.length === 3);
|
|
912
|
+
const className = children[2];
|
|
913
|
+
assertStringNode(className);
|
|
914
|
+
return {type: "field-desc", fieldType: "object", span: node.span, children: [typecode, name, className]}
|
|
915
|
+
} else {
|
|
916
|
+
assert(children.length === 2);
|
|
917
|
+
return {type: "field-desc", fieldType: "primitive", span: node.span, children: [typecode, name]}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
case "values": {
|
|
921
|
+
assert(children.every(c => c.type === "primitive" || c.type === "object"));
|
|
922
|
+
return {type: "values", span: node.span, children: children}
|
|
923
|
+
}
|
|
924
|
+
default:
|
|
925
|
+
throw new exc.InternalError();
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function assertDescNode(node: ast.Node): asserts node is ast.ClassDescNode {
|
|
930
|
+
assert(
|
|
931
|
+
node.type === "object" &&
|
|
932
|
+
(node.objectType === "new-class-desc" || node.objectType === "null" || node.objectType === "prev-object")
|
|
933
|
+
)
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
function assertStringNode(node: ast.Node): asserts node is ast.StringNode {
|
|
937
|
+
assert(
|
|
938
|
+
node.type === "object" &&
|
|
939
|
+
(node.objectType === "new-string" || node.objectType === "prev-object")
|
|
940
|
+
)
|
|
941
|
+
}
|