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