From 8d913def37a571bf8dd817f4f0d0f3bab3751f3a Mon Sep 17 00:00:00 2001 From: Petr Pucil Date: Wed, 25 Sep 2024 17:55:16 +0200 Subject: [PATCH] JS build: fix TypeError when `null` occurs in YAML input Fixes KSC "crashes" like this (this stack trace is from https://ide.kaitai.io/devel/): ``` TypeError: Cannot convert undefined or null to object at Function.keys () at new $c_sjs_js_WrappedDictionary$DictionaryIterator (https://ide.kaitai.io/devel/lib/_npm/kaitai-struct-compiler/kaitai-struct-compiler.js:56693:69) at $c_sjs_js_WrappedDictionary.iterator__sc_Iterator (https://ide.kaitai.io/devel/lib/_npm/kaitai-struct-compiler/kaitai-struct-compiler.js:108532:10) at $f_scm_Growable__addAll__sc_IterableOnce__scm_Growable (https://ide.kaitai.io/devel/lib/_npm/kaitai-struct-compiler/kaitai-struct-compiler.js:43131:21) at $c_sci_MapBuilderImpl.addAll__sc_IterableOnce__sci_MapBuilderImpl (https://ide.kaitai.io/devel/lib/_npm/kaitai-struct-compiler/kaitai-struct-compiler.js:70719:192) at $c_sci_Map$.from__sc_IterableOnce__sci_Map (https://ide.kaitai.io/devel/lib/_npm/kaitai-struct-compiler/kaitai-struct-compiler.js:49082:26) at $c_Lio_kaitai_struct_format_JavaScriptKSYParser$.yamlJavascriptToScala__O__O (https://ide.kaitai.io/devel/lib/_npm/kaitai-struct-compiler/kaitai-struct-compiler.js:17228:35) ... ``` I think that this is an error that Web IDE users see relatively often when they're in the middle of writing a .ksy spec directly in the Web IDE's built-in editor. Web IDE recompiles the .ksy spec automatically while the user types, so it's likely that there will be an attempt to compile a non-well-formed .ksy spec like this: ```ksy meta: id: js_cannot_convert_null instances: foo: # NOTE: the following line "value:" is equivalent to "value: null" in YAML value: ``` Notice that the `value:` line is unfinished, but the Web IDE doesn't know that, so it compiles this spec anyway. As a result, the KSC crashes with the stack trace posted above, because the `yamlJavascriptToScala` wasn't prepared for `null` or `undefined` and tried to treat these values as if they were JS objects (i.e. "dictionaries"), which doesn't work. This is fixed in this commit, so the new error message in the Web IDE for the above .ksy snippet looks like this: ``` (main): /instances/foo/value: error: expected string, got null ``` This is consistent with the error message printed by the JVM build: ```console $ kaitai-struct-compiler -t python js_cannot_convert_null.ksy js_cannot_convert_null.ksy: /instances/foo/value: error: expected string, got null ``` --- A footnote to the code change: I removed `_: Int` because it was redundant - JavaScript doesn't have this type. `int` only exists in Java, JavaScript uses `_: Double` for all numbers. --- .../kaitai/struct/format/JavaScriptKSYParser.scala | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/js/src/main/scala/io/kaitai/struct/format/JavaScriptKSYParser.scala b/js/src/main/scala/io/kaitai/struct/format/JavaScriptKSYParser.scala index f2ca22629..e73265a01 100644 --- a/js/src/main/scala/io/kaitai/struct/format/JavaScriptKSYParser.scala +++ b/js/src/main/scala/io/kaitai/struct/format/JavaScriptKSYParser.scala @@ -29,12 +29,20 @@ object JavaScriptKSYParser { def yamlJavascriptToScala(src: Any): Any = { src match { - case array: js.Array[AnyRef] => + case array: js.Array[_] => array.toList.map(yamlJavascriptToScala) - case _: String | _: Int | _: Double | _: Boolean => + // See : + // + // > There are no explicit definitions for JavaScript primitive types, as + // > one could expect, because the corresponding Scala types stand in + // > their stead: + // > * (...) + // > * `Unit` is the type of the JavaScript undefined value + // > * `Null` is the type of the JavaScript null value + case _: Boolean | _: Double | _: String | _: Unit | null => src case dict => - dict.asInstanceOf[js.Dictionary[AnyRef]].toMap.view.mapValues(yamlJavascriptToScala).toMap + dict.asInstanceOf[js.Dictionary[_]].toMap.view.mapValues(yamlJavascriptToScala).toMap } } }