Skip to content

JavaScript: constructor of a class with a BaseType never calls super(), so every new Derived() throws ReferenceError #103

Description

@matt-edmondson

What's wrong

JavaScriptGenerator.GenerateMethod (Coder/Languages/JavaScriptGenerator.cs:417-472) writes a constructor in a fixed order. It writes constructor(...) {, then one this.<member> = <value>; per MemberInitialiser (:459-469), then the body. GenerateClassDeclaration writes class X extends Base whenever BaseType is set (:352-355). Nothing in the file ever writes super(...); grep super Coder/Languages/JavaScriptGenerator.cs finds nothing.

In JavaScript, a constructor in a derived class must call super(). It must do so before it touches this, and before it returns even when it does nothing. Without the call, constructing the class always throws. C++ and C# call the base's default constructor implicitly, so the same AST is correct in those targets and broken only in JavaScript.

Reproduction

var shape = new ClassDeclaration("Shape");
shape.Members.Add(new FieldDeclaration("name", "string") { InitialValue = new LiteralExpression<string>("shape") });
var circle = new ClassDeclaration("Circle") { BaseType = "Shape" };
circle.Members.Add(new FieldDeclaration("radius", "double"));
var ctor = new FunctionDeclaration("Circle") { Kind = FunctionKind.Constructor };
ctor.Parameters.Add(new Parameter("radius", "double"));
ctor.Initialisers.Add(new MemberInitialiser("radius") { Value = new VariableReference("radius") });
circle.Members.Add(ctor);
Console.WriteLine(new JavaScriptGenerator().Generate(new SourceFile("shapes") { Members = { shape, circle } }));

Generated:

class Shape {
    name = "shape";
}

class Circle extends Shape {
    radius;
    constructor(radius) {
        this.radius = radius;
    }
}

I appended const c = new Circle(2); console.log(c.radius, c.name); and ran it with node 22:

ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
    at new Circle (derived.mjs:8:9)

Expected: 2 shape. The C++ and C# output of the same AST constructs correctly, because both call Shape() implicitly.

Why it matters

No class with a base type and a constructor can be instantiated in the generated JavaScript. Removing the initialisers does not help, because a derived constructor that returns without calling super() throws too. There is no JavaScript toolchain test, so the failure is invisible in CI.

Suggested fix / acceptance criteria

  • When the enclosing class has a BaseType, the generator writes super(); as the first statement of the constructor, before any initialiser assignment. The AST has no way to pass arguments to the base constructor, so the argument-less call matches what C++ and C# do implicitly.
  • If a MemberInitialiser named after the base type is meant to carry base-constructor arguments (the C++ : Base(args) idiom), it should become super(args) rather than this.Base = ....
  • Add a test that runs the output under node when node is on the path, like the other toolchain tests. At minimum, add a text test asserting that super(); precedes this.radius = radius;.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions