Skip to content

ANTLR4 runtime cannot be transpiled to ES5 because of incorrect inheritance #3032

@octogonz

Description

@octogonz

Background

Web applications must be transpiled to ECMAScript 5 (ES5) if they need to support older JavaScript runtimes, for example Internet Explorer or hardware devices that shipped with older JavaScript engines. In the past, the ANTLR runtime was written using ES5 compatible code. But version 4.9.x recently introduced ES6 class definitions that require transpilation. This is fine.

However, the ANTLR library has some classes that inherit from the system Error class, which is incompatible with transpilation. The reason is that ES5 system APIs are modeled as ES6 classes, which use a different inheritance mechanism from transpiled classes. (See
this article for some details.)

Repro

  1. Transpile ANTLR4 to ES5 and bundle it into a web application.

This code fails to catch LexerNoViableAltException:

                    catch (e) {
                        if (e instanceof RecognitionException) {
                            this.notifyListeners(e); // report error
                            this.recover(e);
                        }
                        else {
                            console.log(e.stack);
                            throw e;
                        }
                    }

The e instanceof RecognitionException test returns false, even though LexerNoViableAltException inherits from RecognitionException. This happens because of the incorrect prototype introduced when RecognitionException inherits from the Error base class.

Possible fixes

The two standard solutions are:

  1. Don't inherit from the Error class; instead simply implement its contract using a custom base class. This will break instanceof Error, but generally nobody ever tests that, so this approach works fine in practice. The callstack property can be calculated using (new Error()).stack.
    - OR -
  2. Call Object.setPrototypeOf() in the constructor for every subclass of the Error class. This also works pretty well. Its main downside is that people sometimes forget to apply the workaround when adding new subclasses.

I would be willing to make a PR to fix this. Would you accept a fix?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions