Skip to content

Commit e15dc7e

Browse files
committed
test: add test
1 parent eaf1651 commit e15dc7e

File tree

2 files changed

+204
-139
lines changed

2 files changed

+204
-139
lines changed

test.js

Lines changed: 81 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,90 @@
1-
const WBC = require('./');
1+
const { compileJavaScriptToWbc } = require('./');
2+
const Qjsc = require('./qjsc');
3+
const path = require('path');
24

3-
const wbc = new WBC();
5+
const testCode = '1 + "3"';
46

5-
describe('wbc', () => {
6-
it('throw error when empty arguments', () => {
7-
expect(() => wbc.compile()).toThrowError('1st arguments should be string.');
8-
});
7+
describe('compileJavaScriptToWbc', () => {
8+
const rootDir = path.resolve(__dirname, '.');
9+
const bindings = require('node-gyp-build')(rootDir);
10+
const signatureSize = 9;
11+
12+
describe('Header', () => {
13+
it('header should contains signature', async () => {
14+
const bytes = compileJavaScriptToWbc(testCode);
15+
const uint8Array = new Uint8Array(bytes);
16+
expect(Array.from(uint8Array.slice(0, 9))).toEqual([0x89, 0x57, 0x42, 0x43, 0x31, 0x0D, 0x0A, 0x1A, 0x0A]);
17+
});
18+
19+
it('validate the header chunk header', () => {
20+
const bytes = compileJavaScriptToWbc(testCode);
21+
const uint8Array = new Uint8Array(bytes);
22+
expect([...uint8Array.slice(9, 13)]).toEqual([0, 0, 0, 18]);
23+
});
24+
25+
it('validate the chunk type', () => {
26+
const bytes = compileJavaScriptToWbc(testCode);
27+
const uint8Array = new Uint8Array(bytes);
28+
expect([...uint8Array.slice(13, 17)]).toEqual([0x57, 0x42, 0x48, 0x44]);
29+
});
30+
31+
it('validate compression method', () => {
32+
const bytes = compileJavaScriptToWbc(testCode);
33+
const uint8Array = new Uint8Array(bytes);
34+
expect([...uint8Array.slice(17, 18)]).toEqual([0]);
35+
});
936

10-
it('throw error when js syntax not correct', () => {
11-
const code = `
12-
function f() {
37+
it('validate compile level', () => {
38+
const bytes = compileJavaScriptToWbc(testCode);
39+
const uint8Array = new Uint8Array(bytes);
40+
expect([...uint8Array.slice(18, 19)]).toEqual([0]);
41+
});
1342

14-
console.log(111;
43+
it('validate bytecode version', () => {
44+
const bytes = compileJavaScriptToWbc(testCode);
45+
const uint8Array = new Uint8Array(bytes);
46+
expect([...uint8Array.slice(19, 20)]).toEqual([0]);
47+
});
1548

16-
`;
17-
expect(() => wbc.compile(code)).toThrowError();
49+
it('validate additional data', () => {
50+
const bytes = compileJavaScriptToWbc(testCode);
51+
const uint8Array = new Uint8Array(bytes);
52+
expect([...uint8Array.slice(20, 23)]).toEqual([0, 0, 0]);
53+
});
54+
55+
it('validate header check sum', () => {
56+
const bytes = compileJavaScriptToWbc(testCode);
57+
const uint8Array = new Uint8Array(bytes);
58+
const headerLength = uint8Array[12];
59+
const bodyOffset = signatureSize + headerLength;
60+
const headerCheckSumOffset = bodyOffset - 4;
61+
const checksum = bindings.getAdler32(uint8Array.slice(signatureSize, headerCheckSumOffset));
62+
const buffer = Buffer.alloc(4);
63+
buffer.writeUInt32BE(checksum, 0);
64+
65+
expect([...new Uint8Array(buffer)]).toEqual([10, 168, 1, 56]);
66+
});
1867
});
1968

20-
it('return bytecode binary', () => {
21-
const code = `
22-
function f() { return 1 + '1234'; }
23-
f();
24-
`;
25-
let buffer = wbc.compile(code);
26-
expect(wbc._evalByteCode(buffer)).toBe('11234');
69+
describe('Body', () => {
70+
const bytes = compileJavaScriptToWbc(testCode);
71+
const uint8Array = new Uint8Array(bytes);
72+
const headeLength = uint8Array.slice(12, 13)[0];
73+
const bodyHeader = uint8Array.slice(headeLength + 9);
74+
const bodyLength = bodyHeader[3];
75+
const bodySlice = uint8Array.slice(headeLength + 9, headeLength + bodyLength + 9);
76+
77+
it('body should contains signature', () => {
78+
expect([...bodySlice.slice(4, 8)]).toEqual([0x57, 0x42, 0x44, 0x59]);
79+
});
80+
81+
it('body content bytes should be executable', () => {
82+
const bytecode = bodySlice.slice(8, bodySlice.length - 4);
83+
const qjs = new Qjsc();
84+
const result = qjs._evalByteCode(Buffer.from(bytecode.buffer));
85+
expect(result).toBe('13');
86+
});
2787
});
88+
89+
2890
});

wbc.js

Lines changed: 123 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const path = require('path');
1+
const path = require('path');
22

33
//each part of header length
44
const HEADER_LENGTH = 4;
@@ -19,130 +19,133 @@ const END_LENGTH = 4;
1919
const END_CHUNK_TYPE = 4;
2020

2121
const HEADER_FIELDS = [
22-
HEADER_LENGTH,
23-
HEADER_CHUNK_TYPE,
24-
HEADER_COMPRESSION_METHOD,
25-
HEADER_COMPILE_LEVEL,
26-
HEADER_BYTECODE_VERSION,
27-
HEADER_ADDITIONAL_DATA,
28-
HEADER_CRC32
22+
HEADER_LENGTH,
23+
HEADER_CHUNK_TYPE,
24+
HEADER_COMPRESSION_METHOD,
25+
HEADER_COMPILE_LEVEL,
26+
HEADER_BYTECODE_VERSION,
27+
HEADER_ADDITIONAL_DATA,
28+
HEADER_CRC32
2929
];
3030

3131
class Wbc {
32-
constructor(options = {}) {
33-
const rootDir = path.resolve(__dirname, '.');
34-
this._bindings = require('node-gyp-build')(rootDir);
32+
constructor(options = {}) {
33+
const rootDir = path.resolve(__dirname, '.');
34+
this._bindings = require('node-gyp-build')(rootDir);
35+
}
36+
37+
getAdler32(buffer) {
38+
return this._bindings.getAdler32(buffer);
39+
}
40+
41+
generateWbcBytecode(oriBody) {
42+
let signatureBuffer = this.generateSignature();
43+
let headerBuffer = this.generateHeader();
44+
let bodyBuffer = this.generateBody(oriBody);
45+
let endBuffer = this.generateEnd();
46+
47+
let totalLength = signatureBuffer.length + headerBuffer.length + bodyBuffer.length + endBuffer.length;
48+
let bytecodeBuffer = Buffer.concat([signatureBuffer, headerBuffer, bodyBuffer, endBuffer], totalLength);
49+
return bytecodeBuffer;
50+
}
51+
52+
//0x89 0x57 0x42 0x43 0x31 0x0D 0x0A 0x1A 0x0A
53+
generateSignature() {
54+
const buffer = Buffer.alloc(9);
55+
buffer.writeUInt8(0x89, 0);
56+
buffer.writeUInt8(0x57, 1);
57+
buffer.writeUInt8(0x42, 2);
58+
buffer.writeUInt8(0x43, 3);
59+
buffer.writeUInt8(0x31, 4);
60+
buffer.writeUInt8(0x0D, 5);
61+
buffer.writeUInt8(0x0A, 6);
62+
buffer.writeUInt8(0x1A, 7);
63+
buffer.writeUInt8(0x0A, 8);
64+
return buffer;
65+
}
66+
67+
generateHeader() {
68+
let pointer = 0;
69+
let length = this.calculateHeaderLength();
70+
const headerBuffer = Buffer.alloc(length);
71+
72+
// Length
73+
headerBuffer.writeUInt32BE(length, 0);
74+
pointer += HEADER_LENGTH;
75+
76+
// ASCII value for the letter WBHD (0x57 0x42 0x48 0x44 in hexadecimal)
77+
headerBuffer.writeUInt32BE(0x57424844, pointer);
78+
pointer += HEADER_CHUNK_TYPE;
79+
80+
// Compression method
81+
headerBuffer.writeUInt8(0, pointer);
82+
pointer += HEADER_COMPRESSION_METHOD;
83+
84+
// Compile level
85+
headerBuffer.writeUInt8(0, pointer);
86+
pointer += HEADER_COMPILE_LEVEL;
87+
88+
// Bytecode version
89+
headerBuffer.writeUInt8(0, pointer);
90+
pointer += HEADER_BYTECODE_VERSION;
91+
92+
// Additional data zone
93+
for (let i = 0; i < HEADER_ADDITIONAL_DATA; i++) {
94+
headerBuffer.writeUint8(0, pointer);
95+
pointer += 1;
3596
}
3697

37-
getAdler32(buffer) {
38-
return this._bindings.getAdler32(buffer);
39-
}
40-
41-
generateWbcBytecode(oriBody) {
42-
let signatureBuffer = this.generateSignature();
43-
let headerBuffer = this.generateHeader();
44-
let bodyBuffer = this.generateBody(oriBody);
45-
let endBuffer = this.generateEnd();
46-
47-
let totalLength = signatureBuffer.length + headerBuffer.length + bodyBuffer.length + endBuffer.length;
48-
let bytecodeBuffer = Buffer.concat([signatureBuffer, headerBuffer, bodyBuffer, endBuffer], totalLength);
49-
return bytecodeBuffer;
50-
}
51-
52-
//0x89 0x57 0x42 0x43 0x31 0x0D 0x0A 0x1A 0x0A
53-
generateSignature() {
54-
const buffer = Buffer.alloc(9);
55-
buffer.writeUInt8(0x89, 0);
56-
buffer.writeUInt8(0x57, 1);
57-
buffer.writeUInt8(0x42, 2);
58-
buffer.writeUInt8(0x43, 3);
59-
buffer.writeUInt8(0x31, 4);
60-
buffer.writeUInt8(0x0D, 5);
61-
buffer.writeUInt8(0x0A, 6);
62-
buffer.writeUInt8(0x1A, 7);
63-
buffer.writeUInt8(0x0A, 8);
64-
return buffer;
65-
}
66-
67-
generateHeader() {
68-
let pointer = 0;
69-
let length = this.calculateHeaderLength();
70-
const headerBuffer = Buffer.alloc(length);
71-
72-
//length
73-
headerBuffer.writeUInt32BE(length, 0);
74-
75-
//ASCII value for the letter WBHD (0x57 0x42 0x48 0x44 in hexadecimal)
76-
pointer += HEADER_LENGTH;
77-
headerBuffer.writeUInt32BE(0x57424844, pointer);
78-
79-
//compressionMethod
80-
pointer += HEADER_CHUNK_TYPE;
81-
headerBuffer.writeUInt8(0, pointer);
82-
83-
//compileLevel
84-
pointer += HEADER_COMPRESSION_METHOD;
85-
headerBuffer.writeUInt8(0, pointer);
86-
87-
//bytecodeVersion
88-
pointer += HEADER_COMPILE_LEVEL;
89-
headerBuffer.writeUInt8(0, pointer);
90-
91-
//additionalData 3bytes
92-
pointer += HEADER_BYTECODE_VERSION;
93-
94-
//Only the CRC32 value of the first 14 bytes is calculated because the last CRC32 field is reserved
95-
pointer += HEADER_ADDITIONAL_DATA;
96-
const adler32Value = this.getAdler32(headerBuffer.slice(0, pointer));
97-
98-
// Write the calculated CRC32 value to the last 4 bytes of the Buffer
99-
headerBuffer.writeUInt32BE(adler32Value, pointer);
100-
return headerBuffer;
101-
}
102-
103-
calculateHeaderLength() {
104-
return HEADER_FIELDS.reduce((sum, value) => sum + value, 0);
105-
}
106-
107-
generateBody(oriBody) {
108-
let pointer = 0;
109-
var bodyChunk = oriBody;
110-
let length = BODY_LENGTH + BODY_CHUNK_TYPE + bodyChunk.length + BODY_CRC32;
111-
const bodyBuffer = Buffer.alloc(length);
112-
113-
//length
114-
bodyBuffer.writeUInt32BE(length, 0);
115-
116-
//ASCII value for the letter WBDY (0x57 0x42 0x44 0x59 in hexadecimal)
117-
pointer += BODY_LENGTH;
118-
bodyBuffer.writeUInt32BE(0x57424459, pointer);
119-
120-
//body chunk
121-
pointer += BODY_CHUNK_TYPE;
122-
bodyChunk.copy(bodyBuffer, pointer);
123-
124-
//crc32
125-
pointer += bodyChunk.length;
126-
const adler32Value = this.getAdler32(bodyBuffer.slice(0, pointer));
127-
128-
// Write the calculated CRC32 value to the last 4 bytes of the Buffer
129-
bodyBuffer.writeUInt32BE(adler32Value, pointer);
130-
return bodyBuffer;
131-
}
132-
133-
generateEnd() {
134-
let pointer = 0;
135-
let length = END_LENGTH + END_CHUNK_TYPE;
136-
const endBuffer = Buffer.alloc(length);
137-
138-
//length
139-
endBuffer.writeUInt32BE(length, 0);
140-
141-
//The ASCII values for the letters 'WEND' (0x57 0x45 0x4e 0x44 in hexadecimal).
142-
pointer += END_LENGTH;
143-
endBuffer.writeUInt32BE(0x57454e44, pointer);
144-
return endBuffer;
145-
}
98+
// Checksum
99+
const adler32Value = this.getAdler32(headerBuffer.slice(0, pointer));
100+
// Only the CRC32 value of the first 14 bytes is calculated because the last CRC32 field is reserved
101+
// Write the calculated CRC32 value to the last 4 bytes of the Buffer
102+
headerBuffer.writeUInt32BE(adler32Value, pointer);
103+
return headerBuffer;
104+
}
105+
106+
calculateHeaderLength() {
107+
return HEADER_FIELDS.reduce((sum, value) => sum + value, 0);
108+
}
109+
110+
generateBody(oriBody) {
111+
let pointer = 0;
112+
var bodyChunk = oriBody;
113+
let length = BODY_LENGTH + BODY_CHUNK_TYPE + bodyChunk.length + BODY_CRC32;
114+
const bodyBuffer = Buffer.alloc(length);
115+
116+
//length
117+
bodyBuffer.writeUInt32BE(length, 0);
118+
119+
//ASCII value for the letter WBDY (0x57 0x42 0x44 0x59 in hexadecimal)
120+
pointer += BODY_LENGTH;
121+
bodyBuffer.writeUInt32BE(0x57424459, pointer);
122+
123+
//body chunk
124+
pointer += BODY_CHUNK_TYPE;
125+
bodyChunk.copy(bodyBuffer, pointer);
126+
127+
//crc32
128+
pointer += bodyChunk.length;
129+
const adler32Value = this.getAdler32(bodyBuffer.slice(0, pointer));
130+
131+
// Write the calculated CRC32 value to the last 4 bytes of the Buffer
132+
bodyBuffer.writeUInt32BE(adler32Value, pointer);
133+
return bodyBuffer;
134+
}
135+
136+
generateEnd() {
137+
let pointer = 0;
138+
let length = END_LENGTH + END_CHUNK_TYPE;
139+
const endBuffer = Buffer.alloc(length);
140+
141+
//length
142+
endBuffer.writeUInt32BE(length, 0);
143+
144+
//The ASCII values for the letters 'WEND' (0x57 0x45 0x4e 0x44 in hexadecimal).
145+
pointer += END_LENGTH;
146+
endBuffer.writeUInt32BE(0x57454e44, pointer);
147+
return endBuffer;
148+
}
146149
}
147150

148151
exports.Wbc = Wbc

0 commit comments

Comments
 (0)