I'm just coming back from having been away from Perl for awhile, and this was a very challenging problem that allowed me to learn some useful stuff -- so thank you very much for posting the question.
It's helpful/important to point out that the wide-character escape form that you have there is based on UTF-16BE ("big-endian", aka high-byte-first) -- this is also known (as mentioned in the manual for the pack function) as "network" order.
I found that the following approach seems to work; note that I'm looping over the surrogate pairs first, then looping over the "normal" characters (the ones with code points less than 0x10000) -- and I print out the results at each iteration to see what's going on.
#!/usr/bin/perl -CS
use strict;
use warnings;
use Encode qw(decode);
$_ = <DATA>;
print;
while (/(\\u(d8[0-9a-f]{2})\\u(d[c-f][0-9a-f]{2}))/ ) { ## NB: Match
+only surrogate pairs
my $rplc = $1;
my $sp = pack( "nn", hex($2), hex($3) );
s/\Q$rplc/decode( "UTF-16BE", $sp )/e;
print;
}
while (/(\\u([0-9a-f]{4}))/ ) {
my $rplc = $1;
my $cp = pack( "n", hex($2) );
s/\Q$rplc/decode( "UTF-16BE", $cp )/e;
print;
}
__DATA__
Ren\u00e9 \ud83d\ude06 Fran\u00e7oise
Output:
Ren\u00e9 \ud83d\ude06 Fran\u00e7oise
Ren\u00e9 😆 Fran\u00e7oise
René 😆 Fran\u00e7oise
René 😆 Françoise
IMPORTANT UPDATE: I altered the initial regex above -- the one for matching surrogate-pair escape sequences -- to ensure that the two escapes both start with the hex-digit "d" start with "d8" (for the first surrogate) and "d[c-f]" (for the second surrogate); this avoids misfiring on cases where two non-surrogate characters happen to appear next to each other in the data.