|
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); |
| 1 | +#!/usr/bin/env node |
| 2 | +const Wbc = require('./wbc_lib').Wbc; |
| 3 | +const path = require('path'); |
| 4 | +const fs = require('fs'); |
| 5 | +const { program } = require('commander'); |
| 6 | +const packageConfig = require('../package.json'); |
| 7 | +const Qjsc = require('../index'); |
| 8 | + |
| 9 | +program |
| 10 | + .version(packageConfig.version) |
| 11 | + .description('The WBC file generator') |
| 12 | + .requiredOption('-s, --source <source>', 'the Javascript source file path') |
| 13 | + .requiredOption('-d, --dist <dist>', 'the generated bytecode file path') |
| 14 | + .option('--legacy_kbc1', 'generate legacy kbc1 file, (compact with openkraken project)') |
| 15 | + .parse(process.argv); |
| 16 | + |
| 17 | +let options = program.opts(); |
| 18 | +let source = options.source; |
| 19 | +let dist = options.dist; |
| 20 | + |
| 21 | +let type = "wbc"; |
| 22 | +if (options.legacy_kbc1) { |
| 23 | + type = 'kbc1'; |
| 24 | +} |
125 | 25 |
|
126 |
| - //crc32 |
127 |
| - pointer += bodyChunk.length; |
128 |
| - const adler32Value = this.getAdler32(bodyBuffer.slice(0, pointer)); |
| 26 | +if (!path.isAbsolute(source)) { |
| 27 | + source = path.join(process.cwd(), source); |
| 28 | +} |
| 29 | +if (!path.isAbsolute(dist)) { |
| 30 | + dist = path.join(process.cwd(), dist); |
| 31 | +} |
129 | 32 |
|
130 |
| - // Write the calculated CRC32 value to the last 4 bytes of the Buffer |
131 |
| - bodyBuffer.writeUInt32BE(adler32Value, pointer); |
132 |
| - return bodyBuffer; |
133 |
| - } |
| 33 | +const qjsc = new Qjsc(); |
134 | 34 |
|
135 |
| - generateEnd() { |
136 |
| - let pointer = 0; |
137 |
| - let length = END_LENGTH + END_CHUNK_TYPE; |
138 |
| - const endBuffer = Buffer.alloc(length); |
| 35 | +const sourceFileName = source.split('/').slice(-1)[0].split('.')[0]; |
| 36 | +const sourceCode = fs.readFileSync(source, {encoding: 'utf-8'}); |
139 | 37 |
|
140 |
| - //length |
141 |
| - endBuffer.writeUInt32BE(length, 0); |
| 38 | +let buffer = qjsc.compile(sourceCode); |
142 | 39 |
|
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 |
| - } |
| 40 | +if (type == 'kbc1') { |
| 41 | + let distPath = path.join(dist, sourceFileName + '.kbc1'); |
| 42 | + fs.writeFileSync(distPath, buffer); |
| 43 | + console.log('Quickjs bytecode generated kbc1 at: \n' + distPath); |
| 44 | +} else if (type == 'wbc') { |
| 45 | + const wbc = new Wbc(); |
| 46 | + let wbcBytecode = wbc.generateWbcBytecode(buffer); |
| 47 | + let distPath = path.join(dist, sourceFileName + '.wbc1'); |
| 48 | + fs.writeFileSync(distPath, wbcBytecode); |
| 49 | + console.log('Quickjs bytecode generated wbc1 at: \n' + distPath); |
148 | 50 | }
|
149 |
| - |
150 |
| -exports.Wbc = Wbc |
0 commit comments