1 | ;
|
2 |
|
3 | // Per https://github.com/es-shims/es-shim-api :
|
4 | // In every way possible, the package must attempt to make itself
|
5 | // robust against the environment being modified after it is required.
|
6 | const { prototype } = Function;
|
7 | const { toString } = prototype;
|
8 | const { apply } = Reflect;
|
9 | const { defineProperty, freeze, hasOwnProperty } = Object;
|
10 |
|
11 | const tokenize = require('./lib/quick-and-dirty-lexer.js');
|
12 |
|
13 | const memoTable = new WeakMap();
|
14 |
|
15 | // eslint-disable-next-line complexity
|
16 | function arityOf(fun) {
|
17 | if (memoTable.has(fun)) {
|
18 | return memoTable.get(fun);
|
19 | }
|
20 | const str = apply(toString, fun, []);
|
21 |
|
22 | // Between the parentheses around the formals.
|
23 | let inFormalParameterList = false;
|
24 | // Count of open brackets ('[', '(', '{') that have been seen without a corresponding
|
25 | // close bracket.
|
26 | let bracketDepth = 0;
|
27 | // Number of formal parameters seen so far. This includes any rest param.
|
28 | let declaredParameterCount = 0;
|
29 | // True if the formal parameter list has a ... pattern.
|
30 | let hasRestParam = false;
|
31 |
|
32 | // Action to apply to the next token processed. This allows equiv of LA(1)
|
33 | // without implementing pushback.
|
34 | let onNext = null; // Special action for next token.
|
35 |
|
36 | tokenLoop:
|
37 | for (const tok of tokenize(str)) {
|
38 | if (onNext) {
|
39 | const onTok = onNext;
|
40 | onNext = null;
|
41 | onTok(tok);
|
42 | }
|
43 | switch (tok) {
|
44 | case '(':
|
45 | if (!bracketDepth) {
|
46 | inFormalParameterList = true;
|
47 | onNext = function countFirstParam(next) { // eslint-disable-line
|
48 | if (next !== ')') {
|
49 | declaredParameterCount = 1;
|
50 | }
|
51 | };
|
52 | }
|
53 | // fallthrough
|
54 | case '{':
|
55 | case '[':
|
56 | // This works even if str has a dynamic name like
|
57 | // [nameExpression](formal,parameter,list) {body}
|
58 | ++bracketDepth;
|
59 | break;
|
60 | case ',':
|
61 | if (inFormalParameterList && bracketDepth === 1) {
|
62 | ++declaredParameterCount;
|
63 | }
|
64 | break;
|
65 | case '...':
|
66 | if (inFormalParameterList && bracketDepth === 1) {
|
67 | hasRestParam = true;
|
68 | }
|
69 | break;
|
70 | case ')':
|
71 | if (inFormalParameterList && bracketDepth === 1) {
|
72 | break tokenLoop;
|
73 | }
|
74 | // fallthrough
|
75 | case '}': case ']':
|
76 | if (!bracketDepth) {
|
77 | throw new Error(str);
|
78 | }
|
79 | --bracketDepth;
|
80 | break;
|
81 | default:
|
82 | break;
|
83 | }
|
84 | }
|
85 | const record = freeze({
|
86 | __proto__: null,
|
87 | max: declaredParameterCount,
|
88 | usesRest: hasRestParam,
|
89 | });
|
90 | memoTable.set(fun, record);
|
91 | return record;
|
92 | }
|
93 |
|
94 | module.exports = arityOf;
|
95 |
|
96 | // Per https://github.com/es-shims/es-shim-api :
|
97 |
|
98 | // require('foo').implementation or require('foo/implementation') is a
|
99 | // spec-compliant JS function, that will depend on a receiver
|
100 | // (a “this” value) as the spec requires.
|
101 | arityOf.implementation = arityOf;
|
102 |
|
103 | // require('foo').getPolyfill or require('foo/polyfill') is a function
|
104 | // that when invoked, will return the most compliant and performant
|
105 | // function that it can - if a native version is available, and does
|
106 | // not violate the spec, then the native function will be returned -
|
107 | // otherwise, either the implementation, or a custom, wrapped version
|
108 | // of the native function, will be returned. This is also the result
|
109 | // that will be used as the default export.
|
110 | arityOf.getPolyfill = function getPolyfill() {
|
111 | // TODO: look for native
|
112 | return arityOf;
|
113 | };
|
114 |
|
115 | // require('foo').shim or require('foo/shim') is a function that when
|
116 | // invoked, will call getPolyfill, and if the polyfill doesn’t match
|
117 | // the built-in value, will install it into the global environment.
|
118 | //
|
119 | // The only place the package may modify the environment is within its
|
120 | // shim method.
|
121 | arityOf.shim = function shim() {
|
122 | if (!hasOwnProperty(prototype, 'maxArity')) {
|
123 | defineProperty(
|
124 | prototype,
|
125 | 'maxArity',
|
126 | {
|
127 | configurable: true,
|
128 | get() {
|
129 | return arityOf(this).max;
|
130 | },
|
131 | });
|
132 | }
|
133 | if (!hasOwnProperty(prototype, 'usesRest')) {
|
134 | defineProperty(
|
135 | prototype,
|
136 | 'usesRest',
|
137 | {
|
138 | configurable: true,
|
139 | get() {
|
140 | return arityOf(this).usesRest;
|
141 | },
|
142 | });
|
143 | }
|
144 | };
|