Bug 228064 - JS binary addition operator does not conform to spec
Summary: JS binary addition operator does not conform to spec
Status: RESOLVED INVALID
Alias: None
Product: WebKit
Classification: Unclassified
Component: JavaScriptCore (show other bugs)
Version: Other
Hardware: All All
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2021-07-18 14:12 PDT by Jules Bertholet
Modified: 2021-07-22 19:43 PDT (History)
9 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jules Bertholet 2021-07-18 14:12:15 PDT
WebKit Version       : WebKitGTK 2.32.2
Other browsers tested:
    Firefox: 89.0.2: OK
    V8 (Chromium/NodeJS/Deno): Same issue

What steps will reproduce the problem?
(1) Evaluate the JavaScript expression "{} + {}"

What is the expected result?
The numeric value "NaN" is returned, as per the spec (https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-applystringornumericbinaryoperator).

What happens instead?
The string '[object Object][object Object]' is returned.
Comment 1 Alexey Proskuryakov 2021-07-18 14:58:12 PDT
I cannot reproduce this on macOS (Apple Silicon) with macOS 11.5 or 12 betas.
Comment 2 Jules Bertholet 2021-07-19 01:56:29 PDT
I've also tested WebKitGTK 2.33.2 and gotten the same result (GNOME Web / Epiphany, Fedora 34, Intel x86_64)
Comment 3 Sam Sneddon [:gsnedders] 2021-07-19 07:56:49 PDT
Where are you evaluating it?

"[object Object][object Object]" is what I'd expect if it's being evaluated as a statement and not as an expression (as then you have an empty BlockStatement followed by a UnaryExpression, and not an AdditiveExpression).
Comment 4 Jules Bertholet 2021-07-19 15:14:18 PDT
(In reply to Sam Sneddon [:gsnedders] from comment #3)
> Where are you evaluating it?
> 
> "[object Object][object Object]" is what I'd expect if it's being evaluated
> as a statement and not as an expression (as then you have an empty
> BlockStatement followed by a UnaryExpression, and not an AdditiveExpression).

In the browser console; running the statement alone gives the same result as assigning to a variable. Substituting "{}" by another object also exhibits the same issue
Comment 5 Radar WebKit Bug Importer 2021-07-22 16:46:16 PDT
<rdar://problem/80990349>
Comment 6 Yusuke Suzuki 2021-07-22 19:06:04 PDT
I tried it in several versions of Safari, but I cannot reproduce it, so closing this issue.

My guess is that the web page has `Object.prototype.toString` override, and opening a console in that web page causes it since the expression should be evaluated under the page's context. Please check the web page's script.
Comment 7 Yusuke Suzuki 2021-07-22 19:40:39 PDT
Ah, OK. I think I know what it is.

https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-applystringornumericbinaryoperator

The result "[object Object][object Object]" is the right value according to the spec.
This is because ToPrimitive onto object should return "[object Object]".

Let's look https://tc39.es/ecma262/multipage/abstract-operations.html#sec-toprimitive carefully. Since @@toPrimitive is not defined, we call OrdinaryToPrimitive with "default" preference.

https://tc39.es/ecma262/multipage/abstract-operations.html#sec-ordinarytoprimitive
Then, we first call object.valueOf(). But it returns an object itself.
Since it is not a primitive value, we discard this, and then call object.toString() next.
See step 5-b-ii "If Type(result) is not Object, return result.". Since valueOf returns an object, we call object.toString next (as listed in step 4-a).

https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-applystringornumericbinaryoperator
So, in step 2-c, both lprim and rprim are strings "[object Object]". Therefore, 2-c-iii will return "[object Object][object Object]".

So, JSC and V8 behaviors are correct.

Then, why SpiderMonkey returns NaN.

In JSC and V8 consoles, we evaluate the input as "expression" first instead of "statement". This is convenient because users do not need to wrap the expression with "()".
On the other hand, maybe, SpiderMonkey is evaluating it as "statement". Then, what is happening is that,

{} + {}

is evaluated as ({} + {}) in JSC and V8's console.

{} + {}

is evaluated as

{
   // Block statement
}
+{};  // + and object => NaN.
in SpiderMonkey.

So,

1. {} + {} is correctly evaluated in JSC. If it is evaluated as an expression, the result should be "[object Object][object Object]"
2. {} + {} is evaluated differently in the console in Firefox, Safari, and V8. And this is engine's preference.
Comment 8 Yusuke Suzuki 2021-07-22 19:43:09 PDT
We can check this preference easily.
Let's evaluate "{}" in each console.

Safari / Chrome => empty object
Firefox => undefined (Since Firefox evaluated it as empty block statement).