-
-
Notifications
You must be signed in to change notification settings - Fork 662
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
dce should consider all members of a type consumed by an extern as used when the extern function is referenced #5889
Comments
And same example with DCE off to prove that it would work if DCE were did not prune members of types referenced by used externs: http://try.haxe.org/#46428 |
I don"t think we should do that by default because externs are usually not written for Haxe types. |
@Simn If externs are being written for non-Haxe types, they won’t be using Haxe types like |
I think that in that case you should exclude Map from your packageB and use packageA implementation instead, see what @elsassph is doing for JS code splitting. |
Yeah, I think the best way would be to generate a module containing the whole Haxe standard library and having something like a -nostdlib option to Haxe and just compile everything against an extern variant of stdlib. I just saw some references to @elsassph’s repository today and have not had time to investigate it. At the moment I am getting more pressed for time and may have to just resort to hacks for what I’m doing. It is also true that if one is writing code a module at a time, DCE is less and less useful. You would never write a small module with huge amounts of dead code in the first place because, with the whole “test coverage” thing, etc., dead code is likely buggy code. And the module’s public API would require you to mark most everything as “keep”. DCE is most useful when the Haxe compiler can see the whole program and all its dependencies together in a traditional setting, especially if you have multiple entry points, because removing unused members close to the entry point of the program may eliminate the need for huge portions of the stdlib itself. So, when trying to write modules, However, with all that said, if packageA declares an interface and provides a function which accepts an argument with a type of that interface and packageB has its own implementation of the interface but only creates instances to pass to packageA, DCE will eat packageB’s implementation if the interface is `IMap<K, V>, see below my confusion. I’m still convinced that externs are to describe the border APIs of black boxes and that the moment you pass an object to a black box’s interface which accepts a particular type, the only right thing to do is mark that whole type as “keep”. But, while playing around with the compiler, I found that the following code does not reproduce this problem as the class Thing implements IThing {
public function new() {
}
public function act() {
trace("hi");
}
}
class Test {
static function main() {
}
}
@:native("({consume: thing => thing.act(),})")
extern class Consumer {
static function consume(thing:IThing):Void;
}
@:native("null")
extern interface IThing {
function act():Void;
} So it looks like I’m actually hitting something different and I found code which is not eliminated even though I’d expect it to be ( I don’t understand enough of haxe’s implementation to guess why this happens. Somehow, if I implement Sorry for the rambling, but I’m confused on this. Why is |
FWIW my experimentation (haxe-modular, branch automatic-splitting) is post processing a single JS output to emit multiple JS files which can be lazy-loaded and share classes. It"s not one JS file by class but one main JS and several JS modules. |
I’m trying something different, I think. I want a haxe invocation on a project to generate a single JavaScript module along with a directory tree of I still don’t understand why haxe emits different code when I implement my own interface versus |
OK, I’m not sure what I was doing wrong earlier (actually, I suspect I was using interface IThing {
function act():Void;
}
@:native("({consume: thing => thing.act()})")
extern class NativeClass {
public static function consume(thing:IThing):Void;
}
class Test {
static function main() {
NativeClass.consume(new Thing());
}
}
class Thing implements IThing {
public function new() {
}
public function act() {
trace("hi");
}
} Produced by // Generated by Haxe 3.4.0
(function () { "use strict";
var IThing = function() { };
var Test = function() { };
Test.main = function() {
({consume: thing => thing.act()}).consume(new Thing());
};
var Thing = function() {
};
Thing.__interfaces__ = [IThing];
Test.main();
})(); And
Since Does this make sense? |
P.S., I did verify that my code runs fine with |
I have written two distinct modules. One exports a function which takes an
IMap<String, String>
and iterates over it:packageA/src/packageA/MapConsumer.hx
:packageA/externs/packageA/MapConsumer.hx
:My other module instantiates a
StringMap
and calls the function through the extern definition:packageB/src/packageB/MapConsumerConsumer.hx
:When compiling
packageB
with default settings and its classpath includingpackageA/externs
, I get the following code generated for the JavaScript target:Trying to run the program (after manually assembling the emitted JavaScript as modules together) results in
Failed: TypeError: values.keys is not a function
being thrown bypackageA.MapConsumer.consume()
(in a JavaScript target).As you can see, none of the
IMap<K, V>
are defined in theStringMap
implementation due to dead code elimination. It would be nice if DCE could mark types that an extern definition consumes as needing to be kept if the extern is being referenced in kept code. In this case, with an extern consumingIMap<K, V>
, the compiler has no knowledge of what methods the extern might call on the interface, so it should keep all of the implementations in the generated calling code.As a workaround, I am probably going to have to write a dummy file which consumes
IMap<K, V>
to and mark it with@:keep
to force the type to be more fully generated so that it can be passed toextern
consumers. But this is difficult to maintain.The text was updated successfully, but these errors were encountered: