Skip to content

Commit 148c886

Browse files
committed
Add type checking for all JS library decorators
See #20107
1 parent df74d74 commit 148c886

File tree

5 files changed

+72
-23
lines changed

5 files changed

+72
-23
lines changed

ChangeLog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ See docs/process.md for more on how version tagging works.
3030
incoming module but forget to include them in `-sINCOMING_MODULE_API`
3131
will see an error in debug builds so this change will not generate any
3232
silent failures.
33+
- JS library decorators such as `__deps` and `__async` are now type checked so
34+
that errors are not silently ignored.
3335

3436
3.1.45 - 08/23/23
3537
-----------------

src/jsifier.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ function(${args}) {
227227

228228
const proxyingMode = LibraryManager.library[symbol + '__proxy'];
229229
if (proxyingMode) {
230-
if (proxyingMode !== 'sync' && proxyingMode !== 'async') {
230+
if (proxyingMode !== 'sync' && proxyingMode !== 'async' && proxyingMode !== 'none') {
231231
throw new Error(`Invalid proxyingMode ${symbol}__proxy: '${proxyingMode}' specified!`);
232232
}
233233
if (SHARED_MEMORY) {

src/library_syscall.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,7 @@ var SyscallsLibrary = {
838838
return ___syscall_statfs64(0, size, buf);
839839
},
840840
__syscall_fadvise64__nothrow: true,
841-
__syscall_fadvise64__proxy: false,
841+
__syscall_fadvise64__proxy: 'none',
842842
__syscall_fadvise64: (fd, offset, len, advice) => {
843843
return 0; // your advice is important to us (but we can't use it)
844844
},

src/utility.js

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -145,22 +145,55 @@ function mergeInto(obj, other, options = null) {
145145
}
146146

147147
for (const key of Object.keys(other)) {
148-
if (key.endsWith('__sig')) {
149-
if (obj.hasOwnProperty(key)) {
150-
const oldsig = obj[key];
151-
const newsig = other[key];
152-
if (oldsig == newsig) {
153-
warn(`signature redefinition for: ${key}`);
154-
} else {
155-
error(`signature redefinition for: ${key}. (old=${oldsig} vs new=${newsig})`);
148+
if (isDecorator(key)) {
149+
if (key.endsWith('__sig')) {
150+
if (obj.hasOwnProperty(key)) {
151+
const oldsig = obj[key];
152+
const newsig = other[key];
153+
if (oldsig == newsig) {
154+
warn(`signature redefinition for: ${key}`);
155+
} else {
156+
error(`signature redefinition for: ${key}. (old=${oldsig} vs new=${newsig})`);
157+
}
156158
}
157159
}
158-
}
159160

160-
if (key.endsWith('__deps')) {
161-
const deps = other[key];
162-
if (!Array.isArray(deps)) {
163-
error(`JS library directive ${key}=${deps.toString()} is of type ${typeof deps}, but it should be an array`);
161+
const index = key.lastIndexOf('__');
162+
const decoratorName = key.slice(index);
163+
const type = typeof other[key];
164+
165+
// Specific type checking for `__deps` which is expected to be an array
166+
// (not just any old `object`)
167+
if (decoratorName === '__deps') {
168+
const deps = other[key];
169+
if (!Array.isArray(deps)) {
170+
error(`JS library directive ${key}=${deps.toString()} is of type '${type}', but it should be an array`);
171+
}
172+
for (let dep of deps) {
173+
if (typeof dep !== 'string' && typeof dep !== 'function') {
174+
error(`__deps entries must be of type 'string' or 'function' not '${typeof dep}': ${key}`)
175+
}
176+
}
177+
} else {
178+
// General type checking for all other decorators
179+
const decoratorTypes = {
180+
'__sig': 'string',
181+
'__proxy': 'string',
182+
'__asm': 'boolean',
183+
'__inline': 'boolean',
184+
'__postset': ['string', 'function'],
185+
'__docs': 'string',
186+
'__nothrow': 'boolean',
187+
'__noleakcheck': 'boolean',
188+
'__internal': 'boolean',
189+
'__user': 'boolean',
190+
'__async': 'boolean',
191+
'__i53abi': 'boolean',
192+
};
193+
const expected = decoratorTypes[decoratorName];
194+
if (type !== expected && !expected.includes(type)) {
195+
error(`Decorator (${key}} has wrong type. Expected '${expected}' not '${type}'`);
196+
}
164197
}
165198
}
166199
}

test/test_other.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3963,15 +3963,29 @@ def test_js_lib_invalid_deps(self):
39633963
jslibfunc: (x) => {},
39643964
});
39653965
''')
3966-
create_file('src.c', r'''
3967-
#include <stdio.h>
3968-
int jslibfunc();
3969-
int main() {
3970-
printf("c calling: %d\n", jslibfunc());
3971-
}
3966+
3967+
err = self.expect_fail([EMCC, test_file('hello_world.c'), '--js-library', 'lib.js'])
3968+
self.assertContained('lib.js: JS library directive jslibfunc__deps=hello is of type \'string\', but it should be an array', err)
3969+
3970+
create_file('lib2.js', r'''
3971+
addToLibrary({
3972+
jslibfunc__deps: [1,2,3],
3973+
jslibfunc: (x) => {},
3974+
});
3975+
''')
3976+
3977+
err = self.expect_fail([EMCC, test_file('hello_world.c'), '--js-library', 'lib2.js'])
3978+
self.assertContained("lib2.js: __deps entries must be of type 'string' or 'function' not 'number': jslibfunc__deps", err)
3979+
3980+
def test_js_lib_invalid_decorator(self):
3981+
create_file('lib.js', r'''
3982+
addToLibrary({
3983+
jslibfunc__async: 'hello',
3984+
jslibfunc: (x) => {},
3985+
});
39723986
''')
3973-
err = self.expect_fail([EMCC, 'src.c', '--js-library', 'lib.js'])
3974-
self.assertContained('lib.js: JS library directive jslibfunc__deps=hello is of type string, but it should be an array', err)
3987+
err = self.expect_fail([EMCC, test_file('hello_world.c'), '--js-library', 'lib.js'])
3988+
self.assertContained("lib.js: Decorator (jslibfunc__async} has wrong type. Expected 'boolean' not 'string'", err)
39753989

39763990
def test_js_lib_legacy(self):
39773991
create_file('lib.js', r'''

0 commit comments

Comments
 (0)