Files: cfc8d49135488f7554210234b3a503c2dbd6c6a5 / lib / fabricator.js
4407 bytesRaw
1 | import { log } from './log.js'; |
2 | import { spawn } from 'child_process'; |
3 | |
4 | const script = ` |
5 | var vm = require('vm'); |
6 | var module = require('module'); |
7 | var stdin = Buffer.alloc(0); |
8 | process.stdin.on('data', function (data) { |
9 | stdin = Buffer.concat([ stdin, data ]); |
10 | if (stdin.length >= 4) { |
11 | var sizeOfSnap = stdin.readInt32LE(0); |
12 | if (stdin.length >= 4 + sizeOfSnap + 4) { |
13 | var sizeOfBody = stdin.readInt32LE(4 + sizeOfSnap); |
14 | if (stdin.length >= 4 + sizeOfSnap + 4 + sizeOfBody) { |
15 | var snap = stdin.toString('utf8', 4, 4 + sizeOfSnap); |
16 | var body = Buffer.alloc(sizeOfBody); |
17 | var startOfBody = 4 + sizeOfSnap + 4; |
18 | stdin.copy(body, 0, startOfBody, startOfBody + sizeOfBody); |
19 | stdin = Buffer.alloc(0); |
20 | var code = module.wrap(body); |
21 | var s = new vm.Script(code, { |
22 | filename: snap, |
23 | produceCachedData: true, |
24 | sourceless: true |
25 | }); |
26 | if (!s.cachedDataProduced) { |
27 | console.error('Pkg: Cached data not produced.'); |
28 | process.exit(2); |
29 | } |
30 | var h = Buffer.alloc(4); |
31 | var b = s.cachedData; |
32 | h.writeInt32LE(b.length, 0); |
33 | process.stdout.write(h); |
34 | process.stdout.write(b); |
35 | } |
36 | } |
37 | } |
38 | }); |
39 | process.stdin.resume(); |
40 | `; |
41 | |
42 | const children = {}; |
43 | |
44 | export function fabricate (bakes, fabricator, snap, body, cb) { |
45 | bakes = bakes.filter(function (bake) { |
46 | // list of bakes that don't influence the bytecode |
47 | const bake2 = bake.replace(/_/g, '-'); |
48 | return ![ '--prof', '--v8-options', '--trace-opt', '--trace-deopt' ].includes(bake2); |
49 | }); |
50 | |
51 | const cmd = fabricator.binaryPath; |
52 | const key = JSON.stringify([ cmd, bakes ]); |
53 | let child = children[key]; |
54 | |
55 | if (!child) { |
56 | const stderr = log.debugMode ? process.stdout : 'ignore'; |
57 | child = children[key] = spawn( |
58 | cmd, bakes.concat('-e', script), |
59 | { stdio: [ 'pipe', 'pipe', stderr ], |
60 | env: { PKG_EXECPATH: 'PKG_INVOKE_NODEJS' } } |
61 | ); |
62 | } |
63 | |
64 | function kill () { |
65 | delete children[key]; |
66 | child.kill(); |
67 | } |
68 | |
69 | let stdout = Buffer.alloc(0); |
70 | |
71 | function onError (error) { |
72 | removeListeners(); |
73 | kill(); |
74 | cb(new Error(`Failed to make bytecode ${fabricator.nodeRange}-${fabricator.arch} for file ${snap} error (${error.message})`)); |
75 | } |
76 | |
77 | function onClose (code) { |
78 | removeListeners(); |
79 | kill(); |
80 | if (code !== 0) { |
81 | return cb(new Error(`Failed to make bytecode ${fabricator.nodeRange}-${fabricator.arch} for file ${snap}`)); |
82 | } else { |
83 | console.log(stdout.toString()); |
84 | return cb(new Error(`${cmd} closed unexpectedly`)); |
85 | } |
86 | } |
87 | |
88 | function onData (data) { |
89 | stdout = Buffer.concat([ stdout, data ]); |
90 | if (stdout.length >= 4) { |
91 | const sizeOfBlob = stdout.readInt32LE(0); |
92 | if (stdout.length >= 4 + sizeOfBlob) { |
93 | const blob = Buffer.alloc(sizeOfBlob); |
94 | stdout.copy(blob, 0, 4, 4 + sizeOfBlob); |
95 | removeListeners(); |
96 | return cb(undefined, blob); |
97 | } |
98 | } |
99 | } |
100 | |
101 | child.on('error', onError); |
102 | child.on('close', onClose); |
103 | child.stdin.on('error', onError); |
104 | child.stdout.on('error', onError); |
105 | child.stdout.on('data', onData); |
106 | function removeListeners () { |
107 | child.removeListener('error', onError); |
108 | child.removeListener('close', onClose); |
109 | child.stdin.removeListener('error', onError); |
110 | child.stdout.removeListener('error', onError); |
111 | child.stdout.removeListener('data', onData); |
112 | } |
113 | |
114 | const h = Buffer.alloc(4); |
115 | let b = Buffer.from(snap); |
116 | h.writeInt32LE(b.length, 0); |
117 | child.stdin.write(h); |
118 | child.stdin.write(b); |
119 | b = body; |
120 | h.writeInt32LE(b.length, 0); |
121 | child.stdin.write(h); |
122 | child.stdin.write(b); |
123 | } |
124 | |
125 | export function fabricateTwice (bakes, fabricator, snap, body, cb) { |
126 | fabricate(bakes, fabricator, snap, body, (error, buffer) => { |
127 | // node0 can not produce second time, even if first time produced fine, |
128 | // probably because of 'filename' cache. also, there are wierd cases |
129 | // when node4 can not compile as well, for example file 'lib/js-yaml/dumper.js' |
130 | // of package js-yaml@3.9.0 does not get bytecode second time on node4-win-x64 |
131 | if (error) return fabricate(bakes, fabricator, snap, body, cb); |
132 | cb(undefined, buffer); |
133 | }); |
134 | } |
135 | |
136 | export function shutdown () { |
137 | for (const key in children) { |
138 | const child = children[key]; |
139 | delete children[key]; |
140 | child.kill(); |
141 | } |
142 | } |
143 |
Built with git-ssb-web