Changeset 198042 in webkit


Ignore:
Timestamp:
Mar 11, 2016, 1:08:08 PM (9 years ago)
Author:
mark.lam@apple.com
Message:

Implement Function.name and Function#toString for ES6 class.
https://bugs.webkit.org/show_bug.cgi?id=155336

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

The only thing that the ES6 spec says about toString with regards to class
objects is:

"The string representation must have the syntax of a FunctionDeclaration,
FunctionExpression, GeneratorDeclaration, GeneratorExpression, ClassDeclaration,
ClassExpression, ArrowFunction, MethodDefinition, or GeneratorMethod depending
upon the actual characteristics of the object."

Previously, invoking toString() on a class object will return the function
source string of the class' constructor function. This does not conform to the
spec in that the toString string for a class does not have the syntax of a
ClassDeclaration or ClassExpression.

This is now fixed by doing the following:

  1. Added "m_classSource" to FunctionExecutable (and correspondingly to UnlinkedFunctionExecutable, FunctionMetadataNode, and ClassExprNode). m_classSource is the SourceCode for the code range "class ... { ... }".

Since the class constructor function is the in memory representation of the
class object, only class constructor functions will have its m_classSource
set. m_classSource will be "null" (by default) for all other functions.
This is how we know if a FunctionExecutable is for a class.

Note: FunctionExecutable does not have its own m_classSource. It always gets
it from its UnlinkedFunctionExecutable. This is ok to do because our CodeCache
currently does not cache UnlinkedFunctionExecutables for class constructors.

  1. The ClassExprNode now tracks the SourceCode range for the class expression. This is used to set m_classSource in the UnlinkedFunctionExecutable at bytecode generation time, and the FunctionExecutable later at bytecode linking time.
  1. Function.prototype.toString() now checks if the function is for a class. If so, it returns the string for the class source instead of just the function source for the class constructor.

Note: the class source is static from the time the class was parsed. This
can introduces some weirdness at runtime. Consider the following:

var v1 = class {}
v1.toString(); yields "class {}".

class c2 extends v1 {}

c2.proto === v1; yields true i.e. c2 extends v1.
c2.toString();
yields "class c2 extends v1 {}" which is fine.

v1 = {}; point v1 to something else now.

c2.proto === v1; now yields false i.e. c2 no longer extends v1.

c2 actually extends the class that v1 used to
point to, but ...

c2.toString(); still yields "class c2 extends v1 {}" which is no longer true.

It is unclear how we can best implement toString() to avoid this issue.
The above behavior is how Chrome (Version 51.0.2671.0 canary (64-bit))
currently implements toString() of a class, and we do the same in this patch.
In Firefox (45.0), toString() of a class will yield the function source of it
constructor function, which is not better.

In this patch, we also added ES6 compliance for Function.name on class objects:

  1. The ClassExprNode now has a m_ecmaName string for tracking the inferred name of a class according to the ES6 spec. The ASTBuilder now mirrors its handling of FuncExprNodes to ClassExprNodes in setting the nodes' m_ecmaName where relevant.

The m_ecmaName is later used to set the m_ecmaName of the FunctionExecutable
of the class constructor, which in turn is used to populate the initial value
of the Function.name property.

  1. Also renamed some variable names (/m_metadata/metadata/) to be consistent with webkit naming convention.
  • bytecode/UnlinkedFunctionExecutable.cpp:

(JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):

  • bytecode/UnlinkedFunctionExecutable.h:
  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::emitNewArrowFunctionExpression):
(JSC::BytecodeGenerator::emitNewDefaultConstructor):

  • bytecompiler/BytecodeGenerator.h:
  • bytecompiler/NodesCodegen.cpp:

(JSC::ClassExprNode::emitBytecode):

  • parser/ASTBuilder.h:

(JSC::ASTBuilder::createAssignResolve):
(JSC::ASTBuilder::createYield):
(JSC::ASTBuilder::createClassExpr):
(JSC::ASTBuilder::createFunctionExpr):
(JSC::ASTBuilder::createProperty):
(JSC::ASTBuilder::makeAssignNode):

  • parser/NodeConstructors.h:

(JSC::FunctionParameters::FunctionParameters):
(JSC::BaseFuncExprNode::BaseFuncExprNode):
(JSC::FuncExprNode::FuncExprNode):
(JSC::FuncDeclNode::FuncDeclNode):
(JSC::ArrowFuncExprNode::ArrowFuncExprNode):
(JSC::ClassDeclNode::ClassDeclNode):
(JSC::ClassExprNode::ClassExprNode):

  • parser/Nodes.h:

(JSC::ExpressionNode::isDestructuringNode):
(JSC::ExpressionNode::isFuncExprNode):
(JSC::ExpressionNode::isArrowFuncExprNode):
(JSC::ExpressionNode::isClassExprNode):
(JSC::ExpressionNode::isCommaNode):
(JSC::ExpressionNode::isSimpleArray):
(JSC::ExpressionNode::isAdd):

  • parser/Parser.cpp:

(JSC::stringForFunctionMode):
(JSC::Parser<LexerType>::parseFunctionInfo):
(JSC::Parser<LexerType>::parseClass):

  • parser/ParserFunctionInfo.h:
  • parser/SyntaxChecker.h:

(JSC::SyntaxChecker::createEmptyLetExpression):
(JSC::SyntaxChecker::createYield):
(JSC::SyntaxChecker::createClassExpr):
(JSC::SyntaxChecker::createFunctionExpr):
(JSC::SyntaxChecker::createFunctionMetadata):
(JSC::SyntaxChecker::createArrowFunctionExpr):

  • runtime/Executable.cpp:

(JSC::FunctionExecutable::FunctionExecutable):
(JSC::FunctionExecutable::finishCreation):

  • runtime/Executable.h:
  • runtime/FunctionPrototype.cpp:

(JSC::functionProtoFuncToString):

  • tests/es6.yaml:

LayoutTests:

  • js/class-syntax-name-expected.txt:
  • js/script-tests/class-syntax-name.js:

(shouldBe):
(shouldBeTrue):

  • Rebased expected result.
  • js/function-toString-vs-name.html:
  • js/script-tests/function-toString-vs-name.js:
  • Added new tests for class.
  • platform/mac/inspector/model/remote-object-expected.txt:
  • Rebased expected result.
Location:
trunk
Files:
21 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r198039 r198042  
     12016-03-11  Mark Lam  <mark.lam@apple.com>
     2
     3        Implement Function.name and Function#toString for ES6 class.
     4        https://bugs.webkit.org/show_bug.cgi?id=155336
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        * js/class-syntax-name-expected.txt:
     9        * js/script-tests/class-syntax-name.js:
     10        (shouldBe):
     11        (shouldBeTrue):
     12        - Rebased expected result.
     13
     14        * js/function-toString-vs-name.html:
     15        * js/script-tests/function-toString-vs-name.js:
     16        - Added new tests for class.
     17
     18        * platform/mac/inspector/model/remote-object-expected.txt:
     19        - Rebased expected result.
     20
    1212016-03-11  Ryan Haddad  <ryanhaddad@apple.com>
    222
  • trunk/LayoutTests/js/class-syntax-name-expected.txt

    r187680 r198042  
    1313PASS class A { constructor() {} }
    1414PASS 'use strict'; class A { constructor() {} }
    15 PASS class A { constructor() {} }; A.toString():::'function A() {}'
    16 PASS 'use strict'; class A { constructor() {} }; A.toString():::'function A() {}'
     15PASS class A { constructor() {} }; A.toString():::'class A { constructor() {} }'
     16PASS 'use strict'; class A { constructor() {} }; A.toString():::'class A { constructor() {} }'
    1717PASS class A { constructor() {} }; (new A) instanceof A
    1818PASS 'use strict'; class A { constructor() {} }; (new A) instanceof A
    19 PASS class A { constructor() { this.base = A; } }; (new A).base.toString():::'function A() { this.base = A; }'
    20 PASS 'use strict'; class A { constructor() { this.base = A; } }; (new A).base.toString():::'function A() { this.base = A; }'
     19PASS class A { constructor() { this.base = A; } }; (new A).base.toString():::'class A { constructor() { this.base = A; } }'
     20PASS 'use strict'; class A { constructor() { this.base = A; } }; (new A).base.toString():::'class A { constructor() { this.base = A; } }'
    2121PASS class A { constructor() {} }; class B extends A {};
    2222PASS 'use strict'; class A { constructor() {} }; class B extends A {};
    23 PASS class A { constructor() {} }; class B extends A { constructor() {} }; B.toString():::'function B() {}'
    24 PASS 'use strict'; class A { constructor() {} }; class B extends A { constructor() {} }; B.toString():::'function B() {}'
     23PASS class A { constructor() {} }; class B extends A { constructor() {} }; B.toString():::'class B extends A { constructor() {} }'
     24PASS 'use strict'; class A { constructor() {} }; class B extends A { constructor() {} }; B.toString():::'class B extends A { constructor() {} }'
    2525PASS class A { constructor() {} }; class B extends A {}; (new B) instanceof A
    2626PASS 'use strict'; class A { constructor() {} }; class B extends A {}; (new B) instanceof A
    2727PASS class A { constructor() {} }; class B extends A {}; (new B) instanceof B
    2828PASS 'use strict'; class A { constructor() {} }; class B extends A {}; (new B) instanceof B
    29 PASS class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).base.toString():::'function A() {}'
    30 PASS 'use strict'; class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).base.toString():::'function A() {}'
    31 PASS class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).derived.toString():::'function B() { super(); this.base = A; this.derived = B; }'
    32 PASS 'use strict'; class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).derived.toString():::'function B() { super(); this.base = A; this.derived = B; }'
     29PASS class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).base.toString():::'class A { constructor() {} }'
     30PASS 'use strict'; class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).base.toString():::'class A { constructor() {} }'
     31PASS class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).derived.toString():::'class B extends A { constructor() { super(); this.base = A; this.derived = B; } }'
     32PASS 'use strict'; class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).derived.toString():::'class B extends A { constructor() { super(); this.base = A; this.derived = B; } }'
    3333
    3434Class expression
     
    5353PASS (new (class A { constructor() { this.base = A; } })).base
    5454PASS 'use strict'; (new (class A { constructor() { this.base = A; } })).base
    55 PASS (new (class A { constructor() { this.base = A; } })).base.toString():::"function A() { this.base = A; }"
    56 PASS 'use strict'; (new (class A { constructor() { this.base = A; } })).base.toString():::"function A() { this.base = A; }"
     55PASS (new (class A { constructor() { this.base = A; } })).base.toString():::"class A { constructor() { this.base = A; } }"
     56PASS 'use strict'; (new (class A { constructor() { this.base = A; } })).base.toString():::"class A { constructor() { this.base = A; } }"
    5757PASS class A {}; (class B extends A {})
    5858PASS 'use strict'; class A {}; (class B extends A {})
     
    6565PASS class A {}; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })) instanceof A
    6666PASS 'use strict'; class A {}; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })) instanceof A
    67 PASS class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).base.toString():::'function A() {}'
    68 PASS 'use strict'; class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).base.toString():::'function A() {}'
    69 PASS class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).derived.toString():::'function B() { super(); this.base = A; this.derived = B; }'
    70 PASS 'use strict'; class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).derived.toString():::'function B() { super(); this.base = A; this.derived = B; }'
     67PASS class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).base.toString():::'class A { constructor() {} }'
     68PASS 'use strict'; class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).base.toString():::'class A { constructor() {} }'
     69PASS class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).derived.toString():::'class B extends A { constructor() { super(); this.base = A; this.derived = B; } }'
     70PASS 'use strict'; class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).derived.toString():::'class B extends A { constructor() { super(); this.base = A; this.derived = B; } }'
    7171
    7272Class expression assignment to variable
     
    7575PASS var VarA = class {}
    7676PASS 'use strict'; var VarA = class {}
    77 PASS var VarA = class { constructor() {} }; VarA.toString():::'function () {}'
    78 PASS 'use strict'; var VarA = class { constructor() {} }; VarA.toString():::'function () {}'
     77PASS var VarA = class { constructor() {} }; VarA.toString():::'class { constructor() {} }'
     78PASS 'use strict'; var VarA = class { constructor() {} }; VarA.toString():::'class { constructor() {} }'
    7979PASS VarA:::ReferenceError: Can't find variable: VarA
    8080PASS 'use strict'; VarA:::ReferenceError: Can't find variable: VarA
    8181PASS var VarA = class A { constructor() {} }
    8282PASS 'use strict'; var VarA = class A { constructor() {} }
    83 PASS var VarA = class A { constructor() {} }; VarA.toString():::'function A() {}'
    84 PASS 'use strict'; var VarA = class A { constructor() {} }; VarA.toString():::'function A() {}'
     83PASS var VarA = class A { constructor() {} }; VarA.toString():::'class A { constructor() {} }'
     84PASS 'use strict'; var VarA = class A { constructor() {} }; VarA.toString():::'class A { constructor() {} }'
    8585PASS var VarA = class A { constructor() {} }; A.toString():::ReferenceError: Can't find variable: A
    8686PASS 'use strict'; var VarA = class A { constructor() {} }; A.toString():::ReferenceError: Can't find variable: A
    8787PASS var VarA = class A { constructor() {} }; (new VarA) instanceof VarA
    8888PASS 'use strict'; var VarA = class A { constructor() {} }; (new VarA) instanceof VarA
    89 PASS var VarA = class A { constructor() { this.base = A; } }; (new VarA).base.toString():::'function A() { this.base = A; }'
    90 PASS 'use strict'; var VarA = class A { constructor() { this.base = A; } }; (new VarA).base.toString():::'function A() { this.base = A; }'
     89PASS var VarA = class A { constructor() { this.base = A; } }; (new VarA).base.toString():::'class A { constructor() { this.base = A; } }'
     90PASS 'use strict'; var VarA = class A { constructor() { this.base = A; } }; (new VarA).base.toString():::'class A { constructor() { this.base = A; } }'
    9191PASS var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} };
    9292PASS 'use strict'; var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} };
    9393PASS var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; B:::ReferenceError: Can't find variable: B
    9494PASS 'use strict'; var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; B:::ReferenceError: Can't find variable: B
    95 PASS var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; VarB.toString():::'function B() {}'
    96 PASS 'use strict'; var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; VarB.toString():::'function B() {}'
     95PASS var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; VarB.toString():::'class B extends VarA { constructor() {} }'
     96PASS 'use strict'; var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; VarB.toString():::'class B extends VarA { constructor() {} }'
    9797PASS var VarA = class A { constructor() {} }; var VarB = class B extends VarA { }; (new VarB) instanceof VarA
    9898PASS 'use strict'; var VarA = class A { constructor() {} }; var VarB = class B extends VarA { }; (new VarB) instanceof VarA
     
    119119PASS eval('var Foo = 10'); Foo:::10
    120120PASS 'use strict'; eval('var Foo = 10'); Foo:::ReferenceError: Can't find variable: Foo
    121 PASS eval('class Bar { constructor() {} }; Bar.toString()');:::'function Bar() {}'
     121PASS eval('class Bar { constructor() {} }; Bar.toString()');:::'class Bar { constructor() {} }'
    122122PASS 'use strict'; eval('class Bar { constructor() {} }'); Bar.toString():::ReferenceError: Can't find variable: Bar
    123123PASS successfullyParsed
  • trunk/LayoutTests/js/function-toString-vs-name.html

    r197815 r198042  
    66<body>
    77<script src="script-tests/function-toString-vs-name.js"></script>
     8
     9<script>
     10failures = "";
     11
     12section = "global class statements with identical class bodies from different scripts";
     13</script>
     14<!-- Case 1: A global class statement: -->
     15    <script>class globalClass1 { constructor(x) { return x; } stuff() { return 5; } }</script>
     16
     17<!-- Case 2: Same class body as Case 1 but indented. -->
     18        <script>class globalClass2 { constructor(x) { return x; } stuff() { return 5; } }</script>
     19
     20<!-- Case 3: Same class body indented on the same line. -->
     21<script>class globalClass3 { constructor(x) { return x; } stuff() { return 5; } }</script>
     22
     23<script>
     24section = "global class expressions with identical class bodies from different scripts";
     25</script>
     26<!-- Case 1: A global class expression: -->
     27    <script>var globalClassExpr1 = class { constructor(x) { return x; } stuff() { return 5; } }</script>
     28
     29<!-- Case 2: Same class body as Case 1 but indented. -->
     30        <script>var globalClassExpr2 = class { constructor(x) { return x; } stuff() { return 5; } }</script>
     31
     32<!-- Case 3: Same class body indented on the same line. -->
     33<script>var globalClassExpr3 = class { constructor(x) { return x; } stuff() { return 5; } }</script>
     34
     35<script>
     36test(globalClass1, "globalClass1", "class globalClass1 { constructor(x) { return x; } stuff() { return 5; } }");
     37test(globalClass2, "globalClass2", "class globalClass2 { constructor(x) { return x; } stuff() { return 5; } }");
     38test(globalClass3, "globalClass3", "class globalClass3 { constructor(x) { return x; } stuff() { return 5; } }");
     39
     40test(globalClassExpr1, "globalClassExpr1", "class { constructor(x) { return x; } stuff() { return 5; } }");
     41test(globalClassExpr2, "globalClassExpr2", "class { constructor(x) { return x; } stuff() { return 5; } }");
     42test(globalClassExpr3, "globalClassExpr3", "class { constructor(x) { return x; } stuff() { return 5; } }");
     43
     44if (failureCount)
     45    throw Error("Found " + failureCount + " failures:\n" + failures);
     46</script>
     47
    848<script src="../resources/js-test-post.js"></script>
    949</body>
  • trunk/LayoutTests/js/script-tests/class-syntax-name.js

    r187680 r198042  
    6969runTestShouldThrow("class { constructor() {} }");
    7070runTestShouldNotThrow("class A { constructor() {} }");
    71 runTestShouldBe("class A { constructor() {} }; A.toString()", "'function A() {}'");
     71runTestShouldBe("class A { constructor() {} }; A.toString()", "'class A { constructor() {} }'");
    7272runTestShouldBeTrue("class A { constructor() {} }; (new A) instanceof A");
    73 runTestShouldBe("class A { constructor() { this.base = A; } }; (new A).base.toString()", "'function A() { this.base = A; }'");
     73runTestShouldBe("class A { constructor() { this.base = A; } }; (new A).base.toString()", "'class A { constructor() { this.base = A; } }'");
    7474runTestShouldNotThrow("class A { constructor() {} }; class B extends A {};");
    75 runTestShouldBe("class A { constructor() {} }; class B extends A { constructor() {} }; B.toString()", "'function B() {}'");
     75runTestShouldBe("class A { constructor() {} }; class B extends A { constructor() {} }; B.toString()", "'class B extends A { constructor() {} }'");
    7676runTestShouldBeTrue("class A { constructor() {} }; class B extends A {}; (new B) instanceof A");
    7777runTestShouldBeTrue("class A { constructor() {} }; class B extends A {}; (new B) instanceof B");
    78 runTestShouldBe("class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).base.toString()", "'function A() {}'");
    79 runTestShouldBe("class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).derived.toString()", "'function B() { super(); this.base = A; this.derived = B; }'");
     78runTestShouldBe("class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).base.toString()", "'class A { constructor() {} }'");
     79runTestShouldBe("class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).derived.toString()", "'class B extends A { constructor() { super(); this.base = A; this.derived = B; } }'");
    8080
    8181// Class expression. Class name not added to scope. Class name is available inside class scope.
     
    9191runTestShouldBe("typeof (new (class A {}))", '"object"');
    9292runTestShouldNotThrow("(new (class A { constructor() { this.base = A; } })).base");
    93 runTestShouldBe("(new (class A { constructor() { this.base = A; } })).base.toString()", '"function A() { this.base = A; }"');
     93runTestShouldBe("(new (class A { constructor() { this.base = A; } })).base.toString()", '"class A { constructor() { this.base = A; } }"');
    9494runTestShouldNotThrow("class A {}; (class B extends A {})");
    9595runTestShouldThrow("class A {}; (class B extends A {}); B");
     
    9797runTestShouldNotThrow("class A {}; new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })");
    9898runTestShouldBeTrue("class A {}; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })) instanceof A");
    99 runTestShouldBe("class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).base.toString()", "'function A() {}'");
    100 runTestShouldBe("class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).derived.toString()", "'function B() { super(); this.base = A; this.derived = B; }'");
     99runTestShouldBe("class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).base.toString()", "'class A { constructor() {} }'");
     100runTestShouldBe("class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).derived.toString()", "'class B extends A { constructor() { super(); this.base = A; this.derived = B; } }'");
    101101
    102102// Assignment of a class expression to a variable. Variable name available in scope, class name is not. Class name is available inside class scope.
     
    104104runTestShouldThrow("A");
    105105runTestShouldNotThrow("var VarA = class {}");
    106 runTestShouldBe("var VarA = class { constructor() {} }; VarA.toString()", "'function () {}'");
     106runTestShouldBe("var VarA = class { constructor() {} }; VarA.toString()", "'class { constructor() {} }'");
    107107runTestShouldThrow("VarA");
    108108runTestShouldNotThrow("var VarA = class A { constructor() {} }");
    109 runTestShouldBe("var VarA = class A { constructor() {} }; VarA.toString()", "'function A() {}'");
     109runTestShouldBe("var VarA = class A { constructor() {} }; VarA.toString()", "'class A { constructor() {} }'");
    110110runTestShouldThrow("var VarA = class A { constructor() {} }; A.toString()");
    111111runTestShouldBeTrue("var VarA = class A { constructor() {} }; (new VarA) instanceof VarA");
    112 runTestShouldBe("var VarA = class A { constructor() { this.base = A; } }; (new VarA).base.toString()", "'function A() { this.base = A; }'");
     112runTestShouldBe("var VarA = class A { constructor() { this.base = A; } }; (new VarA).base.toString()", "'class A { constructor() { this.base = A; } }'");
    113113runTestShouldNotThrow("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} };");
    114114runTestShouldThrow("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; B");
    115 runTestShouldBe("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; VarB.toString()", "'function B() {}'");
     115runTestShouldBe("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; VarB.toString()", "'class B extends VarA { constructor() {} }'");
    116116runTestShouldBeTrue("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { }; (new VarB) instanceof VarA");
    117117runTestShouldBeTrue("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { }; (new VarB) instanceof VarB");
     
    129129shouldBe("eval('var Foo = 10'); Foo", "10");
    130130shouldThrow("'use strict'; eval('var Foo = 10'); Foo");
    131 shouldBe("eval('class Bar { constructor() {} }; Bar.toString()');", "'function Bar() {}'");
     131shouldBe("eval('class Bar { constructor() {} }; Bar.toString()');", "'class Bar { constructor() {} }'");
    132132shouldThrow("'use strict'; eval('class Bar { constructor() {} }'); Bar.toString()");
  • trunk/LayoutTests/js/script-tests/function-toString-vs-name.js

    r197867 r198042  
    219219}
    220220
     221section = "class from statement";
     222(function () {
     223    class foo {}
     224    class bar {}
     225    class bax { static name() {} }
     226    let baz = bar;
     227    class goo extends foo {}
     228
     229    test(foo, "foo", "class foo {}");
     230    test(bar, "bar", "class bar {}");
     231    shouldBe("typeof bax.name of ", "bax", typeof bax.name, "function");
     232    shouldBe("toString of ", "bax", bax.toString(), "class bax { static name() {} }");
     233    test(baz, "bar", "class bar {}");
     234    test(goo, "goo", "class goo extends foo {}");
     235
     236    section = "bound class from statement";
     237    {
     238        let bound1 = foo.bind({});
     239        test(bound1, "bound foo", "function foo() { [native code] }");
     240        let bound2 = bar.bind({});
     241        test(bound2, "bound bar", "function bar() { [native code] }");
     242        let bound3 = bax.bind({});
     243        test(bound3, "bound ", "function() { [native code] }"); // bax.name is not a string.
     244        let bound4 = baz.bind({});
     245        test(bound4, "bound bar", "function bar() { [native code] }");
     246        let bound5 = goo.bind({});
     247        test(bound5, "bound goo", "function goo() { [native code] }");
     248    }
     249})();
     250
     251section = "class with constructor from statement";
     252(function () {
     253    class foo { constructor(x) {} }
     254    class bar { constructor() {} }
     255    class bax { static name() {} constructor() {} }
     256    let baz = bar;
     257    class goo extends foo { constructor() { super(5); } }
     258
     259    test(foo, "foo", "class foo { constructor(x) {} }");
     260    test(bar, "bar", "class bar { constructor() {} }");
     261    shouldBe("typeof bax.name of ", "bax", typeof bax.name, "function");
     262    shouldBe("toString of ", "bax", bax.toString(), "class bax { static name() {} constructor() {} }");
     263    test(baz, "bar", "class bar { constructor() {} }");
     264    test(goo, "goo", "class goo extends foo { constructor() { super(5); } }");
     265
     266    section = "bound class with constructor from statement";
     267    {
     268        let bound1 = foo.bind({});
     269        test(bound1, "bound foo", "function foo() { [native code] }");
     270        let bound2 = bar.bind({});
     271        test(bound2, "bound bar", "function bar() { [native code] }");
     272        let bound3 = bax.bind({});
     273        test(bound3, "bound ", "function() { [native code] }"); // bax.name is not a string.
     274        let bound4 = baz.bind({});
     275        test(bound4, "bound bar", "function bar() { [native code] }");
     276        let bound5 = goo.bind({});
     277        test(bound5, "bound goo", "function goo() { [native code] }");
     278    }
     279})();
     280
     281section = "class from expression";
     282(function () {
     283    let foo = class namedFoo {}
     284    let bar = class {}
     285    let bax = class { static name() {} }
     286    let baz = bar;
     287    let goo = class extends foo {}
     288
     289    test(foo, "namedFoo", "class namedFoo {}");
     290    test(bar, "bar", "class {}");
     291    shouldBe("typeof bax.name of ", "bax", typeof bax.name, "function");
     292    shouldBe("toString of ", "bax", bax.toString(), "class { static name() {} }");
     293    test(baz, "bar", "class {}");
     294    test(goo, "goo", "class extends foo {}");
     295
     296    section = "bound class from expression";
     297    {
     298        let bound1 = foo.bind({});
     299        test(bound1, "bound namedFoo", "function namedFoo() { [native code] }");
     300        let bound2 = bar.bind({});
     301        test(bound2, "bound bar", "function bar() { [native code] }");
     302        let bound3 = bax.bind({});
     303        test(bound3, "bound ", "function() { [native code] }"); // bax.name is not a string.
     304        let bound4 = baz.bind({});
     305        test(bound4, "bound bar", "function bar() { [native code] }");
     306        let bound5 = goo.bind({});
     307        test(bound5, "bound goo", "function goo() { [native code] }");
     308    }
     309})();
     310
     311section = "class with constructor from expression";
     312(function () {
     313    let foo = class namedFoo { constructor(x) {} }
     314    let bar = class { constructor() {} }
     315    let bax = class { static name() {} constructor() {} }
     316    let baz = bar;
     317    let goo = class extends foo { constructor() { super(x) } }
     318
     319    test(foo, "namedFoo", "class namedFoo { constructor(x) {} }");
     320    test(bar, "bar", "class { constructor() {} }");
     321    shouldBe("typeof bax.name of ", "bax", typeof bax.name, "function");
     322    shouldBe("toString of ", "bax", bax.toString(), "class { static name() {} constructor() {} }");
     323    test(baz, "bar", "class { constructor() {} }");
     324    test(goo, "goo", "class extends foo { constructor() { super(x) } }");
     325
     326    section = "bound class with constructor from expression";
     327    {
     328        let bound1 = foo.bind({});
     329        test(bound1, "bound namedFoo", "function namedFoo() { [native code] }");
     330        let bound2 = bar.bind({});
     331        test(bound2, "bound bar", "function bar() { [native code] }");
     332        let bound3 = bax.bind({});
     333        test(bound3, "bound ", "function() { [native code] }"); // bax.name is not a string.
     334        let bound4 = baz.bind({});
     335        test(bound4, "bound bar", "function bar() { [native code] }");
     336        let bound5 = goo.bind({});
     337        test(bound5, "bound goo", "function goo() { [native code] }");
     338    }
     339})();
     340
     341section = "class in object property";
     342(function () {
     343    class gooBase {}
     344    let o = {
     345        foo: class {},
     346        bar: class {},
     347        bax: class { static name() {} },
     348        goo: class extends gooBase {},
     349    };
     350    o.bay = o.bar;
     351    o.baz = class {};
     352
     353    test(o.foo, "foo", "class {}");
     354    test(o.bar, "bar", "class {}");
     355    shouldBe("typeof o.bax.name of ", "o.bax", typeof o.bax.name, "function");
     356    shouldBe("toString of ", "o.bax", o.bax.toString(), "class { static name() {} }");
     357    test(o.bay, "bar", "class {}");
     358    test(o.baz, "", "class {}");
     359    test(o.goo, "goo", "class extends gooBase {}");
     360
     361    section = "bound class in object property";
     362    {
     363        let bound1 = o.foo.bind({});
     364        test(bound1, "bound foo", "function foo() { [native code] }");
     365        let bound2 = o.bar.bind({});
     366        test(bound2, "bound bar", "function bar() { [native code] }");
     367        let bound3 = o.bax.bind({});
     368        test(bound3, "bound ", "function() { [native code] }"); // bax.name is not a string.
     369        let bound4 = o.bay.bind({});
     370        test(bound4, "bound bar", "function bar() { [native code] }");
     371        let bound5 = o.baz.bind({});
     372        test(bound5, "bound ", "function() { [native code] }");
     373        let bound6 = o.goo.bind({});
     374        test(bound6, "bound goo", "function goo() { [native code] }");
     375    }
     376})();
     377
     378section = "class with constructor in object property";
     379(function () {
     380    class gooBase { constructor(x) {} }
     381    let o = {
     382        foo: class { constructor(x) {} },
     383        bar: class { constructor() {} },
     384        bax: class { static name() {} constructor() {} },
     385        goo: class extends gooBase { constructor() { super(5); } },
     386    };
     387    o.bay = o.bar;
     388    o.baz = class { constructor() {} };
     389
     390    test(o.foo, "foo", "class { constructor(x) {} }");
     391    test(o.bar, "bar", "class { constructor() {} }");
     392    shouldBe("typeof o.bax.name of ", "o.bax", typeof o.bax.name, "function");
     393    shouldBe("toString of ", "o.bax", o.bax.toString(), "class { static name() {} constructor() {} }");
     394    test(o.bay, "bar", "class { constructor() {} }");
     395    test(o.baz, "", "class { constructor() {} }");
     396    test(o.goo, "goo", "class extends gooBase { constructor() { super(5); } }");
     397
     398    section = "bound class with constructor in object property";
     399    {
     400        let bound1 = o.foo.bind({});
     401        test(bound1, "bound foo", "function foo() { [native code] }");
     402        let bound2 = o.bar.bind({});
     403        test(bound2, "bound bar", "function bar() { [native code] }");
     404        let bound3 = o.bax.bind({});
     405        test(bound3, "bound ", "function() { [native code] }"); // bax.name is not a string.
     406        let bound4 = o.bay.bind({});
     407        test(bound4, "bound bar", "function bar() { [native code] }");
     408        let bound5 = o.baz.bind({});
     409        test(bound5, "bound ", "function() { [native code] }");
     410        let bound6 = o.goo.bind({});
     411        test(bound6, "bound goo", "function goo() { [native code] }");
     412    }
     413})();
     414
     415section = "global class statement";
     416// Checking if there are CodeCache badness that can result from global class statements
     417// with identical bodies.
     418class globalCS1 { constructor(x) { return x; } stuff() { return 5; } }
     419// Identical class body as CS1.
     420class globalCS2 { constructor(x) { return x; } stuff() { return 5; } }
     421// Identical constructor as CS2 & CS1, but different otherwise.
     422class globalCS3 { constructor(x) { return x; } stuff3() { return 15; } }
     423
     424test(globalCS1, "globalCS1", "class globalCS1 { constructor(x) { return x; } stuff() { return 5; } }");
     425test(globalCS2, "globalCS2", "class globalCS2 { constructor(x) { return x; } stuff() { return 5; } }");
     426test(globalCS3, "globalCS3", "class globalCS3 { constructor(x) { return x; } stuff3() { return 15; } }");
     427
     428section = "global class expression";
     429// Checking if there are CodeCache badness that can result from global class expressions
     430// with identical bodies.
     431var globalCE1 = class { constructor(x) { return x; } stuff() { return 5; } }
     432// Identical class body as CSE1.
     433var globalCE2 = class { constructor(x) { return x; } stuff() { return 5; } }
     434// Identical constructor as CSE2 & CSE1, but different otherwise.
     435var globalCE3 = class { constructor(x) { return x; } stuff3() { return 15; } }
     436
     437test(globalCE1, "globalCE1", "class { constructor(x) { return x; } stuff() { return 5; } }");
     438test(globalCE2, "globalCE2", "class { constructor(x) { return x; } stuff() { return 5; } }");
     439test(globalCE3, "globalCE3", "class { constructor(x) { return x; } stuff3() { return 15; } }");
     440
     441section = "class statements in eval";
     442// Checking if there are CodeCache badness that can result from class statements in
     443// identical eval statements.
     444(function () {
     445    let body1 = "class foo { constructor(x) { return x; } stuff() { return 5; } }";
     446    // Identical class body as body1.
     447    let body2 = "class foo { constructor(x) { return x; } stuff() { return 5; } }";
     448    // Identical constructor as body1 & body2, but different otherwise.
     449    let body3 = "class foo3 { constructor(x) { return x; } stuff3() { return 15; } }";
     450
     451    let bar1 = eval(body1);
     452    let bar2 = eval(body2);
     453    let bar3 = eval(body3);
     454    let bar4 = eval(body1);
     455
     456    test(bar1, "foo", "class foo { constructor(x) { return x; } stuff() { return 5; } }");
     457    test(bar2, "foo", "class foo { constructor(x) { return x; } stuff() { return 5; } }");
     458    test(bar3, "foo3", "class foo3 { constructor(x) { return x; } stuff3() { return 15; } }");
     459    test(bar4, "foo", "class foo { constructor(x) { return x; } stuff() { return 5; } }");
     460})();
     461
     462section = "class expressions in eval";
     463// Checking if there are CodeCache badness that can result from class expressions in
     464// identical eval statements.
     465(function () {
     466    let body1 = "var foo = class { constructor(x) { return x; } stuff() { return 5; } }; foo";
     467    // Identical class body as body1.
     468    let body2 = "var foo = class { constructor(x) { return x; } stuff() { return 5; } }; foo";
     469    // Identical constructor as body1 & body2, but different otherwise.
     470    let body3 = "var foo3 = class { constructor(x) { return x; } stuff3() { return 15; } }; foo3";
     471
     472    let bar1 = eval(body1);
     473    let bar2 = eval(body2);
     474    let bar3 = eval(body3);
     475    let bar4 = eval(body1);
     476
     477    test(bar1, "foo", "class { constructor(x) { return x; } stuff() { return 5; } }");
     478    test(bar2, "foo", "class { constructor(x) { return x; } stuff() { return 5; } }");
     479    test(bar3, "foo3", "class { constructor(x) { return x; } stuff3() { return 15; } }");
     480    test(bar4, "foo", "class { constructor(x) { return x; } stuff() { return 5; } }");
     481})();
     482
     483section = "class statements in dynamically created Functions";
     484// Checking if there are CodeCache badness that can result from dynamically created
     485// Function objects with class statements in identical bodies.
     486(function () {
     487    let body1 = "class foo { constructor(x) { return x; } stuff() { return 5; } } return foo;";
     488    // Identical class body as body1.
     489    let body2 = "class foo { constructor(x) { return x; } stuff() { return 5; } } return foo;";
     490    // Identical constructor as body1 & body2, but different otherwise.
     491    let body3 = "class foo3 { constructor(x) { return x; } stuff3() { return 15; } } return foo3;";
     492
     493    let bar1 = new Function(body1);
     494    let bar2 = new Function(body2);
     495    let bar3 = new Function(body3);
     496
     497    test(bar1(), "foo", "class foo { constructor(x) { return x; } stuff() { return 5; } }");
     498    test(bar2(), "foo", "class foo { constructor(x) { return x; } stuff() { return 5; } }");
     499    test(bar3(), "foo3", "class foo3 { constructor(x) { return x; } stuff3() { return 15; } }");
     500})();
     501
     502section = "class expressions in dynamically created Functions";
     503// Checking if there are CodeCache badness that can result from dynamically created
     504// Function objects with class expressions in identical bodies.
     505(function () {
     506    let body1 = "var foo = class { constructor(x) { return x; } stuff() { return 5; } }; return foo;";
     507    // Identical class body as body1.
     508    let body2 = "var foo = class { constructor(x) { return x; } stuff() { return 5; } }; return foo;";
     509    // Identical constructor as body1 & body2, but different otherwise.
     510    let body3 = "var foo3 = class { constructor(x) { return x; } stuff3() { return 15; } }; return foo3;";
     511
     512    let bar1 = new Function(body1);
     513    let bar2 = new Function(body2);
     514    let bar3 = new Function(body3);
     515
     516    test(bar1(), "foo", "class { constructor(x) { return x; } stuff() { return 5; } }");
     517    test(bar2(), "foo", "class { constructor(x) { return x; } stuff() { return 5; } }");
     518    test(bar3(), "foo3", "class { constructor(x) { return x; } stuff3() { return 15; } }");
     519})();
     520
    221521// FIXME: Uncomment these when we've added support for Function.name of computed properties.
    222522// section = "Object computed string property";
  • trunk/LayoutTests/platform/mac/inspector/model/remote-object-expected.txt

    r197966 r198042  
    47494749    "_description": "Person"
    47504750  },
    4751   "_functionDescription": "function Person(name){}"
     4751  "_functionDescription": "class Person { constructor(name){} get fullName(){} methodName(p1, p2){} }"
    47524752}
    47534753
     
    47724772    "_description": "B"
    47734773  },
    4774   "_functionDescription": "function B() { super(...arguments); }"
     4774  "_functionDescription": "class B extends Alpha { methodB(){} }"
    47754775}
    47764776
  • trunk/Source/JavaScriptCore/ChangeLog

    r198024 r198042  
     12016-03-11  Mark Lam  <mark.lam@apple.com>
     2
     3        Implement Function.name and Function#toString for ES6 class.
     4        https://bugs.webkit.org/show_bug.cgi?id=155336
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        The only thing that the ES6 spec says about toString with regards to class
     9        objects is:
     10
     11        "The string representation must have the syntax of a FunctionDeclaration,
     12        FunctionExpression, GeneratorDeclaration, GeneratorExpression, ClassDeclaration,
     13        ClassExpression, ArrowFunction, MethodDefinition, or GeneratorMethod depending
     14        upon the actual characteristics of the object."
     15
     16        Previously, invoking toString() on a class object will return the function
     17        source string of the class' constructor function.  This does not conform to the
     18        spec in that the toString string for a class does not have the syntax of a
     19        ClassDeclaration or ClassExpression.
     20
     21        This is now fixed by doing the following:
     22
     23        1. Added "m_classSource" to FunctionExecutable (and correspondingly to
     24           UnlinkedFunctionExecutable, FunctionMetadataNode, and ClassExprNode).
     25           m_classSource is the SourceCode for the code range "class ... { ... }".
     26
     27           Since the class constructor function is the in memory representation of the
     28           class object, only class constructor functions will have its m_classSource
     29           set.  m_classSource will be "null" (by default) for all other functions.
     30           This is how we know if a FunctionExecutable is for a class.
     31
     32           Note: FunctionExecutable does not have its own m_classSource.  It always gets
     33           it from its UnlinkedFunctionExecutable.  This is ok to do because our CodeCache
     34           currently does not cache UnlinkedFunctionExecutables for class constructors.
     35
     36        2. The ClassExprNode now tracks the SourceCode range for the class expression.
     37           This is used to set m_classSource in the UnlinkedFunctionExecutable at
     38           bytecode generation time, and the FunctionExecutable later at bytecode
     39           linking time.
     40
     41        3. Function.prototype.toString() now checks if the function is for a class.
     42           If so, it returns the string for the class source instead of just the
     43           function source for the class constructor.
     44
     45           Note: the class source is static from the time the class was parsed.  This
     46           can introduces some weirdness at runtime.  Consider the following:
     47
     48               var v1 = class {}
     49               v1.toString(); // yields "class {}".
     50
     51               class c2 extends v1 {}
     52
     53               c2.__proto__ === v1; // yields true i.e. c2 extends v1.
     54               c2.toString(); // yields "class c2 extends v1 {}" which is fine.
     55
     56               v1 = {}; // point v1 to something else now.
     57
     58               c2.__proto__ === v1; // now yields false i.e. c2 no longer extends v1.
     59                                    // c2 actually extends the class that v1 used to
     60                                    // point to, but ...
     61               c2.toString(); // still yields "class c2 extends v1 {}" which is no longer true.
     62
     63           It is unclear how we can best implement toString() to avoid this issue.
     64           The above behavior is how Chrome (Version 51.0.2671.0 canary (64-bit))
     65           currently implements toString() of a class, and we do the same in this patch.
     66           In Firefox (45.0), toString() of a class will yield the function source of it
     67           constructor function, which is not better.
     68
     69        In this patch, we also added ES6 compliance for Function.name on class objects:
     70
     71        4. The ClassExprNode now has a m_ecmaName string for tracking the inferred
     72           name of a class according to the ES6 spec.  The ASTBuilder now mirrors its
     73           handling of FuncExprNodes to ClassExprNodes in setting the nodes' m_ecmaName
     74           where relevant.
     75
     76           The m_ecmaName is later used to set the m_ecmaName of the FunctionExecutable
     77           of the class constructor, which in turn is used to populate the initial value
     78           of the Function.name property.
     79
     80        5. Also renamed some variable names (/m_metadata/metadata/) to be consistent with
     81           webkit naming convention.
     82
     83        * bytecode/UnlinkedFunctionExecutable.cpp:
     84        (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
     85        * bytecode/UnlinkedFunctionExecutable.h:
     86        * bytecompiler/BytecodeGenerator.cpp:
     87        (JSC::BytecodeGenerator::emitNewArrowFunctionExpression):
     88        (JSC::BytecodeGenerator::emitNewDefaultConstructor):
     89        * bytecompiler/BytecodeGenerator.h:
     90        * bytecompiler/NodesCodegen.cpp:
     91        (JSC::ClassExprNode::emitBytecode):
     92        * parser/ASTBuilder.h:
     93        (JSC::ASTBuilder::createAssignResolve):
     94        (JSC::ASTBuilder::createYield):
     95        (JSC::ASTBuilder::createClassExpr):
     96        (JSC::ASTBuilder::createFunctionExpr):
     97        (JSC::ASTBuilder::createProperty):
     98        (JSC::ASTBuilder::makeAssignNode):
     99        * parser/NodeConstructors.h:
     100        (JSC::FunctionParameters::FunctionParameters):
     101        (JSC::BaseFuncExprNode::BaseFuncExprNode):
     102        (JSC::FuncExprNode::FuncExprNode):
     103        (JSC::FuncDeclNode::FuncDeclNode):
     104        (JSC::ArrowFuncExprNode::ArrowFuncExprNode):
     105        (JSC::ClassDeclNode::ClassDeclNode):
     106        (JSC::ClassExprNode::ClassExprNode):
     107        * parser/Nodes.h:
     108        (JSC::ExpressionNode::isDestructuringNode):
     109        (JSC::ExpressionNode::isFuncExprNode):
     110        (JSC::ExpressionNode::isArrowFuncExprNode):
     111        (JSC::ExpressionNode::isClassExprNode):
     112        (JSC::ExpressionNode::isCommaNode):
     113        (JSC::ExpressionNode::isSimpleArray):
     114        (JSC::ExpressionNode::isAdd):
     115        * parser/Parser.cpp:
     116        (JSC::stringForFunctionMode):
     117        (JSC::Parser<LexerType>::parseFunctionInfo):
     118        (JSC::Parser<LexerType>::parseClass):
     119        * parser/ParserFunctionInfo.h:
     120        * parser/SyntaxChecker.h:
     121        (JSC::SyntaxChecker::createEmptyLetExpression):
     122        (JSC::SyntaxChecker::createYield):
     123        (JSC::SyntaxChecker::createClassExpr):
     124        (JSC::SyntaxChecker::createFunctionExpr):
     125        (JSC::SyntaxChecker::createFunctionMetadata):
     126        (JSC::SyntaxChecker::createArrowFunctionExpr):
     127        * runtime/Executable.cpp:
     128        (JSC::FunctionExecutable::FunctionExecutable):
     129        (JSC::FunctionExecutable::finishCreation):
     130        * runtime/Executable.h:
     131        * runtime/FunctionPrototype.cpp:
     132        (JSC::functionProtoFuncToString):
     133        * tests/es6.yaml:
     134
    11352016-03-11  Commit Queue  <commit-queue@webkit.org>
    2136
     
    580714        (catch): Deleted.
    581715
    582 >>>>>>> .r197960
    5837162016-03-08  Skachkov Oleksandr  <gskachkov@gmail.com>
    584717
  • trunk/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp

    r197867 r198042  
    11/*
    2  * Copyright (C) 2012, 2013, 2015 Apple Inc. All Rights Reserved.
     2 * Copyright (C) 2012-2013, 2015-2016 Apple Inc. All Rights Reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    106106    , m_inferredName(node->inferredName())
    107107    , m_sourceOverride(WTFMove(sourceOverride))
     108    , m_classSource(node->classSource())
    108109{
    109110    ASSERT(m_constructorKind == static_cast<unsigned>(node->constructorKind()));
  • trunk/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h

    r197867 r198042  
    11/*
    2  * Copyright (C) 2012-2015 Apple Inc. All Rights Reserved.
     2 * Copyright (C) 2012-2016 Apple Inc. All Rights Reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    7777    const Identifier& name() const { return m_name; }
    7878    const Identifier& ecmaName() const { return m_ecmaName; }
     79    void setEcmaName(const Identifier& name) { m_ecmaName = name; }
    7980    const Identifier& inferredName() const { return m_inferredName; }
    8081    unsigned parameterCount() const { return m_parameterCount; };
    8182    SourceParseMode parseMode() const { return static_cast<SourceParseMode>(m_sourceParseMode); };
     83
     84    const SourceCode& classSource() const { return m_classSource; };
     85    void setClassSource(const SourceCode& source) { m_classSource = source; };
     86
    8287    bool isInStrictContext() const { return m_isInStrictContext; }
    8388    FunctionMode functionMode() const { return static_cast<FunctionMode>(m_functionMode); }
     
    164169    Identifier m_inferredName;
    165170    RefPtr<SourceProvider> m_sourceOverride;
     171    SourceCode m_classSource;
    166172
    167173    VariableEnvironment m_parentScopeTDZVariables;
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp

    r197928 r198042  
    11/*
    2  * Copyright (C) 2008, 2009, 2012-2015 Apple Inc. All rights reserved.
     2 * Copyright (C) 2008-2009, 2012-2016 Apple Inc. All rights reserved.
    33 * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca>
    44 * Copyright (C) 2012 Igalia, S.L.
     
    28112811}
    28122812
    2813 RegisterID* BytecodeGenerator::emitNewDefaultConstructor(RegisterID* dst, ConstructorKind constructorKind, const Identifier& name)
     2813RegisterID* BytecodeGenerator::emitNewDefaultConstructor(RegisterID* dst, ConstructorKind constructorKind, const Identifier& name,
     2814    const Identifier& ecmaName, const SourceCode& classSource)
    28142815{
    28152816    UnlinkedFunctionExecutable* executable = m_vm->builtinExecutables()->createDefaultConstructor(constructorKind, name);
    28162817    executable->setInvalidTypeProfilingOffsets();
     2818    executable->setEcmaName(ecmaName);
     2819    executable->setClassSource(classSource);
    28172820
    28182821    unsigned index = m_codeBlock->addFunctionExpr(executable);
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h

    r197928 r198042  
    11/*
    2  * Copyright (C) 2008, 2009, 2012-2015 Apple Inc. All rights reserved.
     2 * Copyright (C) 2008-2009, 2012-2016 Apple Inc. All rights reserved.
    33 * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca>
    44 * Copyright (C) 2012 Igalia, S.L.
     
    519519        RegisterID* emitNewFunction(RegisterID* dst, FunctionMetadataNode*);
    520520        RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode* func);
    521         RegisterID* emitNewDefaultConstructor(RegisterID* dst, ConstructorKind, const Identifier& name);
     521        RegisterID* emitNewDefaultConstructor(RegisterID* dst, ConstructorKind, const Identifier& name, const Identifier& ecmaName, const SourceCode& classSource);
    522522        RegisterID* emitNewArrowFunctionExpression(RegisterID*, ArrowFuncExprNode*);
    523523        RegisterID* emitNewRegExp(RegisterID* dst, RegExp*);
  • trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r197410 r198042  
    22*  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
    33*  Copyright (C) 2001 Peter Kelly (pmk@post.com)
    4 *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012, 2013, 2015 Apple Inc. All rights reserved.
     4*  Copyright (C) 2003-2009, 2012-2013, 2015-2016 Apple Inc. All rights reserved.
    55*  Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
    66*  Copyright (C) 2007 Maks Orlovich
     
    32213221
    32223222    // FIXME: Make the prototype non-configurable & non-writable.
    3223     if (m_constructorExpression)
     3223    if (m_constructorExpression) {
     3224        ASSERT(m_constructorExpression->isFuncExprNode());
     3225        FunctionMetadataNode* metadata = static_cast<FuncExprNode*>(m_constructorExpression)->metadata();
     3226        metadata->setEcmaName(ecmaName());
     3227        metadata->setClassSource(m_classSource);
    32243228        constructor = generator.emitNode(dst, m_constructorExpression);
    3225     else {
     3229    } else {
    32263230        constructor = generator.emitNewDefaultConstructor(generator.finalDestination(dst),
    3227             m_classHeritage ? ConstructorKind::Derived : ConstructorKind::Base, m_name);
     3231            m_classHeritage ? ConstructorKind::Derived : ConstructorKind::Base,
     3232            m_name, ecmaName(), m_classSource);
    32283233    }
    32293234
  • trunk/Source/JavaScriptCore/parser/ASTBuilder.h

    r197947 r198042  
    11/*
    2  * Copyright (C) 2010, 2013 Apple Inc. All rights reserved.
     2 * Copyright (C) 2010, 2013, 2016 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    347347            metadata->setEcmaName(ident);
    348348            metadata->setInferredName(ident);
    349         }
     349        } else if (rhs->isClassExprNode())
     350            static_cast<ClassExprNode*>(rhs)->setEcmaName(ident);
    350351        AssignResolveNode* node = new (m_parserArena) AssignResolveNode(location, ident, rhs, assignmentContext);
    351352        setExceptionLocation(node, start, divot, end);
     
    365366    }
    366367
    367     ClassExprNode* createClassExpr(const JSTokenLocation& location, const Identifier& name, VariableEnvironment& classEnvironment, ExpressionNode* constructor,
     368    ClassExprNode* createClassExpr(const JSTokenLocation& location, const ParserClassInfo<ASTBuilder>& classInfo, VariableEnvironment& classEnvironment, ExpressionNode* constructor,
    368369        ExpressionNode* parentClass, PropertyListNode* instanceMethods, PropertyListNode* staticMethods)
    369370    {
    370         return new (m_parserArena) ClassExprNode(location, name, classEnvironment, constructor, parentClass, instanceMethods, staticMethods);
     371        SourceCode source = m_sourceCode->subExpression(classInfo.startOffset, classInfo.endOffset, classInfo.startLine, classInfo.startColumn);
     372        return new (m_parserArena) ClassExprNode(location, *classInfo.className, source, classEnvironment, constructor, parentClass, instanceMethods, staticMethods);
    371373    }
    372374
     
    443445            metadata->setEcmaName(*propertyName);
    444446            metadata->setInferredName(*propertyName);
    445         }
     447        } else if (node->isClassExprNode())
     448            static_cast<ClassExprNode*>(node)->setEcmaName(*propertyName);
    446449        return new (m_parserArena) PropertyNode(*propertyName, node, type, putType, superBinding);
    447450    }
     
    13021305                metadata->setEcmaName(resolve->identifier());
    13031306                metadata->setInferredName(resolve->identifier());
    1304             }
     1307            } else if (expr->isClassExprNode())
     1308                static_cast<ClassExprNode*>(expr)->setEcmaName(resolve->identifier());
    13051309            AssignResolveNode* node = new (m_parserArena) AssignResolveNode(location, resolve->identifier(), expr, AssignmentContext::AssignmentExpression);
    13061310            setExceptionLocation(node, start, divot, end);
  • trunk/Source/JavaScriptCore/parser/NodeConstructors.h

    r197915 r198042  
    11/*
    2  *  Copyright (C) 2009, 2013 Apple Inc. All rights reserved.
     2 *  Copyright (C) 2009, 2013, 2015 Apple Inc. All rights reserved.
    33 *
    44 *  This library is free software; you can redistribute it and/or
     
    891891
    892892   
    893     inline BaseFuncExprNode::BaseFuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* m_metadata, const SourceCode& source)
    894         : ExpressionNode(location)
    895         , m_metadata(m_metadata)
     893    inline BaseFuncExprNode::BaseFuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source)
     894        : ExpressionNode(location)
     895        , m_metadata(metadata)
    896896    {
    897897        m_metadata->finishParsing(source, ident, FunctionExpression);
    898898    }
    899899
    900     inline FuncExprNode::FuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* m_metadata, const SourceCode& source)
    901         : BaseFuncExprNode(location, ident, m_metadata, source)
    902     {
    903     }
    904 
    905     inline FuncDeclNode::FuncDeclNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* m_metadata, const SourceCode& source)
    906         : StatementNode(location)
    907         , m_metadata(m_metadata)
     900    inline FuncExprNode::FuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source)
     901        : BaseFuncExprNode(location, ident, metadata, source)
     902    {
     903    }
     904
     905    inline FuncDeclNode::FuncDeclNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source)
     906        : StatementNode(location)
     907        , m_metadata(metadata)
    908908    {
    909909        m_metadata->finishParsing(source, ident, FunctionDeclaration);
    910910    }
    911911
    912     inline ArrowFuncExprNode::ArrowFuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* m_metadata, const SourceCode& source)
    913         : BaseFuncExprNode(location, ident, m_metadata, source)
     912    inline ArrowFuncExprNode::ArrowFuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source)
     913        : BaseFuncExprNode(location, ident, metadata, source)
    914914    {
    915915    }
     
    928928    }
    929929
    930     inline ClassExprNode::ClassExprNode(const JSTokenLocation& location, const Identifier& name, VariableEnvironment& classEnvironment, ExpressionNode* constructorExpression, ExpressionNode* classHeritage, PropertyListNode* instanceMethods, PropertyListNode* staticMethods)
     930    inline ClassExprNode::ClassExprNode(const JSTokenLocation& location, const Identifier& name, const SourceCode& classSource, VariableEnvironment& classEnvironment, ExpressionNode* constructorExpression, ExpressionNode* classHeritage, PropertyListNode* instanceMethods, PropertyListNode* staticMethods)
    931931        : ExpressionNode(location)
    932932        , VariableEnvironmentNode(classEnvironment)
     933        , m_classSource(classSource)
    933934        , m_name(name)
     935        , m_ecmaName(&name)
    934936        , m_constructorExpression(constructorExpression)
    935937        , m_classHeritage(classHeritage)
  • trunk/Source/JavaScriptCore/parser/Nodes.h

    r197928 r198042  
    22 *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
    33 *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
    4  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2013, 2015 Apple Inc. All rights reserved.
     4 *  Copyright (C) 2003-2009, 2013, 2015-2016 Apple Inc. All rights reserved.
    55 *  Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
    66 *  Copyright (C) 2007 Maks Orlovich
     
    167167        virtual bool isFuncExprNode() const { return false; }
    168168        virtual bool isArrowFuncExprNode() const { return false; }
     169        virtual bool isClassExprNode() const { return false; }
    169170        virtual bool isCommaNode() const { return false; }
    170171        virtual bool isSimpleArray() const { return false; }
     
    18451846        void overrideName(const Identifier& ident) { m_ident = ident; }
    18461847        const Identifier& ident() { return m_ident; }
    1847         void setEcmaName(const Identifier& ecmaName) { ASSERT(!ecmaName.isNull()); m_ecmaName = ecmaName; }
     1848        void setEcmaName(const Identifier& ecmaName) { m_ecmaName = ecmaName; }
    18481849        const Identifier& ecmaName() { return m_ident.isEmpty() ? m_ecmaName : m_ident; }
    18491850        void setInferredName(const Identifier& inferredName) { ASSERT(!inferredName.isNull()); m_inferredName = inferredName; }
     
    18631864
    18641865        const SourceCode& source() const { return m_source; }
     1866        const SourceCode& classSource() const { return m_classSource; }
     1867        void setClassSource(const SourceCode& source) { m_classSource = source; }
    18651868
    18661869        int startStartOffset() const { return m_startStartOffset; }
     
    18891892        int m_parametersStart;
    18901893        SourceCode m_source;
     1894        SourceCode m_classSource;
    18911895        int m_startStartOffset;
    18921896        unsigned m_parameterCount;
     
    19751979        using ParserArenaDeletable::operator new;
    19761980
    1977         ClassExprNode(const JSTokenLocation&, const Identifier&, VariableEnvironment& classEnvironment, ExpressionNode* constructorExpresssion,
     1981        ClassExprNode(const JSTokenLocation&, const Identifier&, const SourceCode& classSource,
     1982            VariableEnvironment& classEnvironment, ExpressionNode* constructorExpresssion,
    19781983            ExpressionNode* parentClass, PropertyListNode* instanceMethods, PropertyListNode* staticMethods);
    19791984
    19801985        const Identifier& name() { return m_name; }
    1981 
    1982     private:
    1983         RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
    1984 
     1986        const Identifier& ecmaName() { return m_ecmaName ? *m_ecmaName : m_name; }
     1987        void setEcmaName(const Identifier& name) { m_ecmaName = m_name.isNull() ? &name : &m_name; }
     1988
     1989    private:
     1990        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
     1991
     1992        bool isClassExprNode() const override { return true; }
     1993
     1994        SourceCode m_classSource;
    19851995        const Identifier& m_name;
     1996        const Identifier* m_ecmaName;
    19861997        ExpressionNode* m_constructorExpression;
    19871998        ExpressionNode* m_classHeritage;
  • trunk/Source/JavaScriptCore/parser/Parser.cpp

    r197947 r198042  
    22 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
    33 *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
    4  *  Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. All rights reserved.
     4 *  Copyright (C) 2003, 2006-2010, 2013, 2016 Apple Inc. All rights reserved.
    55 *
    66 *  This library is free software; you can redistribute it and/or
     
    21942194    ASSERT(match(CLASSTOKEN));
    21952195    JSTokenLocation location(tokenLocation());
     2196    info.startLine = location.line;
     2197    info.startColumn = tokenColumn();
     2198    info.startOffset = location.startOffset;
    21962199    next();
    21972200
     
    22042207    if (match(IDENT)) {
    22052208        className = m_token.m_data.ident;
    2206         info.className = className;
    22072209        next();
    22082210        failIfTrue(classScope->declareLexicalVariable(className, true) & DeclarationResult::InvalidStrictMode, "'", className->impl(), "' is not a valid class name");
     
    22152217        className = &m_vm->propertyNames->nullIdentifier;
    22162218    ASSERT(className);
     2219    info.className = className;
    22172220
    22182221    TreeExpression parentClass = 0;
     
    23442347    }
    23452348
     2349    info.endOffset = tokenLocation().endOffset - 1;
    23462350    consumeOrFail(CLOSEBRACE, "Expected a closing '}' after a class body");
    23472351
    2348     auto classExpression = context.createClassExpr(location, *className, classScope->finalizeLexicalEnvironment(), constructor, parentClass, instanceMethods, staticMethods);
     2352    auto classExpression = context.createClassExpr(location, info, classScope->finalizeLexicalEnvironment(), constructor, parentClass, instanceMethods, staticMethods);
    23492353    popScope(classScope, TreeBuilder::NeedsFreeVariableInfo);
    23502354    return classExpression;
  • trunk/Source/JavaScriptCore/parser/ParserFunctionInfo.h

    r192147 r198042  
    11/*
    2  * Copyright (C) 2015 Apple Inc. All rights reserved.
     2 * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    4444template <class TreeBuilder>
    4545struct ParserClassInfo {
    46     const Identifier* className = 0;
     46    const Identifier* className { nullptr };
     47    unsigned startOffset { 0 };
     48    unsigned endOffset { 0 };
     49    int startLine { 0 };
     50    unsigned startColumn { 0 };
    4751};
    4852
  • trunk/Source/JavaScriptCore/parser/SyntaxChecker.h

    r197947 r198042  
    11/*
    2  * Copyright (C) 2010, 2013 Apple Inc. All rights reserved.
     2 * Copyright (C) 2010, 2013, 2016 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    183183    ExpressionType createYield(const JSTokenLocation&) { return YieldExpr; }
    184184    ExpressionType createYield(const JSTokenLocation&, ExpressionType, bool, int, int, int) { return YieldExpr; }
    185     ClassExpression createClassExpr(const JSTokenLocation&, const Identifier&, VariableEnvironment&, ExpressionType, ExpressionType, PropertyList, PropertyList) { return ClassExpr; }
     185    ClassExpression createClassExpr(const JSTokenLocation&, const ParserClassInfo<SyntaxChecker>&, VariableEnvironment&, ExpressionType, ExpressionType, PropertyList, PropertyList) { return ClassExpr; }
    186186    ExpressionType createFunctionExpr(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return FunctionExpr; }
    187187    int createFunctionMetadata(const JSTokenLocation&, const JSTokenLocation&, int, int, bool, int, int, int, ConstructorKind, SuperBinding, unsigned, SourceParseMode, bool, InnerArrowFunctionCodeFeatures = NoInnerArrowFunctionFeatures) { return FunctionBodyResult; }
  • trunk/Source/JavaScriptCore/runtime/Executable.h

    r197867 r198042  
    11/*
    2  * Copyright (C) 2009, 2010, 2013-2015 Apple Inc. All rights reserved.
     2 * Copyright (C) 2009, 2010, 2013-2016 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    663663    bool isBuiltinFunction() const { return m_unlinkedExecutable->isBuiltinFunction(); }
    664664    ConstructAbility constructAbility() const { return m_unlinkedExecutable->constructAbility(); }
     665    bool isClass() const { return !classSource().isNull(); }
    665666    bool isArrowFunction() const { return parseMode() == SourceParseMode::ArrowFunctionMode; }
    666667    bool isGetter() const { return parseMode() == SourceParseMode::GetterMode; }
     
    673674    size_t parameterCount() const { return m_unlinkedExecutable->parameterCount(); } // Excluding 'this'!
    674675    SourceParseMode parseMode() const { return m_unlinkedExecutable->parseMode(); }
     676    const SourceCode& classSource() const { return m_unlinkedExecutable->classSource(); }
    675677
    676678    static void visitChildren(JSCell*, SlotVisitor&);
  • trunk/Source/JavaScriptCore/runtime/FunctionPrototype.cpp

    r197815 r198042  
    11/*
    22 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
    3  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2015 Apple Inc. All rights reserved.
     3 *  Copyright (C) 2003-2009, 2015-2016 Apple Inc. All rights reserved.
    44 *
    55 *  This library is free software; you can redistribute it and/or
     
    9090
    9191        FunctionExecutable* executable = function->jsExecutable();
    92        
     92        if (executable->isClass()) {
     93            StringView classSource = executable->classSource().view();
     94            return JSValue::encode(jsString(exec, classSource.toStringWithoutCopying()));
     95        }
     96
    9397        String functionHeader = executable->isArrowFunction() ? "" : "function ";
    9498       
  • trunk/Source/JavaScriptCore/tests/es6.yaml

    r198023 r198042  
    802802  cmd: runES6 :normal
    803803- path: es6/function_name_property_object_methods_class.js
    804   cmd: runES6 :fail
     804  cmd: runES6 :normal
    805805- path: es6/function_name_property_object_methods_function.js
    806806  cmd: runES6 :normal
     
    810810  cmd: runES6 :fail
    811811- path: es6/function_name_property_variables_class.js
    812   cmd: runES6 :fail
     812  cmd: runES6 :normal
    813813- path: es6/function_name_property_variables_function.js
    814814  cmd: runES6 :normal
Note: See TracChangeset for help on using the changeset viewer.