Skip to content

Commit

Permalink
JS build: fix TypeError when null occurs in YAML input
Browse files Browse the repository at this point in the history
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 (<anonymous>)
    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.
  • Loading branch information
generalmimon committed Sep 25, 2024
1 parent c23ec2c commit 8d913de
Showing 1 changed file with 11 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://www.scala-js.org/api/scalajs-library/1.13.1/scala/scalajs/js/index.html>:
//
// > 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
}
}
}

0 comments on commit 8d913de

Please sign in to comment.