Implementing an interface causes name mangling on JS target


#1

The following Kotlin code:

interface IArgless {
    fun veryFun()
}

class CArgless: IArgless {
    override fun veryFun() {

    }
}


interface IArgful {
    fun notSoFun(elligence: Int)
}

class CArgful: IArgful {
    override fun notSoFun(elligence: Int) {

    }
}


class CThrough {
    fun notFunEnough(elligence: Int) {

    }
}

compiles to this output:

(function (Kotlin) {
  'use strict';
  var _ = Kotlin.defineRootPackage(null, /** @lends _ */ {
    IArgless: Kotlin.createTrait(null),
    CArgless: Kotlin.createClass(function () {
      return [_.IArgless];
    }, null, /** @lends _.CArgless.prototype */ {
      veryFun: function () {
      }
    }),
    IArgful: Kotlin.createTrait(null),
    CArgful: Kotlin.createClass(function () {
      return [_.IArgful];
    }, null, /** @lends _.CArgful.prototype */ {
      notSoFun_za3lpa$: function (elligence) {
      }
    }),
    CThrough: Kotlin.createClass(null, null, /** @lends _.CThrough.prototype */ {
      notFunEnough: function (elligence) {
      }
    })
  });
  Kotlin.defineModule('kotlin-js-name-mangling', _);
}(Kotlin));

Notice that all of the concrete implementations of the member functions are given the same names as in the Kotlin file with the exception of the one that both overrides something from a supertype and has a parameter. Changing the type of the parameter also changes what the suffix is.

This interferes with making Kotlin’s output interface with plain Javascript. Is this the expected behavior or is this a bug? Am I doing something wrong (besides using Javascript in the first place)? Is there a workaround?


#2

Kotlin uses name mangling to support function overloading. Right now, the only way to "export" kotlin function to JS is to use `native` annotation on interface or class.

Example:

native interface A {
  fun f(x: Int)
}

open class B : A {
  override fun f(x: Int) {
  println(x)
  }
}

class C : B() {
  override fun f(x: Int) {
  println(x * 2)
  }
}

fun main(args: Array<String>) {
  B().f(10)
  C().f(10)
}


Generated JS:

(function (Kotlin) {
  ‘use strict’;
  var = Kotlin.defineRootPackage(null, /** @lends _ / {
  B: Kotlin.createClass(null, null, /* @lends .B.prototype */ {
  f: function (x) {
  Kotlin.println(x);
  }
  }),
  C: Kotlin.createClass(function () {
  return [.B];
  }, function $fun() {
  $fun.baseInitializer.call(this);
  }, /** @lends .C.prototype / {
  f: function (x) {
  Kotlin.println(x 2);
  }
  }),
  main: function (args) {
  (new .B()).f(10);
  (new .C()).f(10);
  }
  });
  Kotlin.defineModule(‘moduleId’, );
  .main([]);
}(Kotlin));


Note, that a class or an interface annotated with native won’t be generated,
because it’s primary purpose is enabling calling functions from JS libraries.


#3

I might be able to make that work with interfaces (although I then run into the complication of such interfaces not supporting implemented methods, which I would have to work around by using extension functions, which are their own hassle when interfacing with Javascript code because Javascript doesn't support extensions and Kotlin doesn't modify class prototypes automatically) but I don't think that technique can be made to work with open classes. Example:

open class BaseArgless {
    open fun soMuchFun() {}
}

class SubArgless: BaseArgless() {
    override fun soMuchFun() {}
}

open class BaseArgful {
    open fun noFun(erpret: Int) {}
}

class SubArgful: BaseArgful() {
    override fun noFun(erpret: Int) {}
}

The output of the above is:

(function (Kotlin) {
  'use strict';
  var _ = Kotlin.defineRootPackage(null, /** @lends _ */ {
    BaseArgless: Kotlin.createClass(null, null, /** @lends _.BaseArgless.prototype */ {
      soMuchFun: function () {
      }
    }),
    SubArgless: Kotlin.createClass(function () {
      return [_.BaseArgless];
    }, function $fun() {
      $fun.baseInitializer.call(this);
    }, /** @lends _.SubArgless.prototype */ {
      soMuchFun: function () {
      }
    }),
    BaseArgful: Kotlin.createClass(null, null, /** @lends _.BaseArgful.prototype */ {
      noFun_za3lpa$: function (erpret) {
      }
    }),
    SubArgful: Kotlin.createClass(function () {
      return [_.BaseArgful];
    }, function $fun() {
      $fun.baseInitializer.call(this);
    }, /** @lends _.SubArgful.prototype */ {
      noFun_za3lpa$: function (erpret) {
      }
    })
  });
  Kotlin.defineModule('kotlin-js-name-mangling', _);
}(Kotlin));

Notice how the function `noFun(Int)` is renamed to support possible overloading even though I'm not actually overloading it. It's alright when an entire project is written in Kotlin but my use case is to write a library that can be used by web and Node projects that aren't written in pure Kotlin. If I declare the base classes to be native then the generated subclasses don't change - they keep their references to the identifiers of the missing superclasses - thus yielding code that will not execute. The best solution I've found so far is to write the following workaround:

(function() {
    'use strict'

    var baseCreateClass = Kotlin.createClass

  Kotlin.createClass = function(/varargs/) {
  var base = baseCreateClass.apply(this, arguments)

  var wrapper = function(/varargs/) {
           var result = base.apply(this, arguments)

           Object.keys(result.prototype).forEach(function(key) {
           var groups = /(w*)_(w*$)/.exec(key)
           if(groups)
                   result.prototype[groups[1]] = result.prototype[key]
           })

           return result
  }
  wrapper.type = base.type
  Object.defineProperty(wrapper, ‘className’, {set: function(a) { base.className = a }})
  return wrapper
  }
})();

With that workaround, the output of my code can be used the way you would expect from Javascript. It won't work when you overload an open function but no one expects an overloaded function written in Kotlin to interface cleanly with Javascript because Javascript only does parametric polymorphism. What I do expect is for a function that isn't overloaded to interface cleanly. Obviously Kotlin can only be developed so fast but given enough time, it doesn't make much sense to me that I seem to have to defeat one of your "features" in order to write a reasonable Javascript library in a language that targets Javascript.


#4

As Alexey say, right now native interface is best way to avoid mangling(for members) and I don't recommend to patch Kotlin.createClass.

Mainly we use mangling for public API and for overridable members of open classifiers. I.e. for internal and private declaration we used simple naming with adding number when it’s necessary (it’s another possible solution now).

In your example soMuchFun not mangled only because it doesn’t have parameters.


#5

The name mangling is quite annoying! And even more annoying is the lack of proper documentation!

I was really happy when I tried Kotlin for the first time and I migrated some of our core libs in mere hours. The compile to JS feature would make it perfect for our next project - we could implement our business logic just once.

My Problem is that I don’t know how to call my lib functions because of these suffixes. I am looking for an answer for several days now but did not find a solution.

There seems to be some obscure reason for this so called feature, but I don’t get it. The workaround with the native Annoation does also not work, and Google finds several version of this workaround anyway.

It would be really helpful, if you would provide an Explanation …

  • why the name mangling is needed and what the alternatives are
  • how to tackle this problem with Kotlin 1.0.3
  • how to write a JS/Java dual use Lib with Kotlin