* Compile a node into a JavaScript function. * This basically pre-calculates as much as possible and only leaves open * calculations which depend on a dynamic scope with variables. * @param {Object} math Math.js namespace with functions and constants. * @param {Object} argNam
(math, argNames)
| 140 | * evalNode(scope: Object, args: Object, context: *) |
| 141 | */ |
| 142 | _compile (math, argNames) { |
| 143 | // compile arguments |
| 144 | const evalArgs = this.args.map((arg) => arg._compile(math, argNames)) |
| 145 | const fromOptionalChaining = this.optional || |
| 146 | (isAccessorNode(this.fn) && this.fn.optionalChaining) |
| 147 | |
| 148 | if (isSymbolNode(this.fn)) { |
| 149 | const name = this.fn.name |
| 150 | if (!argNames[name]) { |
| 151 | // we can statically determine whether the function |
| 152 | // has the rawArgs property |
| 153 | const fn = name in math ? getSafeProperty(math, name) : undefined |
| 154 | const isRaw = typeof fn === 'function' && fn.rawArgs === true |
| 155 | |
| 156 | const resolveFn = (scope) => { |
| 157 | let value |
| 158 | if (scope.has(name)) { |
| 159 | value = scope.get(name) |
| 160 | } else if (name in math) { |
| 161 | value = getSafeProperty(math, name) |
| 162 | } else if (fromOptionalChaining) value = undefined |
| 163 | else return FunctionNode.onUndefinedFunction(name) |
| 164 | |
| 165 | if (typeof value === 'function' || |
| 166 | (fromOptionalChaining && value === undefined)) { |
| 167 | return value |
| 168 | } |
| 169 | |
| 170 | throw new TypeError( |
| 171 | `'${name}' is not a function; its value is:\n ${strin(value)}` |
| 172 | ) |
| 173 | } |
| 174 | |
| 175 | if (isRaw) { |
| 176 | // pass unevaluated parameters (nodes) to the function |
| 177 | // "raw" evaluation |
| 178 | const rawArgs = this.args |
| 179 | return function evalFunctionNode (scope, args, context) { |
| 180 | const fn = resolveFn(scope) |
| 181 | |
| 182 | // the original function can be overwritten in the scope with a non-rawArgs function |
| 183 | if (fn.rawArgs === true) { |
| 184 | return fn(rawArgs, math, createSubScope(scope, args)) |
| 185 | } else { |
| 186 | // "regular" evaluation |
| 187 | const values = evalArgs.map((evalArg) => evalArg(scope, args, context)) |
| 188 | return fn(...values) |
| 189 | } |
| 190 | } |
| 191 | } else { |
| 192 | // "regular" evaluation |
| 193 | switch (evalArgs.length) { |
| 194 | case 0: return function evalFunctionNode (scope, args, context) { |
| 195 | const fn = resolveFn(scope) |
| 196 | if (fromOptionalChaining && fn === undefined) return undefined |
| 197 | return fn() |
| 198 | } |
| 199 | case 1: return function evalFunctionNode (scope, args, context) { |
nothing calls this directly
no test coverage detected