-
Notifications
You must be signed in to change notification settings - Fork 858
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
Fixed error in coercion to boolean for legacy versions (#1402) #1403
Fixed error in coercion to boolean for legacy versions (#1402) #1403
Conversation
Can you please add a comment on the PR or in the code that explains what you're trying to fix here, or what bug is being addressed? That'll help us remember why we did this. Thanks! |
I think I put a link to the detailed issue that I have opened in the commit message, but I forgot to put it in the PR 😅 |
So in #1402 you provide 3 snippets and the last one is about stuff erroring out. Am unsure if you're saying that that code errors out in the latest version of Rhino, or that you expect it to error out when running with a languageVersion < 1.3. Regardless, I'm unsure why you made a change to ScriptableObject, because I haven't seen anything not working properly without that change in the current version of Rhino |
@@ -763,6 +763,9 @@ public Object[] getAllIds() { | |||
*/ | |||
@Override | |||
public Object getDefaultValue(Class<?> typeHint) { | |||
if (typeHint == ScriptRuntime.BooleanClass) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this change required?
So, the first two snippets are about "weird" behavior in language version < 1.3. Unfortunately, these are things that we still need to support in ServiceNow. The change to if (Context.getContext().isVersionECMA1()) {
// pure ECMA
return true;
}
// ECMA extension
val = ((Scriptable) val).getDefaultValue(BooleanClass);
if ((val instanceof Scriptable) && !isSymbol(val))
throw errorWithClassName("msg.primitive.expected", val); It is invoking var Base = function() {};
var Extending = function() {};
Extending.prototype = Base;
var x = new Extending();
!!x as The end result is that:
|
The rhino/src/org/mozilla/javascript/NativeBoolean.java Lines 34 to 39 in f085d50
The Was there ever a time when the I mean, I don't see any evidence going back to the initial commit of this repo 23 years ago that the array case has ever worked as you're describing. I don't actually know how to build the project from back then to test it, though. It appears to be using Ant, but I think there is some magic that I do not know how to perform. |
@andreabergia in which exact Rhino version did you see the different behavior compared to the latest version? @tonygermano all binaries from Rhino 1.4R3 onwards are available from our docs: https://rhino.github.io/releases/ |
I was unable to get the shell to launch with 1.4R3. In version 1.5R1 This code var Base = function() {};
var Extending = function() {};
Extending.prototype = Base;
var x = new Extending();
!!x ...behaves exactly as the current version and throws a TypeError for versions 100, 110, and 120. I think it would be incorrect to change this behavior now. |
The most current snapshot behaves exactly the same as version 1.5R1. I do not think this PR is necessary.
The reason that version 120 differs from 100 and 110 is because
In addition to |
Good point, I need to fix that.
Yes, this is what happens. We rely on that at ServiceNow.
I disagree, without the change in @Test
public void codeCanBeRunWithoutRaisingErrorInModernMode() {
Utils.runWithAllOptimizationLevels(
cx -> {
cx.setLanguageVersion(Context.VERSION_ES6);
Scriptable scope = cx.initStandardObjects(null);
Object result =
cx.evaluateString(
scope,
"var Base = function() {};\n"
+ "var Extending = function() {};\n"
+ "Extending.prototype = Base;\n"
+ "var x = new Extending();\n"
+ "!!x\n",
"test",
1,
null);
assertEquals(true, result);
return null;
});
} |
I think your assumptions are wrong, and @p-bakker asked you to provide the exact version where rhino functioned the way you think it should. When I pulled and tested on a version over 20 years old, it behaves in exactly the same way as a snapshot build from the master branch for all of your test cases. I understand that you desire Likewise |
I am doubtful there ever was a version of rhino where this worked. But, this is causing problems for us at ServiceNow, so we went and fixed it in our internal fork. |
So, which Rhino version does ServiceNow use? The 'our internal fork' link just links to documentation, not a forked repo. We'd like to know, because you seem to suggest behavior changed in the latest version compared to some earlier version of Rhino, but @tonygermano checked and wasnt able to find any Rhino version that behaved any different than the current master Curious though: the docs you pointed to suggests your users can use ES2021 features that Rhino doesn't support. Are you cross-compiling or has ServiceNow implemented those features in their own Rhino fork? |
Ok, I realize I have been confusing. I meant "engine version, as in 1.0 and so on", not "rhino version" as in release 1.7.11 or so.
I am unsure if my NDA allows me to tell you what we are doing... 😢 I'll try to find out what I can share. |
K, as I said, just curious. If ServiceNow has implemented all those features in Rhino, I'd really like to know more about it, but am assuming cross-compiling is used. |
Looks like |
That's an interesting document. I scoured the Netscape documentation (which was terrible) in archive.org and couldn't find anything to support the following. It was also missing from the MDN document in "what's new in 1.2." Yet we do follow this behavior, anyway, with regard to converting arrays to numbers in 1.2 (and only in 1.2.)
However, at least the MDN documentation did agree with both of these in "what's new in 1.2," which we do support correctly, also for only version 1.2.
If we label the boolean conversion of a zero length array to true in version 1.2 as a bug. Is it a behavior we want to change now, considering it has functioned this way since Rhino was released as open source? My understanding is that the intent is to minimize changes to the legacy versions to preserve backward compatibility. If this was truly a regression bug that was introduced in a more recent version, then surely we would address it, but I would think that the "bug" is the accepted behavior after 20+ years. |
Here's more fun stuff to contemplate... I downloaded Netscape Navigator 7.2 (windows version) and installed it in Wine. For language version 1.2, |
Ok, I got the green light - we are using a transpiler in front of Rhino. 🙂
I don't think it's a bug - we need to preserve it, even if it is weird. But, from what you are saying, my change in @Override
public Object getDefaultValue(Class<?> hint) {
if (hint == ScriptRuntime.NumberClass) {
Context cx = Context.getContext();
if (cx.getLanguageVersion() == Context.VERSION_1_2) return Long.valueOf(length);
}
if (hint == ScriptRuntime.BooleanClass) {
Context cx = Context.getContext();
if (cx.getLanguageVersion() == Context.VERSION_1_2) return length != 0;
}
return super.getDefaultValue(hint);
} I've updated the PR. |
Rhino has not changed behavior in this regard since initial release. Now, if we compare Rhino to Netscape, Rhino differs from Netscape in 3 different language versions (1.0, 1.1, and 1.2.) However, in neither engine did all 3 of those versions evaluate to the same true or false value. Maybe I can illustrate this with some code. I ran this code in Netscape Navigator 7.2 (2004) <html>
<head></head>
<body>
<script language="JavaScript1.0">
document.write('1.0: ' + !![])
</script>
<br>
<script language="JavaScript1.1">
document.write('1.1: ' + !![])
</script>
<br>
<script language="JavaScript1.2">
document.write('1.2: ' + !![])
</script>
<br>
<script language="JavaScript1.3">
document.write('1.3: ' + !![])
</script>
</body>
</html> and this was the output
This was in Rhino 1.5R1 (2000) js> var v = [100,110,120,130]
js> for (i=0; i < v.length; i++) { version(v[i]); print(v[i] + ': ' + !![]) }
100: false
110: false
120: true
130: true Rhino 1.7 release 1 (2008) js> var v = [100,110,120,130]
js> for (i=0; i < v.length; i++) { version(v[i]); print(v[i] + ': ' + !![]) }
100: false
110: false
120: true
130: true Rhino 1.7.15-SNAPSHOT (2023) js> var v = [100,110,120,130]
js> for (i=0; i < v.length; i++) { version(v[i]); print(v[i] + ': ' + !![]) }
100: false
110: false
120: true
130: true Your PR changes this table to the following, and I don't think there is justification for this change being "preservation" as this state has never existed anywhere in the past as far as I can tell.
I think either we call it a "bug" and make all language versions match Netscape or we say Rhino has been constant in this behavior since initial release and therefore the current behavior is now an immutable "feature." I think most people on this project lean towards the latter. |
I absolutely didn't mean to change that behavior, it is an oversight. I've pushed a fix, now it behaves in the "old" way. This PR was only supposed to fix the initial problem with this javascript, not to touch arrays in any way. 🙂 var Base = function() {};
var Extending = function() {};
Extending.prototype = Base;
var x = new Extending();
!!x |
That code is a similar situation. I don't see a need to "fix" anything. The only different between Netscape and current Rhino is that version 1.2 throws an error in Rhino, but doesn't in Netscape. Rhino has been constant in that differing behavior since initial release. Netscape Navigator 7.2 <html>
<head></head>
<body>
<script language="JavaScript1.0">
function test() {var B = function() {}; var E = function() {}; E.prototype = B; var x = new E(), r;
try {r = !!x} catch(e) {r = e.toString()} return r;}
document.write('1.0: ' + test())
</script>
<br>
<script language="JavaScript1.1">
function test() {var B = function() {}; var E = function() {}; E.prototype = B; var x = new E(), r;
try {r = !!x} catch(e) {r = e.toString()} return r;}
document.write('1.1: ' + test())
</script>
<br>
<script language="JavaScript1.2">
function test() {var B = function() {}; var E = function() {}; E.prototype = B; var x = new E(), r;
try {r = !!x} catch(e) {r = e.toString()} return r;}
document.write('1.2: ' + test())
</script>
<br>
<script language="JavaScript1.3">
function test() {var B = function() {}; var E = function() {}; E.prototype = B; var x = new E(), r;
try {r = !!x} catch(e) {r = e.toString()} return r;}
document.write('1.3: ' + test())
</script>
</body>
</html>
Rhino 15R1 js> var v = [100,110,120,130]
js> function test() {var B = function() {}; var E = function() {}; E.prototype = B; var x = new E(), r; try {r = !!x} catch(e) {r = e.toString()} return r;}
js> for (i=0; i < v.length; i++) { version(v[i]); print(v[i] + ': ' + test()) }
100: TypeError: Method "toString" called on incompatible object.
110: TypeError: Method "toString" called on incompatible object.
120: TypeError: Method "toString" called on incompatible object.
130: true Rhino 1.7 release 1 2008 03 06 js> var v = [100,110,120,130]
js> function test() {var B = function() {}; var E = function() {}; E.prototype = B; var x = new E(), r; try {r = !!x} catch(e) {r = e.toString()} return r;}
js> for (i=0; i < v.length; i++) { version(v[i]); print(v[i] + ': ' + test()) }
100: TypeError: Method "toString" called on incompatible object.
110: TypeError: Method "toString" called on incompatible object.
120: TypeError: Method "toString" called on incompatible object.
130: true Rhino 1.7.15-SNAPSHOT js> var v = [100,110,120,130]
js> function test() {var B = function() {}; var E = function() {}; E.prototype = B; var x = new E(), r; try {r = !!x} catch(e) {r = e.toString()} return r;}
js> for (i=0; i < v.length; i++) { version(v[i]); print(v[i] + ': ' + test()) }
100: TypeError: Method "toString" called on incompatible object (org.mozilla.javascript.NativeObject is not an instance of org.mozilla.javascript.BaseFunction).
110: TypeError: Method "toString" called on incompatible object (org.mozilla.javascript.NativeObject is not an instance of org.mozilla.javascript.BaseFunction).
120: TypeError: Method "toString" called on incompatible object (org.mozilla.javascript.NativeObject is not an instance of org.mozilla.javascript.BaseFunction).
130: true |
Well, seems this won't get merged. Closing it. |
Fixes #1402