Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shading #108

Closed
Closed

Conversation

EliteMasterEric
Copy link

@EliteMasterEric EliteMasterEric commented Apr 8, 2023

Provide the ability to rename and transform package and class names of types during compilation.

Rendered proposal

@Aurel300
Copy link
Member

Aurel300 commented Apr 8, 2023

Some comments:

  • Your proposal only talks about Java. Other targets (at least C#) have similar problems, I imagine?
  • Manipulating bytecode directly, just to rename some types, sounds less insane than decompiling and recompiling. Do you know how Gradle or Maven actually implement this feature? I wouldn't be surprised if it was bytecode manipulation.
  • "Shading" is indeed not a great name. I want to say it should actually be "shadowing", but both will clash with Haxe's existing ability to shadow types.
  • Does it make sense to "shade" a class but not the whole package? Is there an actual use case that couldn't be solved by having two different packages (one with a single class) to begin with, then only "shading" one of them?
  • For Java and C# again: how do we deal with different Jars using slightly different Haxe versions? How about two different Jars that DCEd different parts of the std? Maybe this is more of a question for @Simn than for this proposal.

@EliteMasterEric
Copy link
Author

Your proposal only talks about Java. Other targets (at least C#) have similar problems, I imagine?

I am not aware of how this issue interacts with C#. Java is the primary language I can think of where groups of packages are loaded as modules at once, and don't allow or resolve conflicts.

Manipulating bytecode directly, just to rename some types, sounds less insane than decompiling and recompiling. Do you know how Gradle or Maven actually implement this feature? I wouldn't be surprised if it was bytecode manipulation.

Gradle and Maven are provided Java source, and the source is modified during the built process.

"Shading" is indeed not a great name.

Agreed.

Does it make sense to "shade" a class but not the whole package? Is there an actual use case that couldn't be solved by having two different packages (one with a single class) to begin with, then only "shading" one of them?

I can't think of one, honestly. Maybe if you want to give a class a name that is a reserved word in the target language?

For Java and C# again: how do we deal with different Jars using slightly different Haxe versions? How about two different Jars that DCEd different parts of the std? Maybe this is more of a question for @Simn than for this proposal.

I'm not sure about the current solution for dealing with multiple JARs with different Haxe versions (if there even is one), but with shading (I agree, need a better, unambiguous name) you can simply shade one JAR's Haxe implementation into one package and the other JAR's implementation into another, with the result being there are now two sets of Haxe packages loaded, essentially including the specific version of the Haxe libs as a dependency.

@back2dos
Copy link
Member

back2dos commented Apr 8, 2023

Hmm. Sounds like this could be accomplished with @:native, no?

@EliteMasterEric
Copy link
Author

Hmm. Sounds like this could be accomplished with @:native, no?

I don't know how @:native behaves with non-extern Haxe classes, but this definitely doesn't work if you need to shade the haxe package

@back2dos
Copy link
Member

back2dos commented Apr 8, 2023

Here's a rough sketch of how one would apply it across a package:

import haxe.macro.Context.*;
import haxe.macro.Type;

using StringTools;

function apply(from:String, to:String) {
  function dotPad(s:String)
    return if (s.endsWith('.')) s else '$s.';

  from = dotPad(from);
  to = dotPad(to);

  onGenerate(types -> {
    function applyTo<T:BaseType>(r:Ref<T>)
      switch r.toString() {
        case _.startsWith(from) => false:
        case match:
          r.get().meta.add(':native', [macro $v{to + match.substr(from.length)}], (macro null).pos);
      }

    for (t in types)
      switch t {
        case TInst(r, _): applyTo(r);
        case TEnum(r, _): applyTo(r);
        default:
      }
  });
}

Then execute it like this:

-main haxe.Main
-java .
-D jvm
-dce full
--macro Shade.apply('haxe', 'my.shaded.hx')

Which will work like so:

package haxe;

function main() {
  trace(Foo); // class my.shaded.hx.Foo
  trace(Bar); // class my.shaded.hx._Main.Bar
  trace(haxe.ds.StringMap); // class my.shaded.hx.ds.StringMap
}

class Foo {}
private class Bar {}

@EliteMasterEric
Copy link
Author

Apparently my argument against Haxe's omissions is invalid because MACROS!

I think that having this as a standalone HaxeLib would be great, if you want to clean it up and publish it let me know, and if not I can do it.

How does this interact with abstracts btw? Does it properly move the private implementation class over?

@back2dos
Copy link
Member

back2dos commented Apr 9, 2023

Apparently my argument against Haxe's omissions is invalid because MACROS!

Depends very much on the omissions. But for customizing output, I think you'll find that when it's doable via macros, it's the most customizable solution.

How does this interact with abstracts btw? Does it properly move the private implementation class over?

Yes. Those implementation classes make it into the TInst branch above, where the ClassType::kind is a KAbstractImpl.

I think that having this as a standalone HaxeLib would be great, if you want to clean it up and publish it let me know, and if not I can do it.

Please feel free to go ahead. I have no need for such a library and would thus make for a lousy maintainer.

@EliteMasterEric
Copy link
Author

An update on this proposal:

As far as I'm aware, this issue of duplicate classpaths only occurs on the Java platform (and other targets do not experience these issues), thus the necessity for a shading functionality which is applied to all languages is greatly diminished.

However, when experimenting with the code provided by back2dos, I determined that using @:native on a non-extern class changes the destination at which the class is compiled to, which is the exact result that I had desired from the @:shade annotation in the first place.

I have thus been developing a library which provides macros to calculate and apply the @:native annotations to entire packages of modules. You can find that at https://github.com/EliteMasterEric/haxe-shade

Of note, however, is the fact that using a macro to apply @:native to root Haxe types (such as Array) causes the compiler to crash. Since I do need to be able to do this, I consider the resolution of HaxeFoundation/haxe#11361 to be necessary to fully implement my library and resolve the issues it targets.

Once that is done, the library linked above should be fully capable of performing everything I needed from this proposal. Therefore it is redundant, and I will mark this as closed. It can be reopened in the future if there are other issues or a valid use case arises that is not already covered by existing functionality.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants