-
Notifications
You must be signed in to change notification settings - Fork 0
/
pre.js
212 lines (189 loc) · 8.5 KB
/
pre.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/*
SANE WebAssembly (sane-wasm)
Copyright (C) 2023 Gonçalo MB <[email protected]>
GNU GPLv2
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
GNU LGPLv2.1
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
*/
{
class EnumSANE {
// static reverseObject = Symbol("reverseObject"); // not supported
static promote(obj) {
obj.__proto__ = this.prototype;
obj[this.reverseObject] = {};
Object.keys(obj).forEach(k => {
obj[obj[k]] = k; // reverse mapping à la typescript
obj[this.reverseObject][obj[k]] = k; // XXX: old reverse mapping, consider removing
});
}
asString(i) {
return this[this.constructor.reverseObject][i] || null;
}
}
EnumSANE.reverseObject = Symbol("reverseObject");
// Many SANE functions are expected to return a promise, even though
// they originally are blocking calls. This happens because we use
// emscripten's asyncify feature.
// https://emscripten.org/docs/porting/asyncify.html
// So calls that would block (because they call sleep, access libusb, wait
// for web apis, etc.) just return a promise.
// A call only returns a promise if it falls in one of those cases that
// triggers asyncify.
// To normalize the API, we wrap SANE functions that have change to be
// async in a promise (to make sure they always return a promise).
// We call that promisify.
// Because of the complex code path of all SANE backends, and the use of
// indirect calls, it's not known if all SANE functions need to be wrapped.
// XXX: If any new functions are found to possibly return a promise,
// add them to the list. THIS WILL CAUSE THE PUBLIC API TO CHANGE.
const libFunctionsAsync = { // true = promise return is expected
sane_get_state: false, // sync, implemented in glue.cpp
sane_init: false, // sync?
sane_exit: true, // async, exiting when device is open
sane_get_devices: true, // async
sane_open: true, // async
sane_close: true, // async
sane_get_option_descriptor: false, // sync
sane_control_option_get_value: true, // some options are async
sane_control_option_set_value: true, // some options are async
sane_control_option_set_auto: true, // suspected of possibly being async
sane_get_parameters: false, // sync?
sane_start: false, // sync?
sane_read: true, // async, waits for scan completion
sane_cancel: true, // async, waits for scan completion
sane_strstatus: false, // sync
}
Module.sane = {
debugSANE: false,
debugUSB: false,
debugFunctionCalls: false,
debugTestDevices: 0,
promisify: true,
promisifyQueue: true,
...(Module.sane || {})
};
Module.preRun = Module.preRun || [];
Module.postRun = Module.postRun || [];
Module.preRun.push(() => {
ENV.SANE_CONFIG_DIR = "/etc/sane.d";
if (Module.sane.debugSANE) {
// see ./deps/backends/sanei/sanei_init_debug.c
// each sane backend uses different numbers for different verbosity levels
// we might need to specify each one individually (using 9 for now)
// SANE_DEBUG_GLOBAL is not standard, it's added with a patch
// there are some very verbose levels that should not be used here
ENV.SANE_DEBUG_GLOBAL = "9";
}
if (Module.sane.debugUSB) {
// see ./deps/libusb/libusb/core.c
// LIBUSB_LOG_LEVEL_DEBUG = 4
ENV.LIBUSB_DEBUG = "4";
}
});
// We don't really use the main() function for any library calls,
// so using preRun or postRun here is almost identical.
// The library just starts working when we call sane_init externally.
Module.postRun.push(() => {
// promote enums to more useful objects
["SANE_STATUS", "SANE_TYPE", "SANE_UNIT", "SANE_CONSTRAINT", "SANE_FRAME"].forEach(s => {
EnumSANE.promote(Module[s]);
});
// set test backend number of devices
if (Module.sane.debugTestDevices) {
let buf = Module.FS.readFile("/etc/sane.d/test.conf");
let match = false;
const arr = (new TextDecoder()).decode(buf).split("\n").map(l => {
if (l.match(/^\s*number_of_devices\s+.*$/)) {
match = true;
return `number_of_devices ${Module.sane.debugTestDevices}`;
}
return l;
});
if (!match) {
arr.push(`number_of_devices ${Module.sane.debugTestDevices}`, "");
}
buf = (new TextEncoder()).encode(arr.join("\n"));
Module.FS.writeFile("/etc/sane.d/test.conf", buf);
}
// enable debug for sane function calls
if (Module.sane.debugFunctionCalls) {
const wrap = (fn) => (...args) => {
const res = fn(...args);
console.info(`LibSANE.${fn.name}`, args, res);
if (libFunctionsAsync[fn.name] === false && res instanceof Promise) {
console.warn(`LibSANE.${fn.name} returned promise unexpectedly`);
}
return res;
};
Object.keys(libFunctionsAsync).forEach(name => {
Module[name] = wrap(Module[name]);
});
}
// wrap sane functions with promises to normalize api
if (Module.sane.promisify) {
const wrap = (fn) => (...args) => {
const res = fn(...args);
return res instanceof Promise ? res : Promise.resolve(res);
};
Object.keys(libFunctionsAsync).filter(name => libFunctionsAsync[name]).forEach(name => {
Module[name] = wrap(Module[name]);
});
}
// wrap sane functions using promise queue to prevent further calls
// while other async functions are still running (not supported)
if (Module.sane.promisify && Module.sane.promisifyQueue) {
const queue = [];
let busy = false;
const dequeue = () => {
if (queue.length) {
queue.shift()();
} else {
busy = false;
}
}
const enqueue = (fn) => {
return new Promise((resolve, reject) => {
queue.push(() => fn().then(resolve, reject).finally(dequeue));
});
}
const wrap = (fn) => (...args) => {
if (busy) {
return enqueue(() => fn(...args));
}
busy = true;
return fn(...args).finally(dequeue);
};
const wrapSync = (fn) => (...args) => {
if (busy) {
throw new Error("There is an async operation in progress");
}
return fn(...args);
};
Object.keys(libFunctionsAsync).forEach(name => {
Module[name] = (libFunctionsAsync[name] ? wrap : wrapSync)(Module[name]);
});
}
});
}