mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-06 22:17:29 +00:00
fix(developer): emit compile error when unsupported statements encountered in stores
When a store contains any unsupported statement (any, baselayout, beep, call, context, if, index, layer, notany, nul, platform, reset, save, set, use) or undocumented statement (clearcontext, fix, switch), then emit a compile error. Previously, compiler would not detect use of these statements in stores, which could lead to undefined behaviour at runtime. At time of writing, eight keyboards in the keyboards repository (clavbur9, hausa_ajami_qwerty, sil_cameroon_qwerty, sil_cameroon_azerty, sil_dzongkha, sil_yoruba8, sil_yoruba_bar, sil_yoruba_dot) make use of either beep or nul in stores. Due to compiler unfolding of any/index statements for key part of the rule, these would work in some cases, but this was never supported explicitly nor completely, and so at this point it is better to prevent misuse and add full support as a future task. Fixes: #16718 Test-bot: skip
This commit is contained in:
parent
6ce0b0e1ac
commit
ef212c7e66
27 changed files with 277 additions and 3 deletions
|
|
@ -289,6 +289,8 @@ namespace KmnCompilerMessages {
|
|||
WARN_DeprecatedValueFormat = SevWarn | 0x0BC,
|
||||
WARN_DeprecatedCompileTarget = SevWarn | 0x0BD,
|
||||
|
||||
ERROR_StoreContainsUnsupportedStatement = SevError | 0x0BE,
|
||||
|
||||
FATAL_BufferOverflow = SevFatal | 0x0C0
|
||||
// FATAL_Break = SevFatal | 0x0C1, unused
|
||||
};
|
||||
|
|
|
|||
|
|
@ -930,6 +930,12 @@ export class KmnCompilerMessages {
|
|||
`Compile target '${def(o.p?.[0])}' was deprecated in Keyman 19 and will cause the line to always be ignored by kmc in Keyman 20.`,
|
||||
);
|
||||
|
||||
static ERROR_StoreContainsUnsupportedStatement = SevError | 0x0BE;
|
||||
static Error_StoreContainsUnsupportedStatement = () => mc(
|
||||
this.ERROR_StoreContainsUnsupportedStatement,
|
||||
`A store can contain only characters, deadkeys, virtual keys, and 'outs()' statements.`,
|
||||
);
|
||||
|
||||
static FATAL_BufferOverflow = SevFatal | 0x0C0;
|
||||
static Fatal_BufferOverflow = () => mc(this.FATAL_BufferOverflow, `The compiler memory buffer overflowed`);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-any'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
store(letter) 'F'
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-any) any(letter)
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-baselayout'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
store(opt) 'opt'
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement; note baselayout() compiles to CODE_IFSYSTEMSTORE
|
||||
store(bad-baselayout) baselayout('us')
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-beep'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-beep) beep
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-call'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
store(callee) 'dll.dll:func'
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-call) call(callee)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-clearcontext'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement; note clearcontext undocumented, deprecated in v19
|
||||
store(bad-clearcontext) clearcontext
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-context'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-context) context
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-contextex'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-contextex) context(1)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-fix'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement; note fix undocumented, deprecated in v19
|
||||
store(bad-fix) fix
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-ifopt'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
store(opt) 'opt'
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-if) if(opt='F')
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-ifsystemstore'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-ifs) if(&layer='ah') 'eek'
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-index'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
store(letter) 'F'
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-index) index(letter, 1)
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-layer'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
store(opt) 'opt'
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement; note layer() compiles to CODE_IFSYSTEMSTORE
|
||||
store(bad-layer) layer('shift')
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-notany'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
store(letter) 'F'
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-notany) notany(letter)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-nul'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-nul) nul
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-platform'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
store(opt) 'opt'
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement; note platform() compiles to CODE_IFSYSTEMSTORE
|
||||
store(bad-platform) platform('windows')
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-resetopt'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
store(opt) 'opt'
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-reset) reset(opt)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-return'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-reset) return
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-saveopt'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
store(opt) 'opt'
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-save) save(opt)
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-setopt'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
store(opt) 'opt'
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-set) set(opt='X')
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-setsystemstore'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-setsystemstore) set(&layer='X')
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-switch'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement; note switch undocumented and unused, should be eliminated
|
||||
store(bad-setsystemstore) switch(1)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'error_store_contains_unsupported_statement-use'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_StoreContainsUnsupportedStatement
|
||||
store(bad-use) use(main)
|
||||
|
|
@ -412,8 +412,19 @@ describe('KmnCompilerMessages', function () {
|
|||
// WARN_LanguageHeadersDeprecatedInKeyman10
|
||||
|
||||
['ethnologuecode', 'language', 'windowslanguages'].forEach(storeName =>
|
||||
it(`should generate WARN_LanguageHeadersDeprecatedInKeyman10 if the contains deprecated system store "&${storeName}`, async function() {
|
||||
it(`should generate WARN_LanguageHeadersDeprecatedInKeyman10 if the file contains deprecated system store "&${storeName}"`, async function() {
|
||||
await testForMessage(this, ['keyboards', `warn_language_headers_deprecated_in_keyman10-${storeName}.kmn`], KmnCompilerMessages.WARN_LanguageHeadersDeprecatedInKeyman10);
|
||||
})
|
||||
);
|
||||
|
||||
// ERROR_StoreContainsUnsupportedStatement
|
||||
|
||||
['any', 'index', 'clearcontext', 'fix', 'context', 'nul', 'use', 'return', 'beep', 'switch',
|
||||
'call', 'contextex', 'notany', 'setopt',
|
||||
'ifopt', 'baselayout', 'layer', 'platform',
|
||||
'saveopt', 'resetopt', 'ifsystemstore', 'setsystemstore'].forEach(statement =>
|
||||
it(`should generate ERROR_StoreContainsUnsupportedStatement if a store contains statement "${statement}"`, async function() {
|
||||
await testForMessage(this, ['invalid-keyboards', `error_store_contains_unsupported_statement-${statement}.kmn`], KmnCompilerMessages.ERROR_StoreContainsUnsupportedStatement);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ KMX_DWORD GetRHS(PFILE_KEYBOARD fk, PKMX_WCHAR p, PKMX_WCHAR buf, int bufsize, i
|
|||
PKMX_WCHAR GetDelimitedString(PKMX_WCHAR *p, KMX_WCHAR const * Delimiters, KMX_WORD Flags);
|
||||
KMX_DWORD GetXString(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_WCHAR const * token, PKMX_WCHAR output, int max, int offset, PKMX_WCHAR *newp, int isVKey, int isUnicode);
|
||||
KMX_BOOL GetCompileTargetsFromTargetsStore(KMX_WCHAR *store, int &targets);
|
||||
KMX_BOOL DoesStoreContainInvalidStatement(PKMX_WCHAR store);
|
||||
|
||||
int GetGroupNum(PFILE_KEYBOARD fk, PKMX_WCHAR p);
|
||||
|
||||
|
|
@ -843,6 +844,11 @@ KMX_BOOL ProcessStoreLine(PFILE_KEYBOARD fk, PKMX_WCHAR p) {
|
|||
kmcmp::CodeConstants->reindex(); // has to be done after every character add due to possible use in another store. // I4982
|
||||
}
|
||||
|
||||
if(DoesStoreContainInvalidStatement(sp->dpString)) {
|
||||
ReportCompilerMessage(KmnCompilerMessages::ERROR_StoreContainsUnsupportedStatement);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
fk->cxStoreArray++; // increment now, because GetXString refers to stores
|
||||
|
||||
if (i > 0) {
|
||||
|
|
@ -1526,6 +1532,28 @@ KMX_DWORD CheckStatementOffsets(PFILE_KEYBOARD fk, PFILE_GROUP gp, PKMX_WCHAR co
|
|||
return STATUS_Success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores can contain only characters, deadkeys, and virtual keys (and `outs`).
|
||||
* Note that `outs()` is expanded during read so there is no `CODE_OUTS`.
|
||||
*
|
||||
* @param store
|
||||
* @return KMX_BOOL
|
||||
*/
|
||||
KMX_BOOL DoesStoreContainInvalidStatement(PKMX_WCHAR store) {
|
||||
for (PKMX_WCHAR p = store; *p; p = incxstr(p)) {
|
||||
if (*p == UC_SENTINEL) {
|
||||
auto code = *(p + 1);
|
||||
if(code == CODE_DEADKEY || code == CODE_EXTENDED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// No other codes are permitted; note, outs() does not have a code as it is expanded at compile time
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the order of statements in the context matches the specification.
|
||||
* Rule structure: [context] ['+' key] '>' output
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
set -eu
|
||||
find "$1" -name '*.kmn' | \
|
||||
grep -E '(release|experimental)/([a-z0-9_]+)/([a-z0-9_]+)/source/\3\.kmn$' | \
|
||||
grep -vE 'masaram_gondi|anii|sil_kmhmu|fv_statimcets|fv_nuucaanul|basic_kbdcherp|basic_kbdolch|sil_yi|gff_geez'
|
||||
grep -vE 'masaram_gondi|anii|sil_kmhmu|fv_statimcets|fv_nuucaanul|basic_kbdcherp|basic_kbdolch|sil_yi|gff_geez|clavbur9|hausa_ajami_qwerty|sil_cameroon_qwerty|sil_cameroon_azerty|sil_dzongkha|sil_yoruba8|sil_yoruba_bar|sil_yoruba_dot'
|
||||
# #12623: exclude masaram_gondi due to #11806
|
||||
# #12631: exclude anii, sil_kmhmu as ico references have mismatching case
|
||||
# #12631: exclude fv_statimcets, fv_nuucaanul as these include U+2002 which is not
|
||||
|
|
@ -18,4 +18,7 @@ find "$1" -name '*.kmn' | \
|
|||
# whitespace, see also issue #12307
|
||||
# #14746: exclude sil_yi, as this has whitespace in group name 'Unicode Group'
|
||||
# #16717: exclude gff_geez, as this has vkeys referenced in store in an any()
|
||||
# statement
|
||||
# statement
|
||||
# #16718: exclude clavbur9, hausa_ajami_qwerty, sil_cameroon_qwerty,
|
||||
# sil_cameroon_azerty, sil_dzongkha, sil_yoruba8, sil_yoruba_bar,
|
||||
# sil_yoruba_dot due to use of `nul` or `beep` in stores
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue