Bug 92817 - use strict, string property assignment throws TypeError
Summary: use strict, string property assignment throws TypeError
Status: RESOLVED INVALID
Alias: None
Product: WebKit
Classification: Unclassified
Component: JavaScriptCore (show other bugs)
Version: 528+ (Nightly build)
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Nobody
URL: http://jsbin.com/araqog/4/edit
Keywords:
Depends on:
Blocks:
 
Reported: 2012-07-31 18:05 PDT by James Burke
Modified: 2012-08-01 14:44 PDT (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description James Burke 2012-07-31 18:05:56 PDT
In strict mode, assigning to a property on a string throws a TypeError in Safari 6 and in Nightly WebKit, Version 6.0 (8536.25, 537+).

This does not occur in other strict-aware browsers, tested with Firefox 15 and Chrome 20. They allow the assignment but then later reads of the property return undefined.

It may be that I misunderstood the strict mode rules, but in that case I am surprised the browsers do not agree in this case.

In case the URL to the JSBin goes away, the script code is the following:

    function stricty() {
      //comment out use strict and no error thrown.
      //No error thrown in other strict-aware browsers.
      'use strict';
      var value = 'foo',
          err, result;
      
      try {
        //THIS IS WHERE IT FAILS IN Safari 6/Nightly WebKit
        value.something = true;
      } catch (e) {
        err = e;
      }
      
      if (err) {
        result = 'ERROR: ' + err;
      } else {
        result = 'value.something is: ' + value.something;
      }
          
      document.getElementById('out').innerHTML = result;
    }

    stricty();
Comment 1 Oliver Hunt 2012-08-01 13:00:13 PDT
My reading of ECMA-262 says that comments are not relevant in determination of strict mode:
14.1 Directive Prologues and the Use Strict Directive
A Directive Prologue is the longest sequence of ExpressionStatement productions occurring as the initial SourceElement productions of a Program or FunctionBody and where each ExpressionStatement in the sequence consists entirely of a StringLiteral token followed a semicolon. The semicolon may appear explicitly or may be inserted by automatic semicolon insertion. A Directive Prologue may be an empty sequence.
A Use Strict Directive is an ExpressionStatement in a Directive Prologue whose StringLiteral is either the exact character sequences "use strict" or 'use strict'. A Use Strict Directive may not contain an EscapeSequence or LineContinuation.

Comments of any form are not statement expressions (they are explicitly treated as a single space character)
Comment 2 Oliver Hunt 2012-08-01 13:03:17 PDT
Oh wait, i was misinterpreting what you were saying.
Comment 3 Oliver Hunt 2012-08-01 13:53:07 PDT
This is correct behaviour.

The evaluation of  "value.something = true" is evaluated with by getting the reference for the left hand side, then evaluating the right hand side, then calling put value.

step 1) get the reference for assignment:

11.2.1 Property Accessors
1. essentially baseReference = &value
2. baseValue = value
3. propertyNameReference = &"something" // all lookup is perform as if a.b were a["b"]
4. propertyNameValue = "something"
5. /* do nothing */
6. propertyNameString = ToString(propertyNameValue) /*"something"*/
7. strict = true /*we're in strict mode code*/
8. return Reference({base: baseValue /*string*/, 
                                name: propertyNameString /*"something"*/, 
                                strict: strict /*true*/})

we'll call the result of this lhsRef.

step 2)
Evalute the right hand side (which i won't go through as it's not relevant)

step 3) perform the assignment

PutValue(lhsRef, lhsSide /*true*/)

8.7.2 PutValue (V /*{base: string, name: "something", strict: true}*/, W/*rhs side, so the value: true*/)
1. V is a reference so do nothing
2. base=GetBase(V) /*string*/
3. /* do nothing as we've got a reference */
4. V is a property reference so we now do:
     a. base is a primitive type (string) so put = a special internal version of [[Put]] also defined in 8.7.2 
     b. put(base, V.name, W, V.strict)

    Internal [[Put]](base /* our string*/, P /*"something"*/, W/*true*/, Throw/*true -- because we're in strict mode*/)
    1. O=ToObject(base) // yay allocate a String object
    2. /* do nothing as there's no readonly "something" property in prototype chain */
    3. ownDesc = GetOwnProperty(O, P) // "something" doesn't existing on the String object so this is null
    4. /* do nothing as ownDesc is null*/
    5. desc = GetProperty(O, P) // also null as "something" isn't a property that exists anywhere on the protochain
    6. /* do nothing as desc is null so isn't an accessor property */
    7. We are trying to assign a property to a temporary object
        a. Throw is true, so we throw a TypeError.


So this is the correct behaviour, and the long (even though it's abbreviated) and winding path to throwing an exception here in strict mode.

You should file bugs on the other browsers for not doing this correctly. :D
Comment 4 Oliver Hunt 2012-08-01 13:55:20 PDT
For reference you could run the tests at http://test262.ecmascript.org/# -- it's an ongoing endeavour to have complete coverage of the entire spec.  On Wednesday last week Safari 6 passed every test.
Comment 5 Oliver Hunt 2012-08-01 14:08:14 PDT
The linked test case is actually making the kind of mistake this exception was specifically designed to prevent -- attaching properties to primitive types does not work, as you are simply attaching the properties to a temporary.   If there isn't a setter on the prototype with the right name all you're doing is creating an object, assigning a property, and then throwing the object away.
Comment 6 Gavin Barraclough 2012-08-01 14:40:25 PDT
Safari 6.0's behavior is correct here, see ECMA-262, section 8.7.2.

The expression 'value.something' returns a property reference, and per section 11.13.1 step 5 this calls PutValue (8.7.2).  The base of the reference is a primitive string, so per step 4a the put method we'll use if the special [[Put]] implementation specified below, and per step 4b the 'Throw' argument to [[Put]] is true (since we're in strict code, this is a strict reference).  As the String object & its prototypes do not contain a 'something' property, we'll reach step 7, and throw a type error per step 7a.

cheers,
G.
Comment 7 Gavin Barraclough 2012-08-01 14:41:11 PDT
Oh, wait, Oliver already said all this. Ooops! :-)
Comment 8 James Burke 2012-08-01 14:44:52 PDT
Thanks for the info, looks like the other browsers just have not implemented the matching behavior yet.