Skip to content

Conversation

@gbrail
Copy link
Collaborator

@gbrail gbrail commented Aug 13, 2024

This PR changes some of the logic that does arithmetic using Number objects to try and do integer arithmetic when both operands are integers, rather than assuming that all arithmetic should be done using floating-point.

This also changes a few fundamental constants -- for example, Rhino has always defined "zero" as "0.0" but this PR makes it truly "0".

If Rhino embedders are assuming that all numbers coming back from Rhino are Double objects (rather than checking for Number and then getting either intValue() or doubleValue() as they need), then their code may break. I had to change a few test cases for this reason. If, on the other hand, embedders are working with Number, there should not be an issue.

Some math-heavy benchmarks, like some of the V8 and SunSpider benchmarks, run quite a bit faster with this change.

I would be interested to see if some people have code that breaks because of this, so I'm hoping that a few of you could try it out -- there are likely other optimizations along these lines that are possible if it works.

@p-bakker p-bakker added Potentially Breaking Change Issues that could break backwards compatibility Performance Issues related to the performance of the Rhino engine labels Aug 14, 2024
@p-bakker
Copy link
Collaborator

Afraid I'm not able to try this out in the environment where I use Rhino, but I'm OK if the Rhino integration would break if it wrongfully expects Doubles, instead of Numbers...

Do wonder about whether this PR is in any way related to #350 or (parts of) #717 & #507, in the sense of whether those cases are (partly) resolved/changes by this PR or whether those cases give hints for possible further optimizations in the int/double area

Specifically for #350 I think this PR will just result in Java receiving an int more often, correct? I think #350 can just be closed as Won't Fix, as Java integration code should not expect Doubles, but Numbers. Agree?

Also a question: in #350 (comment) you mentioned there being an integer optimization on optLevel 9. Is that (still?) really the case? I never found any code in Rhino that does anything different between optLevel 1 and 9

@rbri rbri closed this Aug 18, 2024
@rbri rbri reopened this Aug 18, 2024
@rbri
Copy link
Collaborator

rbri commented Aug 18, 2024

sorry....

@rbri
Copy link
Collaborator

rbri commented Aug 18, 2024

@gbrail spend some time to give this a try by applying to core-js.
Looks like nothing really (see below) fails and the overall speed seems to be a bit better (the whole huge test suite runs on a virtual machine so the speed depends on the hoster).

I had one issue to fix - my Reflect/Proxy impl (see #1431) uses instanceof Double at two places in NativeReflect. After fixing that all my tests are running fine. Maybe other products extending Rhino in some ways and also depending on the assumption that every numbers a double internally. But for me that is fine, i guess i know what happens and a have a large test suite to check the compatibility.

Summary: i'm fine with this

gbrail added 2 commits August 21, 2024 21:40
When doing math, try to do integer arithmetic when the arguments passed
are Integer objects, rather than always falling back to floating-point
math. This speeds up some benchmarks that rely heavily on integer
arithmetic.
@gbrail gbrail force-pushed the integer-operations branch from 8634af5 to 4e1ae12 Compare August 22, 2024 04:41
@gbrail gbrail merged commit c743a3f into mozilla:master Aug 23, 2024
@gbrail gbrail deleted the integer-operations branch August 23, 2024 01:23
@drubanovich-soti
Copy link
Contributor

drubanovich-soti commented Sep 2, 2025

@gbrail We just updated Rhino and this change broke one of our tests, which is not critical for us, but Rhino behavior seemed unexpected, so I would like to raise the concern for this specific case:
If a new JavaScript function added by Rhino has parameter or array type, then the actual argument will unexpectedly contain a mix of Integers and Doubles. E.g. if this is the implementation of the function:

        fun add(array: NativeArray): Double {
            var sum = 0.0
            for (number in array) {
                sum += number as Double
            }
            return sum
        }

Then the first invocation below will pass, but the second - will fail, as the NativeArray parameter contains unexpected mix of Integers and doubles:

    let sum1 = add([3, 2]); // Passes
    let sum2 = add([1, 2]); // Fails

@gbrail
Copy link
Collaborator Author

gbrail commented Sep 6, 2025

Can you open an issue for this? Hard to keep up with comments on closed PRs.

I'm not sure what part of your example there is JavaScript and what parts are Kotlin or something else, but in general, a JavaScript function that would typically be expected to return a "number" might return different types of numbers, and internal optimizations may change that specific type, for example if we can do the work using only integers we will. This is probably worth an update to the docs (which need a big update anyway!)

The best practice for calling JavaScript from non-JavaScript in Rhino is to not assume this, and to at least use the various functions of Number like intValue() and doubleValue() if you need a primitive. And in fact, if you are writing truly general-purpose code, you can't assume anything about what comes back from a function written in JS, and if you care you should use a method like ScriptRuntime.toNumber() which can handle most things.

Finally, maybe one of the folks on the project who works on the various reflection-based embeddings would have a different view, since I get the impression that's what you're trying to do here.

@drubanovich-soti
Copy link
Contributor

Thanks Greg, Yes you got it right and this answers all my questions. I'll open an issue as you asked.

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

Labels

Performance Issues related to the performance of the Rhino engine Potentially Breaking Change Issues that could break backwards compatibility

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants