Changeset 219443 in webkit


Ignore:
Timestamp:
Jul 13, 2017 12:11:45 AM (7 years ago)
Author:
Caio Lima
Message:

[ESnext] Implement Object Spread
https://bugs.webkit.org/show_bug.cgi?id=167963

Reviewed by Saam Barati.

JSTests:

  • stress/obj-rest-destructuring-order.js: Added.

(assert):
(o.get z):
(o.get a):

  • stress/obj-spread-order.js: Added.

(assert):
(o.get z):
(o.get a):

  • stress/object-spread.js: Added.

(let.assert):
(assert.sameValue):
(let.o.get a):
(let.obj.get c):
(cthulhu.get x):
(let.obj.set c):
(calls.o.get z):
(calls.o.get a):
(try.let.obj.get foo):
(get calls):

Source/JavaScriptCore:

This patch implements ECMA262 stage 3 Object Spread proposal [1].
It's implemented using CopyDataPropertiesNoExclusions to copy
all enumerable keys from object being spreaded. The implementation of
CopyDataPropertiesNoExclusions follows the CopyDataProperties
implementation, however we don't receive excludedNames as parameter.

[1] - https://github.com/tc39/proposal-object-rest-spread

  • builtins/GlobalOperations.js:

(globalPrivate.copyDataPropertiesNoExclusions):

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::emitLoad):

  • bytecompiler/NodesCodegen.cpp:

(JSC::PropertyListNode::emitBytecode):
(JSC::ObjectSpreadExpressionNode::emitBytecode):

  • parser/ASTBuilder.h:

(JSC::ASTBuilder::createObjectSpreadExpression):
(JSC::ASTBuilder::createProperty):

  • parser/NodeConstructors.h:

(JSC::PropertyNode::PropertyNode):
(JSC::ObjectSpreadExpressionNode::ObjectSpreadExpressionNode):

  • parser/Nodes.h:

(JSC::ObjectSpreadExpressionNode::expression):

  • parser/Parser.cpp:

(JSC::Parser<LexerType>::parseProperty):

  • parser/SyntaxChecker.h:

(JSC::SyntaxChecker::createObjectSpreadExpression):
(JSC::SyntaxChecker::createProperty):

LayoutTests:

  • js/parser-syntax-check-expected.txt:
  • js/script-tests/parser-syntax-check.js:
Location:
trunk
Files:
3 added
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r219433 r219443  
     12017-07-13  Caio Lima  <ticaiolima@gmail.com>
     2
     3        [ESnext] Implement Object Spread
     4        https://bugs.webkit.org/show_bug.cgi?id=167963
     5
     6        Reviewed by Saam Barati.
     7
     8        * stress/obj-rest-destructuring-order.js: Added.
     9        (assert):
     10        (o.get z):
     11        (o.get a):
     12        * stress/obj-spread-order.js: Added.
     13        (assert):
     14        (o.get z):
     15        (o.get a):
     16        * stress/object-spread.js: Added.
     17        (let.assert):
     18        (assert.sameValue):
     19        (let.o.get a):
     20        (let.obj.get c):
     21        (cthulhu.get x):
     22        (let.obj.set c):
     23        (calls.o.get z):
     24        (calls.o.get a):
     25        (try.let.obj.get foo):
     26        (get calls):
     27
    1282017-07-12  Saam Barati  <sbarati@apple.com>
    229
  • trunk/LayoutTests/ChangeLog

    r219426 r219443  
     12017-07-13  Caio Lima  <ticaiolima@gmail.com>
     2
     3        [ESnext] Implement Object Spread
     4        https://bugs.webkit.org/show_bug.cgi?id=167963
     5
     6        Reviewed by Saam Barati.
     7
     8        * js/parser-syntax-check-expected.txt:
     9        * js/script-tests/parser-syntax-check.js:
     10
    1112017-07-12  Nan Wang  <n_wang@apple.com>
    212
  • trunk/LayoutTests/js/parser-syntax-check-expected.txt

    r218861 r219443  
    12341234PASS Valid:   "try { throw {a:2} } catch({...rest}) {}"
    12351235PASS Valid:   "function f() { try { throw {a:2} } catch({...rest}) {} }"
     1236PASS Valid:   "let c = {}; let o = {a: 1, b: 2, ...c};"
     1237PASS Valid:   "function f() { let c = {}; let o = {a: 1, b: 2, ...c}; }"
     1238PASS Valid:   "let o = {a: 1, b: 3, ...{}};"
     1239PASS Valid:   "function f() { let o = {a: 1, b: 3, ...{}}; }"
     1240PASS Valid:   "let o = {a: 1, b: 2, ...null, c: 3};"
     1241PASS Valid:   "function f() { let o = {a: 1, b: 2, ...null, c: 3}; }"
     1242PASS Valid:   "let o = {a: 1, b: 2, ...undefined, c: 3};"
     1243PASS Valid:   "function f() { let o = {a: 1, b: 2, ...undefined, c: 3}; }"
     1244PASS Valid:   "let o = {a: 1, b: 2, ...{...{}}, c: 3};"
     1245PASS Valid:   "function f() { let o = {a: 1, b: 2, ...{...{}}, c: 3}; }"
     1246PASS Valid:   "let c = {}; let o = {a: 1, b: 2, ...c, d: 3, ...c, e: 5};"
     1247PASS Valid:   "function f() { let c = {}; let o = {a: 1, b: 2, ...c, d: 3, ...c, e: 5}; }"
     1248PASS Valid:   "let o = {a: 1, b: 2, ...d = {e: 2}, c: 3};"
     1249PASS Valid:   "function f() { let o = {a: 1, b: 2, ...d = {e: 2}, c: 3}; }"
     1250PASS Valid:   "let p = true; let o = {a: 1, b: 2, ...d = p ? {e: 2} : {f: 4}, c: 3};"
     1251PASS Valid:   "function f() { let p = true; let o = {a: 1, b: 2, ...d = p ? {e: 2} : {f: 4}, c: 3}; }"
     1252PASS Valid:   "let o = {a: 1, b: 2, ...(a) => 3, c: 3};"
     1253PASS Valid:   "function f() { let o = {a: 1, b: 2, ...(a) => 3, c: 3}; }"
     1254PASS Valid:   "function * foo() { return {a: 1, b: 2, ...yield, c: 3}; }"
     1255PASS Valid:   "function f() { function * foo() { return {a: 1, b: 2, ...yield, c: 3}; } }"
    12361256PASS Invalid: "function * foo(o) { ({...{ x = yield }} = o); }". Produced the following syntax error: "SyntaxError: Unexpected token '='. Expected a ':' following the property name 'x'."
    12371257PASS Invalid: "function f() { function * foo(o) { ({...{ x = yield }} = o); } }". Produced the following syntax error: "SyntaxError: Unexpected token '='. Expected a ':' following the property name 'x'."
  • trunk/LayoutTests/js/script-tests/parser-syntax-check.js

    r218861 r219443  
    725725valid("var a, b, c; ({a, b, ...r} = {a: 1, b: 2, c: 3, d: 4});");
    726726valid("try { throw {a:2} } catch({...rest}) {}");
     727valid("let c = {}; let o = {a: 1, b: 2, ...c};");
     728valid("let o = {a: 1, b: 3, ...{}};");
     729valid("let o = {a: 1, b: 2, ...null, c: 3};");
     730valid("let o = {a: 1, b: 2, ...undefined, c: 3};");
     731valid("let o = {a: 1, b: 2, ...{...{}}, c: 3};");
     732valid("let c = {}; let o = {a: 1, b: 2, ...c, d: 3, ...c, e: 5};");
     733valid("let o = {a: 1, b: 2, ...d = {e: 2}, c: 3};");
     734valid("let p = true; let o = {a: 1, b: 2, ...d = p ? {e: 2} : {f: 4}, c: 3};");
     735valid("let o = {a: 1, b: 2, ...(a) => 3, c: 3};");
     736valid("function * foo() { return {a: 1, b: 2, ...yield, c: 3}; }");
    727737invalid("function * foo(o) { ({...{ x = yield }} = o); }");
    728738invalid("var {...r = {a: 2}} = {a: 1, b: 2};");
  • trunk/Source/JavaScriptCore/ChangeLog

    r219437 r219443  
     12017-07-13  Caio Lima  <ticaiolima@gmail.com>
     2
     3        [ESnext] Implement Object Spread
     4        https://bugs.webkit.org/show_bug.cgi?id=167963
     5
     6        Reviewed by Saam Barati.
     7
     8        This patch implements ECMA262 stage 3 Object Spread proposal [1].
     9        It's implemented using CopyDataPropertiesNoExclusions to copy
     10        all enumerable keys from object being spreaded. The implementation of
     11        CopyDataPropertiesNoExclusions follows the CopyDataProperties
     12        implementation, however we don't receive excludedNames as parameter.
     13
     14        [1] - https://github.com/tc39/proposal-object-rest-spread
     15
     16        * builtins/GlobalOperations.js:
     17        (globalPrivate.copyDataPropertiesNoExclusions):
     18        * bytecompiler/BytecodeGenerator.cpp:
     19        (JSC::BytecodeGenerator::emitLoad):
     20        * bytecompiler/NodesCodegen.cpp:
     21        (JSC::PropertyListNode::emitBytecode):
     22        (JSC::ObjectSpreadExpressionNode::emitBytecode):
     23        * parser/ASTBuilder.h:
     24        (JSC::ASTBuilder::createObjectSpreadExpression):
     25        (JSC::ASTBuilder::createProperty):
     26        * parser/NodeConstructors.h:
     27        (JSC::PropertyNode::PropertyNode):
     28        (JSC::ObjectSpreadExpressionNode::ObjectSpreadExpressionNode):
     29        * parser/Nodes.h:
     30        (JSC::ObjectSpreadExpressionNode::expression):
     31        * parser/Parser.cpp:
     32        (JSC::Parser<LexerType>::parseProperty):
     33        * parser/SyntaxChecker.h:
     34        (JSC::SyntaxChecker::createObjectSpreadExpression):
     35        (JSC::SyntaxChecker::createProperty):
     36
    1372017-07-12  Mark Lam  <mark.lam@apple.com>
    238
  • trunk/Source/JavaScriptCore/builtins/GlobalOperations.js

    r218861 r219443  
    11/*
    22 * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>.
     3 * Copyright (C) 2017 Caio Lima <ticaiolima@gmail.com>.
    34 *
    45 * Redistribution and use in source and binary forms, with or without
     
    105106    return target;
    106107}
     108
     109@globalPrivate
     110function copyDataPropertiesNoExclusions(target, source)
     111{
     112    if (!@isObject(target))
     113        @throwTypeError("target needs to be an object");
     114
     115    if (source == null)
     116        return target;
     117
     118    let from = @Object(source);
     119    let keys = @Reflect.@ownKeys(from);
     120    let keysLength = keys.length;
     121    for (let i = 0; i < keysLength; i++) {
     122        let nextKey = keys[i];
     123        if (@propertyIsEnumerable(from, nextKey)) {
     124            let propValue = from[nextKey];
     125            @defineEnumerableWritableConfigurableDataProperty(target, nextKey, propValue);
     126        }
     127    }
     128
     129    return target;
     130}
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp

    r219209 r219443  
    19571957RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, IdentifierSet& set)
    19581958{
    1959     for (ConstantIndentifierSetEntry entry : m_codeBlock->constantIdentifierSets()) {
     1959    for (const auto& entry : m_codeBlock->constantIdentifierSets()) {
    19601960        if (entry.first != set)
    19611961            continue;
  • trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r219209 r219443  
    501501    if (p) {
    502502        // Build a list of getter/setter pairs to try to put them at the same time. If we encounter
    503         // a computed property, just emit everything as that may override previous values.
    504         bool hasComputedProperty = false;
     503        // a computed property or a spread, just emit everything as that may override previous values.
     504        bool canOverrideProperties = false;
    505505
    506506        typedef std::pair<PropertyNode*, PropertyNode*> GetterSetterPair;
     
    511511        for (PropertyListNode* q = p; q; q = q->m_next) {
    512512            PropertyNode* node = q->m_node;
    513             if (node->m_type & PropertyNode::Computed) {
    514                 hasComputedProperty = true;
     513            if (node->m_type & PropertyNode::Computed || node->m_type & PropertyNode::Spread) {
     514                canOverrideProperties = true;
    515515                break;
    516516            }
     517
    517518            if (node->m_type & PropertyNode::Constant)
    518519                continue;
     
    537538                emitPutConstantProperty(generator, dst, *node);
    538539                continue;
     540            } else if (node->m_type & PropertyNode::Spread) {
     541                generator.emitNode(dst, node->m_assign);
     542                continue;
    539543            }
    540544
     
    548552            ASSERT(node->m_type & (PropertyNode::Getter | PropertyNode::Setter));
    549553
    550             // This is a get/set property which may be overridden by a computed property later.
    551             if (hasComputedProperty) {
     554            // This is a get/set property which may be overridden by a computed property or spread later.
     555            if (canOverrideProperties) {
    552556                // Computed accessors.
    553557                if (node->m_type & PropertyNode::Computed) {
     
    42784282}
    42794283
     4284RegisterID* ObjectSpreadExpressionNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
     4285{
     4286    RefPtr<RegisterID> src = generator.newTemporary();
     4287    generator.emitNode(src.get(), m_expression);
     4288   
     4289    // load and call @copyDataPropertiesNoExclusions
     4290    auto var = generator.variable(generator.propertyNames().builtinNames().copyDataPropertiesNoExclusionsPrivateName());
     4291   
     4292    RefPtr<RegisterID> scope = generator.newTemporary();
     4293    generator.moveToDestinationIfNeeded(scope.get(), generator.emitResolveScope(scope.get(), var));
     4294    RefPtr<RegisterID> copyDataProperties = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound);
     4295   
     4296    CallArguments args(generator, nullptr, 2);
     4297    generator.emitLoad(args.thisRegister(), jsUndefined());
     4298    generator.emitMove(args.argumentRegister(0), dst);
     4299    generator.emitMove(args.argumentRegister(1), src.get());
     4300   
     4301    generator.emitCall(generator.newTemporary(), copyDataProperties.get(), NoExpectedFunction, args, divot(), divotStart(), divotEnd(), DebuggableCall::No);
     4302   
     4303    return dst;
     4304}
     4305
    42804306} // namespace JSC
  • trunk/Source/JavaScriptCore/parser/ASTBuilder.h

    r218861 r219443  
    276276    }
    277277
     278    ExpressionNode* createObjectSpreadExpression(const JSTokenLocation& location, ExpressionNode* expression, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end)
     279    {
     280        auto node = new (m_parserArena) ObjectSpreadExpressionNode(location, expression);
     281        setExceptionLocation(node, start, divot, end);
     282        return node;
     283    }
     284
    278285    TemplateStringNode* createTemplateString(const JSTokenLocation& location, const Identifier* cooked, const Identifier* raw)
    279286    {
     
    495502        }
    496503        return new (m_parserArena) PropertyNode(*propertyName, node, type, putType, superBinding, isClassProperty);
     504    }
     505    PropertyNode* createProperty(ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool, SuperBinding superBinding, bool isClassProperty)
     506    {
     507        return new (m_parserArena) PropertyNode(node, type, putType, superBinding, isClassProperty);
    497508    }
    498509    PropertyNode* createProperty(VM* vm, ParserArena& parserArena, double propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool, SuperBinding superBinding, bool isClassProperty)
  • trunk/Source/JavaScriptCore/parser/NodeConstructors.h

    r218794 r219443  
    233233    {
    234234    }
     235   
     236    inline PropertyNode::PropertyNode(ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding, bool isClassProperty)
     237        : m_name(nullptr)
     238        , m_assign(assign)
     239        , m_type(type)
     240        , m_needsSuperBinding(superBinding == SuperBinding::Needed)
     241        , m_putType(putType)
     242        , m_isClassProperty(isClassProperty)
     243    {
     244    }
    235245
    236246    inline PropertyNode::PropertyNode(ExpressionNode* name, ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding, bool isClassProperty)
    237         : m_name(0)
     247        : m_name(nullptr)
    238248        , m_expression(name)
    239249        , m_assign(assign)
     
    289299   
    290300    inline SpreadExpressionNode::SpreadExpressionNode(const JSTokenLocation& location, ExpressionNode* expression)
     301        : ExpressionNode(location)
     302        , m_expression(expression)
     303    {
     304    }
     305   
     306    inline ObjectSpreadExpressionNode::ObjectSpreadExpressionNode(const JSTokenLocation& location, ExpressionNode* expression)
    291307        : ExpressionNode(location)
    292308        , m_expression(expression)
  • trunk/Source/JavaScriptCore/parser/Nodes.h

    r218957 r219443  
    646646    class PropertyNode : public ParserArenaFreeable {
    647647    public:
    648         enum Type { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16 };
     648        enum Type { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16, Spread = 32 };
    649649        enum PutType { Unknown, KnownDirect };
    650650
    651651        PropertyNode(const Identifier&, ExpressionNode*, Type, PutType, SuperBinding, bool isClassProperty);
     652        PropertyNode(ExpressionNode*, Type, PutType, SuperBinding, bool isClassProperty);
    652653        PropertyNode(ExpressionNode* propertyName, ExpressionNode*, Type, PutType, SuperBinding, bool isClassProperty);
    653654
     
    665666        ExpressionNode* m_expression;
    666667        ExpressionNode* m_assign;
    667         unsigned m_type : 5;
     668        unsigned m_type : 6;
    668669        unsigned m_needsSuperBinding : 1;
    669670        unsigned m_putType : 1;
     
    745746       
    746747        bool isSpreadExpression() const override { return true; }
     748        ExpressionNode* m_expression;
     749    };
     750   
     751    class ObjectSpreadExpressionNode : public ExpressionNode, public ThrowableExpressionData {
     752    public:
     753        ObjectSpreadExpressionNode(const JSTokenLocation&, ExpressionNode*);
     754       
     755        ExpressionNode* expression() const { return m_expression; }
     756       
     757    private:
     758        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
     759       
    747760        ExpressionNode* m_expression;
    748761    };
  • trunk/Source/JavaScriptCore/parser/Parser.cpp

    r218861 r219443  
    38723872    case DOTDOTDOT: {
    38733873        if (m_useObjectRestSpread) {
    3874             classifyExpressionError(ErrorIndicatesPattern);
    3875             return 0;
     3874            auto spreadLocation = m_token.m_location;
     3875            auto start = m_token.m_startPosition;
     3876            auto divot = m_token.m_endPosition;
     3877            next();
     3878            TreeExpression elem = parseAssignmentExpressionOrPropagateErrorClass(context);
     3879            failIfFalse(elem, "Cannot parse subject of a spread operation");
     3880            auto node = context.createObjectSpreadExpression(spreadLocation, elem, start, divot, m_lastTokenEndPosition);
     3881            return context.createProperty(node, PropertyNode::Spread, PropertyNode::Unknown, complete, SuperBinding::NotNeeded, isClassProperty);
    38763882        }
    38773883        FALLTHROUGH;
  • trunk/Source/JavaScriptCore/parser/SyntaxChecker.h

    r218861 r219443  
    7777        DeleteExpr, ArrayLiteralExpr, BindingDestructuring, RestParameter,
    7878        ArrayDestructuring, ObjectDestructuring, SourceElementsResult,
    79         FunctionBodyResult, SpreadExpr, ArgumentsResult,
     79        FunctionBodyResult, SpreadExpr, ObjectSpreadExpr, ArgumentsResult,
    8080        PropertyListResult, ArgumentsListResult, ElementsListResult,
    8181        StatementResult, FormalParameterListResult, ClauseResult,
     
    195195    int createArguments(int) { return ArgumentsResult; }
    196196    ExpressionType createSpreadExpression(const JSTokenLocation&, ExpressionType, int, int, int) { return SpreadExpr; }
     197    ExpressionType createObjectSpreadExpression(const JSTokenLocation&, ExpressionType, int, int, int) { return ObjectSpreadExpr; }
    197198    TemplateString createTemplateString(const JSTokenLocation&, const Identifier*, const Identifier*) { return TemplateStringResult; }
    198199    TemplateStringList createTemplateStringList(TemplateString) { return TemplateStringListResult; }
     
    212213        ASSERT(name);
    213214        return Property(name, type);
     215    }
     216    Property createProperty(int, PropertyNode::Type type, PropertyNode::PutType, bool, SuperBinding, bool)
     217    {
     218        return Property(type);
    214219    }
    215220    Property createProperty(VM* vm, ParserArena& parserArena, double name, int, PropertyNode::Type type, PropertyNode::PutType, bool complete, SuperBinding, bool)
Note: See TracChangeset for help on using the changeset viewer.