@@ -1489,15 +1489,23 @@ exports.Obj = class Obj extends Base
14891489 return yes for prop in @properties when prop instanceof Splat
14901490 no
14911491
1492+ # Move rest property to the end of the list.
1493+ # `{a, rest..., b} = obj` -> `{a, b, rest...} = obj`
1494+ # `foo = ({a, rest..., b}) ->` -> `foo = {a, b, rest...}) ->`
1495+ reorderProperties : ->
1496+ props = @properties
1497+ splatProps = (i for prop, i in props when prop instanceof Splat)
1498+ props[splatProps[1 ]].error " multiple spread elements are disallowed" if splatProps ? .length > 1
1499+ splatProp = props .splice splatProps[0 ], 1
1500+ @objects = @properties = [].concat props, splatProp
1501+
14921502 compileNode : (o ) ->
1503+ @ reorderProperties () if @ hasSplat () and @lhs
14931504 props = @properties
14941505 if @generated
14951506 for node in props when node instanceof Value
14961507 node .error ' cannot have an implicit value in an implicit object'
14971508
1498- # Object spread properties. https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md
1499- return @ compileSpread o if @ hasSplat () and not @csx
1500-
15011509 idt = o .indent += TAB
15021510 lastNode = @ lastNode @properties
15031511
@@ -1558,7 +1566,7 @@ exports.Obj = class Obj extends Base
15581566 else
15591567 # `{ [expression] }` output as `{ [expression]: expression }`.
15601568 prop = new Assign key, prop .base .value , ' object'
1561- else if not prop .bareLiteral ? (IdentifierLiteral)
1569+ else if not prop .bareLiteral ? (IdentifierLiteral) and prop not instanceof Splat
15621570 prop = new Assign prop, prop, ' object'
15631571 if indent then answer .push @ makeCode indent
15641572 answer .push prop .compileToFragments (o, LEVEL_TOP)...
@@ -1577,30 +1585,6 @@ exports.Obj = class Obj extends Base
15771585 prop = prop .unwrapAll ()
15781586 prop .eachName iterator if prop .eachName ?
15791587
1580- # Object spread properties. https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md
1581- # `obj2 = {a: 1, obj..., c: 3, d: 4}` → `obj2 = _extends({}, {a: 1}, obj, {c: 3, d: 4})`
1582- compileSpread : (o ) ->
1583- props = @properties
1584- # Store object spreads.
1585- splatSlice = []
1586- propSlices = []
1587- slices = []
1588- addSlice = ->
1589- slices .push new Obj propSlices if propSlices .length
1590- slices .push splatSlice... if splatSlice .length
1591- splatSlice = []
1592- propSlices = []
1593- for prop in props
1594- if prop instanceof Splat
1595- splatSlice .push new Value prop .name
1596- addSlice ()
1597- else
1598- propSlices .push prop
1599- addSlice ()
1600- slices .unshift new Obj unless slices[0 ] instanceof Obj
1601- _extends = new Value new Literal utility ' _extends' , o
1602- (new Call _extends, slices).compileToFragments o
1603-
16041588 compileCSXAttributes : (o ) ->
16051589 props = @properties
16061590 answer = []
@@ -2213,12 +2197,11 @@ exports.Assign = class Assign extends Base
22132197 # know that, so that those nodes know that they’re assignable as
22142198 # destructured variables.
22152199 @variable .base .lhs = yes
2216- # Check if @variable contains Obj with splats.
2217- hasSplat = @variable .contains (node) -> node instanceof Obj and node .hasSplat ()
2218- return @ compileDestructuring o if not @variable .isAssignable () or @variable .isArray () and hasSplat
2219- # Object destructuring. Can be removed once ES proposal hits Stage 4.
2220- objDestructAnswer = @ compileObjectDestruct (o) if @variable .isObject () and hasSplat
2221- return objDestructAnswer if objDestructAnswer
2200+ unless @variable .isAssignable ()
2201+ if @variable .isObject () and @variable .base .hasSplat ()
2202+ return @ compileObjectDestruct o
2203+ else
2204+ return @ compileDestructuring o
22222205
22232206 return @ compileSplice o if @variable .isSplice ()
22242207 return @ compileConditional o if @context in [' ||=' , ' &&=' , ' ?=' ]
@@ -2289,92 +2272,18 @@ exports.Assign = class Assign extends Base
22892272 else
22902273 answer
22912274
2292- # Check object destructuring variable for rest elements;
2293- # can be removed once ES proposal hits Stage 4.
2275+ # Object rest property is not assignable: `{{a}...}`
22942276 compileObjectDestruct : (o ) ->
2295- # Returns a safe (cached) reference to the key for a given property
2296- getPropKey = (prop ) ->
2297- if prop instanceof Assign
2298- [prop .variable , key ] = prop .variable .cache o
2299- key
2300- else
2301- prop
2302-
2303- # Returns the name of a given property for use with excludeProps
2304- # Property names are quoted (e.g. `a: b` -> 'a'), and everything else uses the key reference
2305- # (e.g. `'a': b -> 'a'`, `"#{a}": b` -> <cached>`)
2306- getPropName = (prop ) ->
2307- key = getPropKey prop
2308- cached = prop instanceof Assign and prop .variable isnt key
2309- if cached or not key .isAssignable ()
2310- key
2311- else
2312- new Literal " '#{ key .compileWithoutComments o} '"
2313-
2314- # Recursive function for searching and storing rest elements in objects.
2315- # e.g. `{[properties...]} = source`.
2316- traverseRest = (properties , source ) =>
2317- restElements = []
2318- restIndex = undefined
2319- source = new Value source unless source .properties ?
2320-
2321- for prop, index in properties
2322- nestedSourceDefault = nestedSource = nestedProperties = null
2323- if prop instanceof Assign
2324- # prop is `k: expr`, we need to check `expr` for nested splats
2325- if prop .value .isObject ? ()
2326- # prop is `k = {...} `
2327- continue unless prop .context is ' object'
2328- # prop is `k: {...}`
2329- nestedProperties = prop .value .base .properties
2330- else if prop .value instanceof Assign and prop .value .variable .isObject ()
2331- # prop is `k: {...} = default`
2332- nestedProperties = prop .value .variable .base .properties
2333- [prop .value .value , nestedSourceDefault ] = prop .value .value .cache o
2334- if nestedProperties
2335- nestedSource = new Value source .base , source .properties .concat [new Access getPropKey prop]
2336- nestedSource = new Value new Op ' ?' , nestedSource, nestedSourceDefault if nestedSourceDefault
2337- restElements .push traverseRest (nestedProperties, nestedSource)...
2338- else if prop instanceof Splat
2339- prop .error " multiple rest elements are disallowed in object destructuring" if restIndex?
2340- restIndex = index
2341- restElements .push {
2342- name : prop .name .unwrapAll ()
2343- source
2344- excludeProps : new Arr (getPropName p for p in properties when p isnt prop)
2345- }
2346-
2347- if restIndex?
2348- # Remove rest element from the properties after iteration
2349- properties .splice restIndex, 1
2350-
2351- restElements
2352-
2353- # Cache the value for reuse with rest elements.
2354- valueRefTemp =
2355- if @value .shouldCache ()
2356- new IdentifierLiteral o .scope .freeVariable ' ref' , reserve : false
2357- else
2358- @value .base
2359-
2360- # Find all rest elements.
2361- restElements = traverseRest @variable .base .properties , valueRefTemp
2362- return no unless restElements and restElements .length > 0
2363-
2364- [@value , valueRef ] = @value .cache o
2365- result = new Block [@ ]
2366-
2367- for restElement in restElements
2368- value = new Call new Value (new Literal utility ' objectWithoutKeys' , o), [restElement .source , restElement .excludeProps ]
2369- result .push new Assign new Value (restElement .name ), value, null , param : if @param then ' alwaysDeclare' else null
2370-
2371- fragments = result .compileToFragments o
2372- if o .level is LEVEL_TOP
2373- # Remove leading tab and trailing semicolon
2374- fragments .shift ()
2375- fragments .pop ()
2376-
2377- fragments
2277+ @variable .base .reorderProperties ()
2278+ {properties : props } = @variable .base
2279+ [... , splat ] = props
2280+ splatProp = splat .name
2281+ assigns = []
2282+ refVal = new Value new IdentifierLiteral o .scope .freeVariable ' ref'
2283+ props .splice - 1 , 1 , new Splat refVal
2284+ assigns .push new Assign (new Value (new Obj props), @value ).compileToFragments o, LEVEL_LIST
2285+ assigns .push new Assign (new Value (splatProp), refVal).compileToFragments o, LEVEL_LIST
2286+ @ joinFragmentArrays assigns, ' , '
23782287
23792288 # Brief implementation of recursive pattern matching, when assigning array or
23802289 # object literals to a value. Peeks at their properties to assign inner names.
@@ -2409,12 +2318,19 @@ exports.Assign = class Assign extends Base
24092318
24102319 isSplat = splats ? .length > 0
24112320 isExpans = expans ? .length > 0
2412- isObject = @variable .isObject ()
2413- isArray = @variable .isArray ()
24142321
24152322 vvar = value .compileToFragments o, LEVEL_LIST
24162323 vvarText = fragmentsToText vvar
24172324 assigns = []
2325+ pushAssign = (variable , val ) =>
2326+ assigns .push new Assign (variable, val, null , param : @param , subpattern : yes ).compileToFragments o, LEVEL_LIST
2327+
2328+ if isSplat
2329+ splatVar = objects[splats[0 ]].name .unwrap ()
2330+ if splatVar instanceof Arr or splatVar instanceof Obj
2331+ splatVarRef = new IdentifierLiteral o .scope .freeVariable ' ref'
2332+ objects[splats[0 ]].name = splatVarRef
2333+ splatVarAssign = -> pushAssign new Value (splatVar), splatVarRef
24182334
24192335 # At this point, there are several things to destructure. So the `fn()` in
24202336 # `{a, b} = fn()` must be cached, for example. Make vvar into a simple
@@ -2438,10 +2354,6 @@ exports.Assign = class Assign extends Base
24382354 # Helper which outputs `[].splice` code.
24392355 compSplice = slicer " splice"
24402356
2441- # Check if `objects` array contains object spread (`{a, r...}`), e.g. `[a, b, {c, r...}]`.
2442- hasObjSpreads = (objs ) ->
2443- (i for obj, i in objs when obj .base instanceof Obj and obj .base .hasSplat ())
2444-
24452357 # Check if `objects` array contains any instance of `Assign`, e.g. {a:1}.
24462358 hasObjAssigns = (objs ) ->
24472359 (i for obj, i in objs when obj instanceof Assign and obj .context is ' object' )
@@ -2451,15 +2363,14 @@ exports.Assign = class Assign extends Base
24512363 return yes for obj in objs when not obj .isAssignable ()
24522364 no
24532365
2454- # `objects` are complex when there is object spread ({a...}), object assign ({a:1}),
2366+ # `objects` are complex when there is object assign ({a:1}),
24552367 # unassignable object, or just a single node.
24562368 complexObjects = (objs ) ->
2457- hasObjSpreads (objs). length or hasObjAssigns (objs).length or objIsUnassignable (objs) or olen is 1
2369+ hasObjAssigns (objs).length or objIsUnassignable (objs) or olen is 1
24582370
24592371 # "Complex" `objects` are processed in a loop.
24602372 # Examples: [a, b, {c, r...}, d], [a, ..., {b, r...}, c, d]
24612373 loopObjects = (objs , vvar , vvarTxt ) =>
2462- objSpreads = hasObjSpreads objs
24632374 for obj, i in objs
24642375 # `Elision` can be skipped.
24652376 continue if obj instanceof Elision
@@ -2478,20 +2389,19 @@ exports.Assign = class Assign extends Base
24782389 # `obj` is [a...], {a...} or a
24792390 vvar = switch
24802391 when obj instanceof Splat then new Value obj .name
2481- when i in objSpreads then new Value obj .base
24822392 else obj
24832393 vval = switch
24842394 when obj instanceof Splat then compSlice (vvarTxt, i)
24852395 else new Value new Literal (vvarTxt), [new Index new NumberLiteral i]
24862396 message = isUnassignable vvar .unwrap ().value
24872397 vvar .error message if message
2488- assigns . push new Assign ( vvar, vval, null , param : @param , subpattern : yes ). compileToFragments o, LEVEL_LIST
2398+ pushAssign vvar, vval
24892399
24902400 # "Simple" `objects` can be split and compiled to arrays, [a, b, c] = arr, [a, b, c...] = arr
24912401 assignObjects = (objs , vvar , vvarTxt ) =>
24922402 vvar = new Value new Arr (objs, yes )
24932403 vval = if vvarTxt instanceof Value then vvarTxt else new Value new Literal (vvarTxt)
2494- assigns . push new Assign ( vvar, vval, null , param : @param , subpattern : yes ). compileToFragments o, LEVEL_LIST
2404+ pushAssign vvar, vval
24952405
24962406 processObjects = (objs , vvar , vvarTxt ) ->
24972407 if complexObjects objs
@@ -2528,6 +2438,7 @@ exports.Assign = class Assign extends Base
25282438 else
25292439 # There is no `Splat` or `Expansion` in `objects`.
25302440 processObjects objects, vvar, vvarText
2441+ splatVarAssign? ()
25312442 assigns .push vvar unless top or @subpattern
25322443 fragments = @ joinFragmentArrays assigns, ' , '
25332444 if o .level < LEVEL_LIST then fragments else @ wrapInParentheses fragments
@@ -2689,7 +2600,7 @@ exports.Code = class Code extends Base
26892600 param .error ' an expansion parameter cannot be the only parameter in a function definition'
26902601 haveSplatParam = yes
26912602 if param .splat
2692- if param .name instanceof Arr
2603+ if param .name instanceof Arr or param . name instanceof Obj
26932604 # Splat arrays are treated oddly by ES; deal with them the legacy
26942605 # way in the function body. TODO: Should this be handled in the
26952606 # function parameter list, and if so, how?
@@ -2743,17 +2654,7 @@ exports.Code = class Code extends Base
27432654 if param .name instanceof Arr or param .name instanceof Obj
27442655 # This parameter is destructured.
27452656 param .name .lhs = yes
2746- # Compile `foo({a, b...}) ->` to `foo(arg) -> {a, b...} = arg`.
2747- # Can be removed once ES proposal hits Stage 4.
2748- if param .name instanceof Obj and param .name .hasSplat ()
2749- splatParamName = o .scope .freeVariable ' arg'
2750- o .scope .parameter splatParamName
2751- ref = new Value new IdentifierLiteral splatParamName
2752- exprs .push new Assign new Value (param .name ), ref, null , param : ' alwaysDeclare'
2753- # Compile `foo({a, b...} = {}) ->` to `foo(arg = {}) -> {a, b...} = arg`.
2754- if param .value ? and not param .assignedInBody
2755- ref = new Assign ref, param .value , null , param : yes
2756- else unless param .shouldCache ()
2657+ unless param .shouldCache ()
27572658 param .name .eachName (prop) ->
27582659 o .scope .parameter prop .value
27592660 else
@@ -3028,7 +2929,10 @@ exports.Splat = class Splat extends Base
30282929
30292930 children : [' name' ]
30302931
2932+ shouldCache : -> no
2933+
30312934 isAssignable : ->
2935+ return no if @name instanceof Obj or @name instanceof Parens
30322936 @name .isAssignable () and (not @name .isAtomic or @name .isAtomic ())
30332937
30342938 assigns : (name ) ->
@@ -3883,33 +3787,14 @@ exports.If = class If extends Base
38833787
38843788UTILITIES =
38853789 modulo : -> ' function(a, b) { return (+a % (b = +b) + b) % b; }'
3886- objectWithoutKeys : -> "
3887- function(o, ks) {
3888- var res = {};
3889- for (var k in o) ([].indexOf.call(ks, k) < 0 && {}.hasOwnProperty.call(o, k)) && (res[k] = o[k]);
3890- return res;
3891- }
3892- "
3790+
38933791 boundMethodCheck : -> "
38943792 function(instance, Constructor) {
38953793 if (!(instance instanceof Constructor)) {
38963794 throw new Error('Bound instance method accessed before binding');
38973795 }
38983796 }
38993797 "
3900- _extends : -> "
3901- Object.assign || function (target) {
3902- for (var i = 1; i < arguments.length; i++) {
3903- var source = arguments[i];
3904- for (var key in source) {
3905- if (Object.prototype.hasOwnProperty.call(source, key)) {
3906- target[key] = source[key];
3907- }
3908- }
3909- }
3910- return target;
3911- }
3912- "
39133798
39143799 # Shortcuts to speed up the lookup time for native functions.
39153800 hasProp : -> ' {}.hasOwnProperty'
0 commit comments