CodeQL queries 1.25
Skip to end of metadata
Go to start of metadata

Name: Parameter reassigned in function that uses arguments

Description: A function that reassigns one of its parameters and also uses the arguments object may not be optimized properly.

ID: js/parameter-reassignment-with-arguments

Kind: problem

Severity: recommendation

Precision: medium

Query: ReassignParameterAndUseArguments.ql
/**
 * @name Parameter reassigned in function that uses arguments
 * @description A function that reassigns one of its parameters and also uses the arguments object
 *              may not be optimized properly.
 * @kind problem
 * @problem.severity recommendation
 * @id js/parameter-reassignment-with-arguments
 * @tags efficiency
 *       maintainability
 * @precision medium
 */

import javascript

from Function f, SimpleParameter p, VarAccess assgn
where
  p = f.getAParameter() and
  f.usesArgumentsObject() and
  assgn = p.getVariable().getAnAccess() and
  assgn.isLValue()
select p,
  "This parameter is reassigned $@, " +
    "which may prevent optimization because the surrounding function " +
    "uses the arguments object.", assgn, "here"

A function that reassigns one of its parameters and also uses the special arguments object may not be optimized properly, which could make the program run slower. In particular, the V8 engine (which is used by the Google Chrome browser and the Node.js platform) does not currently perform any advanced optimizations on such functions.

Recommendation

Either eliminate all uses of the arguments variable, or copy the parameter into a local variable and reassign that local variable instead.

Example

In the following example, function sum takes two arguments xs and start, and returns the sum of all elements in the array xs, plus the value of start. The argument start is optional and defaults to 0.

function sum(xs, start) {
    if (arguments.length < 2)
        start = 0;

    var sum = start;
    for (var i=0; i<xs.length; ++i)
        sum += xs[i];

    return sum;
}

Observe that to find out whether the argument was provided, sum checks arguments.length (which evaluates to the number of actual arguments passed to sum), and reassigns sum to 0 if fewer than two arguments were passed.

In this example, it is easy to eliminate the use of the arguments variable: instead of checking arguments.length, we can instead check whether sum is undefined:

function sum(xs, start) {
    if (typeof start === 'undefined')
        start = 0;

    var sum = start;
    for (var i=0; i<xs.length; ++i)
        sum += xs[i];

    return sum;
}

A more general solution is to introduce a new local variable, assign the initial value of sum to this local variable, and then update and use the local variable instead of the parameter:

function sum(xs, _start) {
    var start = arguments.length < 2 ? 0 : _start;

    var sum = start;
    for (var i=0; i<xs.length; ++i)
        sum += xs[i];

    return sum;
}

References