@@ -343,6 +343,101 @@ def visit_array(node)
343
343
end
344
344
end
345
345
346
+ def visit_aryptn ( node )
347
+ match_failures = [ ]
348
+ jumps_to_exit = [ ]
349
+
350
+ # If there's a constant, then check if we match against that constant or
351
+ # not first. Branch to failure if we don't.
352
+ if node . constant
353
+ iseq . dup
354
+ visit ( node . constant )
355
+ iseq . checkmatch ( YARV ::VM_CHECKMATCH_TYPE_CASE )
356
+ match_failures << iseq . branchunless ( -1 )
357
+ end
358
+
359
+ # First, check if the #deconstruct cache is nil. If it is, we're going to
360
+ # call #deconstruct on the object and cache the result.
361
+ iseq . topn ( 2 )
362
+ branchnil = iseq . branchnil ( -1 )
363
+
364
+ # Next, ensure that the cached value was cached correctly, otherwise fail
365
+ # the match.
366
+ iseq . topn ( 2 )
367
+ match_failures << iseq . branchunless ( -1 )
368
+
369
+ # Since we have a valid cached value, we can skip past the part where we
370
+ # call #deconstruct on the object.
371
+ iseq . pop
372
+ iseq . topn ( 1 )
373
+ jump = iseq . jump ( -1 )
374
+
375
+ # Check if the object responds to #deconstruct, fail the match otherwise.
376
+ branchnil . patch! ( iseq )
377
+ iseq . dup
378
+ iseq . putobject ( :deconstruct )
379
+ iseq . send ( :respond_to? , 1 )
380
+ iseq . setn ( 3 )
381
+ match_failures << iseq . branchunless ( -1 )
382
+
383
+ # Call #deconstruct and ensure that it's an array, raise an error
384
+ # otherwise.
385
+ iseq . send ( :deconstruct , 0 )
386
+ iseq . setn ( 2 )
387
+ iseq . dup
388
+ iseq . checktype ( YARV ::VM_CHECKTYPE_ARRAY )
389
+ match_error = iseq . branchunless ( -1 )
390
+
391
+ # Ensure that the deconstructed array has the correct size, fail the match
392
+ # otherwise.
393
+ jump [ 1 ] = iseq . label
394
+ iseq . dup
395
+ iseq . send ( :length , 0 )
396
+ iseq . putobject ( node . requireds . length )
397
+ iseq . send ( :== , 1 )
398
+ match_failures << iseq . branchunless ( -1 )
399
+
400
+ # For each required element, check if the deconstructed array contains the
401
+ # element, otherwise jump out to the top-level match failure.
402
+ iseq . dup
403
+ node . requireds . each_with_index do |required , index |
404
+ iseq . putobject ( index )
405
+ iseq . send ( :[] , 1 )
406
+
407
+ case required
408
+ when VarField
409
+ lookup = visit ( required )
410
+ iseq . setlocal ( lookup . index , lookup . level )
411
+ else
412
+ visit ( required )
413
+ iseq . checkmatch ( YARV ::VM_CHECKMATCH_TYPE_CASE )
414
+ match_failures << iseq . branchunless ( -1 )
415
+ end
416
+
417
+ if index < node . requireds . length - 1
418
+ iseq . dup
419
+ else
420
+ iseq . pop
421
+ jumps_to_exit << iseq . jump ( -1 )
422
+ end
423
+ end
424
+
425
+ # Set up the routine here to raise an error to indicate that the type of
426
+ # the deconstructed array was incorrect.
427
+ match_error . patch! ( iseq )
428
+ iseq . putspecialobject ( YARV ::VM_SPECIAL_OBJECT_VMCORE )
429
+ iseq . putobject ( TypeError )
430
+ iseq . putobject ( "deconstruct must return Array" )
431
+ iseq . send ( :"core#raise" , 2 )
432
+ iseq . pop
433
+
434
+ # Patch all of the match failures to jump here so that we pop a final
435
+ # value before returning to the parent node.
436
+ match_failures . each { |match_failure | match_failure . patch! ( iseq ) }
437
+ iseq . pop
438
+ jumps_to_exit
439
+ end
440
+
346
441
def visit_assign ( node )
347
442
case node . target
348
443
when ARefField
@@ -1298,6 +1393,33 @@ def visit_range(node)
1298
1393
end
1299
1394
end
1300
1395
1396
+ def visit_rassign ( node )
1397
+ if node . operator . is_a? ( Kw )
1398
+ iseq . putnil
1399
+ visit ( node . value )
1400
+ iseq . dup
1401
+ jumps = [ ]
1402
+
1403
+ case node . pattern
1404
+ when VarField
1405
+ lookup = visit ( node . pattern )
1406
+ iseq . setlocal ( lookup . index , lookup . level )
1407
+ jumps << iseq . jump ( -1 )
1408
+ else
1409
+ jumps . concat ( visit ( node . pattern ) )
1410
+ end
1411
+
1412
+ iseq . pop
1413
+ iseq . pop
1414
+ iseq . putobject ( false )
1415
+ iseq . leave
1416
+
1417
+ jumps . each { |jump | jump [ 1 ] = iseq . label }
1418
+ iseq . adjuststack ( 2 )
1419
+ iseq . putobject ( true )
1420
+ end
1421
+ end
1422
+
1301
1423
def visit_rational ( node )
1302
1424
iseq . putobject ( node . accept ( RubyVisitor . new ) )
1303
1425
end
0 commit comments