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 | function maybeCountParam(next) {
|
37 | if (next !== ')') {
|
38 | ++declaredParameterCount;
|
39 | }
|
40 | }
|
41 |
|
42 | tokenLoop:
|
43 | for (const tok of tokenize(str)) {
|
44 | if (onNext) {
|
45 | const onTok = onNext;
|
46 | onNext = null;
|
47 | onTok(tok);
|
48 | }
|
49 | switch (tok) {
|
50 | case '(':
|
51 | if (!bracketDepth) {
|
52 | inFormalParameterList = true;
|
53 | onNext = maybeCountParam;
|
54 | }
|
55 | // fallthrough
|
56 | case '{':
|
57 | case '[':
|
58 | // This works even if str has a dynamic name like
|
59 | // [nameExpression](formal,parameter,list) {body}
|
60 | ++bracketDepth;
|
61 | break;
|
62 | case ',':
|
63 | if (inFormalParameterList && bracketDepth === 1) {
|
64 | onNext = maybeCountParam;
|
65 | }
|
66 | break;
|
67 | case '...':
|
68 | if (inFormalParameterList && bracketDepth === 1) {
|
69 | hasRestParam = true;
|
70 | }
|
71 | break;
|
72 | case ')':
|
73 | if (inFormalParameterList && bracketDepth === 1) {
|
74 | break tokenLoop;
|
75 | }
|
76 | // fallthrough
|
77 | case '}': case ']':
|
78 | if (!bracketDepth) {
|
79 | throw new Error(str);
|
80 | }
|
81 | --bracketDepth;
|
82 | break;
|
83 | default:
|
84 | break;
|
85 | }
|
86 | }
|
87 | const record = freeze({
|
88 | __proto__: null,
|
89 | max: declaredParameterCount,
|
90 | usesRest: hasRestParam,
|
91 | });
|
92 | memoTable.set(fun, record);
|
93 | return record;
|
94 | }
|
95 |
|
96 | module.exports = arityOf;
|
97 |
|
98 | // Per https://github.com/es-shims/es-shim-api :
|
99 |
|
100 | // require('foo').implementation or require('foo/implementation') is a
|
101 | // spec-compliant JS function, that will depend on a receiver
|
102 | // (a “this” value) as the spec requires.
|
103 | arityOf.implementation = arityOf;
|
104 |
|
105 | // require('foo').getPolyfill or require('foo/polyfill') is a function
|
106 | // that when invoked, will return the most compliant and performant
|
107 | // function that it can - if a native version is available, and does
|
108 | // not violate the spec, then the native function will be returned -
|
109 | // otherwise, either the implementation, or a custom, wrapped version
|
110 | // of the native function, will be returned. This is also the result
|
111 | // that will be used as the default export.
|
112 | arityOf.getPolyfill = function getPolyfill() {
|
113 | // TODO: look for native
|
114 | return arityOf;
|
115 | };
|
116 |
|
117 | // require('foo').shim or require('foo/shim') is a function that when
|
118 | // invoked, will call getPolyfill, and if the polyfill doesn’t match
|
119 | // the built-in value, will install it into the global environment.
|
120 | //
|
121 | // The only place the package may modify the environment is within its
|
122 | // shim method.
|
123 | arityOf.shim = function shim() {
|
124 | if (!hasOwnProperty(prototype, 'maxArity')) {
|
125 | defineProperty(
|
126 | prototype,
|
127 | 'maxArity',
|
128 | {
|
129 | configurable: true,
|
130 | get() {
|
131 | return arityOf(this).max;
|
132 | },
|
133 | });
|
134 | }
|
135 | if (!hasOwnProperty(prototype, 'usesRest')) {
|
136 | defineProperty(
|
137 | prototype,
|
138 | 'usesRest',
|
139 | {
|
140 | configurable: true,
|
141 | get() {
|
142 | return arityOf(this).usesRest;
|
143 | },
|
144 | });
|
145 | }
|
146 | };
|