cincheo / jsweet

A Java to JavaScript transpiler.
http://www.jsweet.org
Other
1.46k stars 161 forks source link

Imports for enums are not generated in JSweet 3.0.0-RC7 #639

Closed deathbeam closed 3 years ago

deathbeam commented 3 years ago

So I was using JSweet 3.0.0-RC3 but it dissapeared so I had to upgrade. But when I tried to upgrade to latest version, the imports for enums in generated JavaScript code were missing.

Here is repository I am trying to generate code from: https://github.com/deathbeam/runelite-web-base/tree/not-working

Example of not working generated code:

// @ts-nocheck
/* eslint-disable */
export class FarmingTracker {
  predictPatch(
    patch,
    configGroup,
    autoweedConfigGroup,
    username,
    getConfiguration
  ) {
    const unixNow = (n => (n < 0 ? Math.ceil(n) : Math.floor(n)))(
      new Date().getTime() / 1000
    )
    let autoweed
    {
      const group = configGroup + '.' + username
      autoweed =
        /* toString */ '' +
          /* Enum.ordinal */ Autoweed[Autoweed[Autoweed.ON]] ===
        (target =>
          typeof target === 'function'
            ? target(group, autoweedConfigGroup)
            : target.apply(group, autoweedConfigGroup))(getConfiguration)
    }
    const group =
      configGroup + '.' + username + '.' + patch.getRegion().getRegionID()
    const key = '' + Varbits['_$wrappers'][patch.getVarbit()].getId()
    const storedValue = (target =>
      typeof target === 'function'
        ? target(group, key)
        : target.apply(group, key))(getConfiguration)
    if (storedValue == null) {
      return null
    }
    let unixTime = 0
    let value = 0
    {
      const parts = storedValue.split(':')
      if (parts.length === 2) {
        try {
          value = parseInt(parts[0])
          unixTime = parseInt(parts[1])
        } catch (e) {}
      }
    }
    if (unixTime <= 0) {
      return null
    }
    const state = PatchImplementation['_$wrappers'][
      patch.getImplementation()
    ].forVarbitValue(value)
    if (state == null) {
      return null
    }
    let stage = state.getStage()
    let stages = state.getStages()
    let tickrate = state.getTickRate() * 60
    if (autoweed && state.getProduce() === PatchImplementation.Produce.WEEDS) {
      stage = 0
      stages = 1
      tickrate = 0
    }
    let doneEstimate = 0
    if (tickrate > 0) {
      const tickNow = (n => (n < 0 ? Math.ceil(n) : Math.floor(n)))(
        (unixNow + 5 * 60) / tickrate
      )
      const tickTime = (n => (n < 0 ? Math.ceil(n) : Math.floor(n)))(
        (unixTime + 5 * 60) / tickrate
      )
      const delta = (tickNow - tickTime) | 0
      doneEstimate = (stages - 1 - stage + tickTime) * tickrate + 5 * 60
      stage += delta
      if (stage >= stages) {
        stage = stages - 1
      }
    }
    return new PatchPrediction(
      state.getProduce(),
      state.getCropState(),
      doneEstimate,
      stage,
      stages
    )
  }
}
FarmingTracker['__class'] = 'timetracking.FarmingTracker'
import { PatchImplementation } from './PatchImplementation'
import { Autoweed } from './Autoweed'
import { PatchPrediction } from './PatchPrediction'

As you can see, there i no import for Varbits (and alos for some reason imports are at bottom. Here is same class but with RC3:

// @ts-nocheck
/* eslint-disable */
import { PatchPrediction } from './PatchPrediction'
import { Autoweed } from './Autoweed'
import { Varbits } from './Varbits'
import { PatchImplementation } from './PatchImplementation'
export class FarmingTracker {
  predictPatch(
    patch,
    configGroup,
    autoweedConfigGroup,
    username,
    getConfiguration
  ) {
    let unixNow = (n => (n < 0 ? Math.ceil(n) : Math.floor(n)))(
      new Date().getTime() / 1000
    )
    let autoweed
    {
      let group = configGroup + '.' + username
      autoweed = /* equals */ ((o1, o2) => {
        if (o1 && o1.equals) {
          return o1.equals(o2)
        } else {
          return o1 === o2
        }
      })(
        /* toString */ '' + /* Enum.ordinal */ Autoweed[Autoweed[Autoweed.ON]],
        (target =>
          typeof target === 'function'
            ? target(group, autoweedConfigGroup)
            : target.apply(group, autoweedConfigGroup))(getConfiguration)
      )
    }
    let group =
      configGroup + '.' + username + '.' + patch.getRegion().getRegionID()
    let key = '' + Varbits['_$wrappers'][patch.getVarbit()].getId()
    let storedValue = (target =>
      typeof target === 'function'
        ? target(group, key)
        : target.apply(group, key))(getConfiguration)
    if (storedValue == null) {
      return null
    }
    let unixTime = 0
    let value = 0
    {
      let parts = storedValue.split(':')
      if (parts.length === 2) {
        try {
          value = parseInt(parts[0])
          unixTime = parseInt(parts[1])
        } catch (e) {}
      }
    }
    if (unixTime <= 0) {
      return null
    }
    let state = PatchImplementation['_$wrappers'][
      patch.getImplementation()
    ].forVarbitValue(value)
    if (state == null) {
      return null
    }
    let stage = state.getStage()
    let stages = state.getStages()
    let tickrate = state.getTickRate() * 60
    if (autoweed && state.getProduce() === PatchImplementation.Produce.WEEDS) {
      stage = 0
      stages = 1
      tickrate = 0
    }
    let doneEstimate = 0
    if (tickrate > 0) {
      let tickNow = (n => (n < 0 ? Math.ceil(n) : Math.floor(n)))(
        (unixNow + 5 * 60) / tickrate
      )
      let tickTime = (n => (n < 0 ? Math.ceil(n) : Math.floor(n)))(
        (unixTime + 5 * 60) / tickrate
      )
      let delta = (tickNow - tickTime) | 0
      doneEstimate = (stages - 1 - stage + tickTime) * tickrate + 5 * 60
      stage += delta
      if (stage >= stages) {
        stage = stages - 1
      }
    }
    return new PatchPrediction(
      state.getProduce(),
      state.getCropState(),
      doneEstimate,
      stage,
      stages
    )
  }
}
FarmingTracker['__class'] = 'timetracking.FarmingTracker'

And here is the source java code:

package timetracking;

import java.lang.String;
import java.util.function.BiFunction;
import def.js.Date;
import def.js.Globals;
import timetracking.PatchImplementation.Produce;

public class FarmingTracker
{
    public PatchPrediction predictPatch(FarmingPatch patch, String configGroup, String autoweedConfigGroup, String username, BiFunction<String, String, String> getConfiguration)
    {
        long unixNow = (long) (new Date().getTime() / 1000);

        boolean autoweed;
        {
            String group = configGroup + "." + username;
            autoweed = Integer.toString(Autoweed.ON.ordinal())
                .equals(getConfiguration.apply(group, autoweedConfigGroup));
        }

        String group = configGroup + "." + username + "." + patch.getRegion().getRegionID();
        String key = Integer.toString(patch.getVarbit().getId());
        String storedValue = getConfiguration.apply(group, key);

        if (storedValue == null)
        {
            return null;
        }

        long unixTime = 0;
        int value = 0;
        {
            String[] parts = storedValue.split(":");
            if (parts.length == 2)
            {
                try
                {
                    value = Globals.parseInt(parts[0]);
                    unixTime = Globals.parseInt(parts[1]);
                }
                catch (Exception e)
                {
                }
            }
        }

        if (unixTime <= 0)
        {
            return null;
        }

        PatchState state = patch.getImplementation().forVarbitValue(value);

        if (state == null)
        {
            return null;
        }

        int stage = state.getStage();
        int stages = state.getStages();
        int tickrate = state.getTickRate() * 60;

        if (autoweed && state.getProduce() == Produce.WEEDS)
        {
            stage = 0;
            stages = 1;
            tickrate = 0;
        }

        long doneEstimate = 0;
        if (tickrate > 0)
        {
            long tickNow = (unixNow + (5 * 60)) / tickrate;
            long tickTime = (unixTime + (5 * 60)) / tickrate;
            int delta = (int) (tickNow - tickTime);

            doneEstimate = ((stages - 1 - stage) + tickTime) * tickrate + (5 * 60);

            stage += delta;
            if (stage >= stages)
            {
                stage = stages - 1;
            }
        }

        return new PatchPrediction(
            state.getProduce(),
            state.getCropState(),
            doneEstimate,
            stage,
            stages
        );
    }
}
lgrignon commented 3 years ago

Oh, you are trying to transpile RuneLite, that's cool :)

Do you have any error during transpilation? There are multiple unit test cases on enums with and without modules so I would be suprised if this is actually broken. Maybe you hit a side case. Anyway we will try to fix it together. It could be great if you could narrow down to a minimal repro, and I will take a look asap. Btw 3.0.0-RC8 is out :)

Thanks for reporting

deathbeam commented 3 years ago

Well im not transpiling RuneLite, just subset of it for RL website for time tracking. I don't see any errors when building the project, but here is the output anyway:

http://ix.io/2BDk

I tried with 3.0.0-RC8 and the issue persists.

I set up minimal repro case here: https://github.com/deathbeam/runelite-web-base/tree/min-repro/src/main/java/timetracking

So it looks like if the enum isnt referenced directly, the import isnt inferred (see VarbitCallerNotWorking and VarbitCallerWorking classes).

Here is VarbitCallerNotWorking.js:

// @ts-nocheck
/* eslint-disable */
export class VarbitCallerNotWorking {
    static getVarbitId(varbitWrapper) {
        return Varbits["_$wrappers"][varbitWrapper.getVarbit()].getId();
    }
}
VarbitCallerNotWorking["__class"] = "timetracking.VarbitCallerNotWorking";

and here VarbitCallerWorking.js:

// @ts-nocheck
/* eslint-disable */
export class VarbitCallerWorking {
    static getVarbitId(varbitWrapper) {
        const varbit = varbitWrapper.getVarbit();
        return Varbits["_$wrappers"][varbit].getId();
    }
}
VarbitCallerWorking["__class"] = "timetracking.VarbitCallerWorking";
import { Varbits } from './Varbits';

EDIT: And even in the working example, the imports are still at bottom

lgrignon commented 3 years ago

We will fix it. Thanks

lgrignon commented 3 years ago

@deathbeam Could you please try on 3.0.0-RC11 This bug should be fixed on it

deathbeam commented 3 years ago

@lgrignon Yea, now it is working, thanks (and the enum import is at top while rest is at bottom but i guess that is unrelated to this issue).