Typically, arrays in javascript are extensible, but this is not true for the array passed as the first argument of a tag function:
let ary = [1,2,3];
console.log(Object.isExtensible(ary));
// returns true
function tag(ary, ...expressionResults)
{
console.log(Array.isArray(ary));
//returns true
console.log(Object.isExtensible(ary));
// returns false
}
tag`test`;
Where, exactly, in the specification, is this array deemed non-extensible? I'm not even sure if I'm looking at the right spot.
You were looking in the right spot. The linked spec even offers a note as to why (emp. mine):
NOTE 2 Each TemplateLiteral in the program code of a realm is associated with a unique template object that is used in the evaluation of tagged Templates (12.2.9.6). The template objects are frozen and the same template object is used each time a specific tagged Template is evaluated.
If you want to understand the actual execution, first look at the tagged templates' runtime semantics are specified in Section 12.3.7.1:
12.3.7.1 Runtime Semantics: Evaluation
MemberExpression: MemberExpression TemplateLiteral
[…]
- Return ? EvaluateCall(tagFunc, tagRef, TemplateLiteral, tailCall).
If you take a look at abstract operation EvaluateCall:
12.3.4.2 Runtime Semantics: EvaluateCall (func, ref, arguments, tailPosition)
[…]
- Let argList be ArgumentListEvaluation of arguments.
So, when calling a tag function with a template literal, the ArgumentListEvaluation of the TemplateLiteral is passed as the argument to the tag function. Taking a look at ArgumentListEvaluation:
12.2.9.3 Runtime Semantics: ArgumentListEvaluation
TemplateLiteral: NoSubstitutionTemplate
[…]
- Let siteObj be GetTemplateObject(templateLiteral).
Looking at operation GetTemplateObject, we see the culprit:
12.2.9.4 Runtime Semantics: GetTemplateObject (templateLiteral)
[…]
- Perform SetIntegrityLevel(template, "frozen").
Where template is array passed to the tag function. We see that it's explicitly frozen. If you want to go a level deeper, see SetIntegrityLevel:
7.3.14 SetIntegrityLevel (O, level)
The abstract operation SetIntegrityLevel is used to fix the set of own properties of an object. This abstract operation performs the following steps:
[…]
- Let status be ? O.[[PreventExtensions]]().
And taking a look at [[PreventExtensions]] of an ordinary object, we see that operation OrdinaryPreventExtensions is called:
9.1.4.1 OrdinaryPreventExtensions (O)
When the abstract operation OrdinaryPreventExtensions is called with Object O, the following steps are taken:
- Set O.[[Extensible]] to false.
- Return true.
So the [[Extensible]] internal slot is explicitly set to false.