The "arguments" object is created for any code that's function code. Except when there's already a binding for "arguments", or one of the formal parameters or variable declarations in the function code has such a binding.
The function below is called with the following parameters:
- func: the function who's code is being evaluated right now
- names: a List of the function's formal parameter names
- args: the values passed on to [[Call]]
- env: the variable environment for this function code
- strict: whether this code is strict mode code
function CreateArgumentsObject(func, names, args, env, strict) {
var len = args.length;
var obj = {};
// this is basically done by creating a new object:
// "set all of the internal methods of obj as specified in 8.12"
obj.[[Class]] = "Arguments";
var Object = global.Object;
obj.[[Prototype]] = global.Object.prototype; // correct?
obj.[[DefineOwnProperty]]("length", PD{[[Value]]:len, [[Writable]]:true, [[Enumerable]]:false, [[Configurable]]: true}, false);
var map = {};
var mappedNames = new List;
var indx = len - 1;
var val, name, g, p;
while (indx >= 0) {
val = args[indx];
obj.[[DefineOwnProperty]](ToString(indx), PD{[[Value]]:val, [[Writable]]:true, [[Enumerable]]:true, [[Configurable]]:true}, false);
if (indx < names.length-1) {
name = names[indx];
g = MakeArgGetter(name, env);
p = MakeArgSetter(name, env);
map.[[DefineOwnProperty]](ToString(indx), PD{[[Set]]:p, [[Get]]:g, [[Configurable]]:true}, false);
}
indx = indx - 1;
}
if (mappedNames.length) {
// Now set up special internal methods and back up the original ones.
// The alternative functions are defined below.
obj.[[ParameterMap]] = map;
obj.[[DefaultGet]] = obj.[[Get]];
obj.[[Get]] = ArgumentsGet;
obj.[[DefaultGetOwnProperty]] = obj.[[GetOwnProperty]];
obj.[[GetOwnProperty]] = ArgumentsGetOwnProperty;
obj.[[DefaultDefineOwnProperty]] = obj.[[DefineOwnProperty]];
obj.[[DefineOwnProperty]] = ArgumentsDefineOwnProperty;
obj.[[DefaultDelete]] = obj.[[Delete]];
obj.[[Delete]] = ArgumentsDelete;
}
if (strict) {
var thrower = [[ThrowTypeError]]; // "the [[ThrowTypeError]] function object"
obj.[[DefineOwnProperty]]("caller", PD{[[Get]]:thrower, [[Set]]:thrower, [[Enumerable]]:false, [[Configurable]]:false}, false);
obj.[[DefineOwnProperty]]("callee", PD{[[Get]]:thrower, [[Set]]:thrower, [[Enumerable]]:false, [[Configurable]]:false}, false);
} else {
obj.[[DefineOwnProperty]]("callee", PD{[[Value]]:func, [[Writable]]:true, [[Enumerable]]:false, [[Configurable]]:true}, false);
}
return obj;
}
And here the extra methods defined in 10.6 for the above function:
function MakeArgGetter(name, env) {
var body = "return "+name+";";
// "return the rsult of creating a function object as described in 13.2 using no FormalParameterList, body for FunctionBody, env as scope and true for strict"
// todo... (need to define a meta way of creating such an object)
}
function MakeArgSetter(name, env) {
var param = name+"_arg";
var body = name+" = "+param;
//todo: return stuff (13.2)
}
// the [[Get]] of an arguments object for a non-strict function with formal parameters when called with a property P
// so there's a function (F), for which "this" is the arguments object, which has a special [[Get]] function, which we define here :)
function ArgumentsGet(P) {
var map = this.[[ParameterMap]];
var isMapped = map.[[GetOwnProperty]](P);
if (isMapped === undefined) {
var v = this.[[DefaultGet]](P);
if (P == "caller" && v instanceof Function && v.strict) throw TypeError;
return v;
}
// map has mapping for P
return map.[[Get]](P);
}
// if this arguments object has no property P, return undefined. otherwise (and only then) check whether the map has a binding for P.
function ArgumentsGetOwnProperty(P) {
var desc = this.[[DefaultGetOwnProperty]](P);
if (desc === undefined) return desc;
var map = this.[[ParameterMap]];
var isMapped = map.[[GetOwnProperty]](P);
if (isMapped !== undefined) desc.[[Value]] = map.[[Get]](P);
return desc;
}
function ArgumentsDefineOwnProperty(P, Desc, Throw) {
var map = this.[[ParameterMap]];
var isMapped = map.[[GetOwnProperty]](P);
var allowed = map.[[DefineOwnProperty]](P, Desc, false);
if (!allowed) {
if (Throw) throw TypeError;
return false;
}
// this happens after the (successfull) assignment...
// remove the property from the map, if exists
if (isMapped !== undefined) {
if (IsAccessorDescriptor(Desc)) {
map.[[Delete]](P, false);
} else {
if ([[Value]] in Desc) map.[[Put]](P, Desc.[[Value]], Throw);
if ([[Writable]] in Desc && !Desc.[[Writable]]) map.[[Delete]](P, false);
}
}
return true;
}
function ArgumentsDelete(P, Throw) {
var map = this.[[ParameterMap]];
var isMapped = map.[[GetOwnProperty]](P);
var result = this.[[DefaultDelete]](P, Throw);
// remove from mapping, but only if it was actually deleted
if (result && isMapped !== undefined) map.[[Delete]](P, false);
return result;
}
Note: for non-strict mode functions, the formal parameters are linked to the arguments object. Changing the value of a parameter changes the value of the arguments as well, and vice versa. This link is removed if you delete the property and add it again or when the variable is changed into an accessor type. This does not hold for strict mode, where the arguments object simply holds a copy of the value.
Note: The ParameterMap is merely an internal type and is not directly accessible from Ecmascript. It might not even actually exist.
Note: The properties "caller" and "callee" of the arguments object in strict mode will throw a TypeError when accessed. Note that only "callee" is defined in non-strict code while the "caller" property is implemented in some implementations (like most browsers).