Skip to content

Commit

Permalink
test: add test
Browse files Browse the repository at this point in the history
  • Loading branch information
andycall committed Sep 17, 2024
1 parent eaf1651 commit e15dc7e
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 139 deletions.
100 changes: 81 additions & 19 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,90 @@
const WBC = require('./');
const { compileJavaScriptToWbc } = require('./');
const Qjsc = require('./qjsc');
const path = require('path');

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

describe('wbc', () => {
it('throw error when empty arguments', () => {
expect(() => wbc.compile()).toThrowError('1st arguments should be string.');
});
describe('compileJavaScriptToWbc', () => {
const rootDir = path.resolve(__dirname, '.');
const bindings = require('node-gyp-build')(rootDir);
const signatureSize = 9;

describe('Header', () => {
it('header should contains signature', async () => {
const bytes = compileJavaScriptToWbc(testCode);
const uint8Array = new Uint8Array(bytes);
expect(Array.from(uint8Array.slice(0, 9))).toEqual([0x89, 0x57, 0x42, 0x43, 0x31, 0x0D, 0x0A, 0x1A, 0x0A]);
});

it('validate the header chunk header', () => {
const bytes = compileJavaScriptToWbc(testCode);
const uint8Array = new Uint8Array(bytes);
expect([...uint8Array.slice(9, 13)]).toEqual([0, 0, 0, 18]);
});

it('validate the chunk type', () => {
const bytes = compileJavaScriptToWbc(testCode);
const uint8Array = new Uint8Array(bytes);
expect([...uint8Array.slice(13, 17)]).toEqual([0x57, 0x42, 0x48, 0x44]);
});

it('validate compression method', () => {
const bytes = compileJavaScriptToWbc(testCode);
const uint8Array = new Uint8Array(bytes);
expect([...uint8Array.slice(17, 18)]).toEqual([0]);
});

it('throw error when js syntax not correct', () => {
const code = `
function f() {
it('validate compile level', () => {
const bytes = compileJavaScriptToWbc(testCode);
const uint8Array = new Uint8Array(bytes);
expect([...uint8Array.slice(18, 19)]).toEqual([0]);
});

console.log(111;
it('validate bytecode version', () => {
const bytes = compileJavaScriptToWbc(testCode);
const uint8Array = new Uint8Array(bytes);
expect([...uint8Array.slice(19, 20)]).toEqual([0]);
});

`;
expect(() => wbc.compile(code)).toThrowError();
it('validate additional data', () => {
const bytes = compileJavaScriptToWbc(testCode);
const uint8Array = new Uint8Array(bytes);
expect([...uint8Array.slice(20, 23)]).toEqual([0, 0, 0]);
});

it('validate header check sum', () => {
const bytes = compileJavaScriptToWbc(testCode);
const uint8Array = new Uint8Array(bytes);
const headerLength = uint8Array[12];
const bodyOffset = signatureSize + headerLength;
const headerCheckSumOffset = bodyOffset - 4;
const checksum = bindings.getAdler32(uint8Array.slice(signatureSize, headerCheckSumOffset));
const buffer = Buffer.alloc(4);
buffer.writeUInt32BE(checksum, 0);

expect([...new Uint8Array(buffer)]).toEqual([10, 168, 1, 56]);
});
});

it('return bytecode binary', () => {
const code = `
function f() { return 1 + '1234'; }
f();
`;
let buffer = wbc.compile(code);
expect(wbc._evalByteCode(buffer)).toBe('11234');
describe('Body', () => {
const bytes = compileJavaScriptToWbc(testCode);
const uint8Array = new Uint8Array(bytes);
const headeLength = uint8Array.slice(12, 13)[0];
const bodyHeader = uint8Array.slice(headeLength + 9);
const bodyLength = bodyHeader[3];
const bodySlice = uint8Array.slice(headeLength + 9, headeLength + bodyLength + 9);

it('body should contains signature', () => {
expect([...bodySlice.slice(4, 8)]).toEqual([0x57, 0x42, 0x44, 0x59]);
});

it('body content bytes should be executable', () => {
const bytecode = bodySlice.slice(8, bodySlice.length - 4);
const qjs = new Qjsc();
const result = qjs._evalByteCode(Buffer.from(bytecode.buffer));
expect(result).toBe('13');
});
});


});
243 changes: 123 additions & 120 deletions wbc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const path = require('path');
const path = require('path');

//each part of header length
const HEADER_LENGTH = 4;
Expand All @@ -19,130 +19,133 @@ const END_LENGTH = 4;
const END_CHUNK_TYPE = 4;

const HEADER_FIELDS = [
HEADER_LENGTH,
HEADER_CHUNK_TYPE,
HEADER_COMPRESSION_METHOD,
HEADER_COMPILE_LEVEL,
HEADER_BYTECODE_VERSION,
HEADER_ADDITIONAL_DATA,
HEADER_CRC32
HEADER_LENGTH,
HEADER_CHUNK_TYPE,
HEADER_COMPRESSION_METHOD,
HEADER_COMPILE_LEVEL,
HEADER_BYTECODE_VERSION,
HEADER_ADDITIONAL_DATA,
HEADER_CRC32
];

class Wbc {
constructor(options = {}) {
const rootDir = path.resolve(__dirname, '.');
this._bindings = require('node-gyp-build')(rootDir);
constructor(options = {}) {
const rootDir = path.resolve(__dirname, '.');
this._bindings = require('node-gyp-build')(rootDir);
}

getAdler32(buffer) {
return this._bindings.getAdler32(buffer);
}

generateWbcBytecode(oriBody) {
let signatureBuffer = this.generateSignature();
let headerBuffer = this.generateHeader();
let bodyBuffer = this.generateBody(oriBody);
let endBuffer = this.generateEnd();

let totalLength = signatureBuffer.length + headerBuffer.length + bodyBuffer.length + endBuffer.length;
let bytecodeBuffer = Buffer.concat([signatureBuffer, headerBuffer, bodyBuffer, endBuffer], totalLength);
return bytecodeBuffer;
}

//0x89 0x57 0x42 0x43 0x31 0x0D 0x0A 0x1A 0x0A
generateSignature() {
const buffer = Buffer.alloc(9);
buffer.writeUInt8(0x89, 0);
buffer.writeUInt8(0x57, 1);
buffer.writeUInt8(0x42, 2);
buffer.writeUInt8(0x43, 3);
buffer.writeUInt8(0x31, 4);
buffer.writeUInt8(0x0D, 5);
buffer.writeUInt8(0x0A, 6);
buffer.writeUInt8(0x1A, 7);
buffer.writeUInt8(0x0A, 8);
return buffer;
}

generateHeader() {
let pointer = 0;
let length = this.calculateHeaderLength();
const headerBuffer = Buffer.alloc(length);

// Length
headerBuffer.writeUInt32BE(length, 0);
pointer += HEADER_LENGTH;

// ASCII value for the letter WBHD (0x57 0x42 0x48 0x44 in hexadecimal)
headerBuffer.writeUInt32BE(0x57424844, pointer);
pointer += HEADER_CHUNK_TYPE;

// Compression method
headerBuffer.writeUInt8(0, pointer);
pointer += HEADER_COMPRESSION_METHOD;

// Compile level
headerBuffer.writeUInt8(0, pointer);
pointer += HEADER_COMPILE_LEVEL;

// Bytecode version
headerBuffer.writeUInt8(0, pointer);
pointer += HEADER_BYTECODE_VERSION;

// Additional data zone
for (let i = 0; i < HEADER_ADDITIONAL_DATA; i++) {
headerBuffer.writeUint8(0, pointer);
pointer += 1;
}

getAdler32(buffer) {
return this._bindings.getAdler32(buffer);
}

generateWbcBytecode(oriBody) {
let signatureBuffer = this.generateSignature();
let headerBuffer = this.generateHeader();
let bodyBuffer = this.generateBody(oriBody);
let endBuffer = this.generateEnd();

let totalLength = signatureBuffer.length + headerBuffer.length + bodyBuffer.length + endBuffer.length;
let bytecodeBuffer = Buffer.concat([signatureBuffer, headerBuffer, bodyBuffer, endBuffer], totalLength);
return bytecodeBuffer;
}

//0x89 0x57 0x42 0x43 0x31 0x0D 0x0A 0x1A 0x0A
generateSignature() {
const buffer = Buffer.alloc(9);
buffer.writeUInt8(0x89, 0);
buffer.writeUInt8(0x57, 1);
buffer.writeUInt8(0x42, 2);
buffer.writeUInt8(0x43, 3);
buffer.writeUInt8(0x31, 4);
buffer.writeUInt8(0x0D, 5);
buffer.writeUInt8(0x0A, 6);
buffer.writeUInt8(0x1A, 7);
buffer.writeUInt8(0x0A, 8);
return buffer;
}

generateHeader() {
let pointer = 0;
let length = this.calculateHeaderLength();
const headerBuffer = Buffer.alloc(length);

//length
headerBuffer.writeUInt32BE(length, 0);

//ASCII value for the letter WBHD (0x57 0x42 0x48 0x44 in hexadecimal)
pointer += HEADER_LENGTH;
headerBuffer.writeUInt32BE(0x57424844, pointer);

//compressionMethod
pointer += HEADER_CHUNK_TYPE;
headerBuffer.writeUInt8(0, pointer);

//compileLevel
pointer += HEADER_COMPRESSION_METHOD;
headerBuffer.writeUInt8(0, pointer);

//bytecodeVersion
pointer += HEADER_COMPILE_LEVEL;
headerBuffer.writeUInt8(0, pointer);

//additionalData 3bytes
pointer += HEADER_BYTECODE_VERSION;

//Only the CRC32 value of the first 14 bytes is calculated because the last CRC32 field is reserved
pointer += HEADER_ADDITIONAL_DATA;
const adler32Value = this.getAdler32(headerBuffer.slice(0, pointer));

// Write the calculated CRC32 value to the last 4 bytes of the Buffer
headerBuffer.writeUInt32BE(adler32Value, pointer);
return headerBuffer;
}

calculateHeaderLength() {
return HEADER_FIELDS.reduce((sum, value) => sum + value, 0);
}

generateBody(oriBody) {
let pointer = 0;
var bodyChunk = oriBody;
let length = BODY_LENGTH + BODY_CHUNK_TYPE + bodyChunk.length + BODY_CRC32;
const bodyBuffer = Buffer.alloc(length);

//length
bodyBuffer.writeUInt32BE(length, 0);

//ASCII value for the letter WBDY (0x57 0x42 0x44 0x59 in hexadecimal)
pointer += BODY_LENGTH;
bodyBuffer.writeUInt32BE(0x57424459, pointer);

//body chunk
pointer += BODY_CHUNK_TYPE;
bodyChunk.copy(bodyBuffer, pointer);

//crc32
pointer += bodyChunk.length;
const adler32Value = this.getAdler32(bodyBuffer.slice(0, pointer));

// Write the calculated CRC32 value to the last 4 bytes of the Buffer
bodyBuffer.writeUInt32BE(adler32Value, pointer);
return bodyBuffer;
}

generateEnd() {
let pointer = 0;
let length = END_LENGTH + END_CHUNK_TYPE;
const endBuffer = Buffer.alloc(length);

//length
endBuffer.writeUInt32BE(length, 0);

//The ASCII values for the letters 'WEND' (0x57 0x45 0x4e 0x44 in hexadecimal).
pointer += END_LENGTH;
endBuffer.writeUInt32BE(0x57454e44, pointer);
return endBuffer;
}
// Checksum
const adler32Value = this.getAdler32(headerBuffer.slice(0, pointer));
// Only the CRC32 value of the first 14 bytes is calculated because the last CRC32 field is reserved
// Write the calculated CRC32 value to the last 4 bytes of the Buffer
headerBuffer.writeUInt32BE(adler32Value, pointer);
return headerBuffer;
}

calculateHeaderLength() {
return HEADER_FIELDS.reduce((sum, value) => sum + value, 0);
}

generateBody(oriBody) {
let pointer = 0;
var bodyChunk = oriBody;
let length = BODY_LENGTH + BODY_CHUNK_TYPE + bodyChunk.length + BODY_CRC32;
const bodyBuffer = Buffer.alloc(length);

//length
bodyBuffer.writeUInt32BE(length, 0);

//ASCII value for the letter WBDY (0x57 0x42 0x44 0x59 in hexadecimal)
pointer += BODY_LENGTH;
bodyBuffer.writeUInt32BE(0x57424459, pointer);

//body chunk
pointer += BODY_CHUNK_TYPE;
bodyChunk.copy(bodyBuffer, pointer);

//crc32
pointer += bodyChunk.length;
const adler32Value = this.getAdler32(bodyBuffer.slice(0, pointer));

// Write the calculated CRC32 value to the last 4 bytes of the Buffer
bodyBuffer.writeUInt32BE(adler32Value, pointer);
return bodyBuffer;
}

generateEnd() {
let pointer = 0;
let length = END_LENGTH + END_CHUNK_TYPE;
const endBuffer = Buffer.alloc(length);

//length
endBuffer.writeUInt32BE(length, 0);

//The ASCII values for the letters 'WEND' (0x57 0x45 0x4e 0x44 in hexadecimal).
pointer += END_LENGTH;
endBuffer.writeUInt32BE(0x57454e44, pointer);
return endBuffer;
}
}

exports.Wbc = Wbc

0 comments on commit e15dc7e

Please sign in to comment.