1 | "use strict"
|
2 |
|
3 | const skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g
|
4 |
|
5 | const acorn = require("acorn")
|
6 | const tt = acorn.tokTypes
|
7 | const TokenType = acorn.TokenType
|
8 |
|
9 | function maybeParseFieldValue(field) {
|
10 | if (this.eat(tt.eq)) {
|
11 | const oldInFieldValue = this._inStaticFieldValue
|
12 | this._inStaticFieldValue = true
|
13 | field.value = this.parseExpression()
|
14 | this._inStaticFieldValue = oldInFieldValue
|
15 | } else field.value = null
|
16 | }
|
17 |
|
18 | function parsePrivateName() {
|
19 | const node = this.startNode()
|
20 | node.name = this.value
|
21 | this.next()
|
22 | this.finishNode(node, "PrivateName")
|
23 | if (this.options.allowReserved == "never") this.checkUnreserved(node)
|
24 | return node
|
25 | }
|
26 |
|
27 | const privateNameToken = new TokenType("privateName")
|
28 |
|
29 | module.exports = function(Parser) {
|
30 | return class extends Parser {
|
31 |
|
32 | getTokenFromCode(code) {
|
33 | if (code === 35) {
|
34 | ++this.pos
|
35 | const word = this.readWord1()
|
36 | return this.finishToken(privateNameToken, word)
|
37 | }
|
38 | return super.getTokenFromCode(code)
|
39 | }
|
40 |
|
41 |
|
42 | parseClass(node, isStatement) {
|
43 | this._privateBoundNamesStack = this._privateBoundNamesStack || []
|
44 | const privateBoundNames = Object.create(this._privateBoundNamesStack[this._privateBoundNamesStack.length - 1] || null)
|
45 | this._privateBoundNamesStack.push(privateBoundNames)
|
46 | this._unresolvedPrivateNamesStack = this._unresolvedPrivateNamesStack || []
|
47 | const unresolvedPrivateNames = Object.create(null)
|
48 | this._unresolvedPrivateNamesStack.push(unresolvedPrivateNames)
|
49 | const _return = super.parseClass(node, isStatement)
|
50 | this._privateBoundNamesStack.pop()
|
51 | this._unresolvedPrivateNamesStack.pop()
|
52 | if (!this._unresolvedPrivateNamesStack.length) {
|
53 | const names = Object.keys(unresolvedPrivateNames)
|
54 | if (names.length) {
|
55 | names.sort((n1, n2) => unresolvedPrivateNames[n1] - unresolvedPrivateNames[n2])
|
56 | this.raise(unresolvedPrivateNames[names[0]], "Usage of undeclared private name")
|
57 | }
|
58 | } else Object.assign(this._unresolvedPrivateNamesStack[this._unresolvedPrivateNamesStack.length - 1], unresolvedPrivateNames)
|
59 | return _return
|
60 | }
|
61 |
|
62 |
|
63 | parseClassElement() {
|
64 | if (this.eat(tt.semi)) return null
|
65 |
|
66 | const node = this.startNode()
|
67 |
|
68 | const tryContextual = (k, noLineBreak) => {
|
69 | if (typeof noLineBreak == 'undefined') noLineBreak = false
|
70 | const start = this.start, startLoc = this.startLoc
|
71 | if (!this.eatContextual(k)) return false
|
72 | if (this.type !== tt.parenL && (!noLineBreak || !this.canInsertSemicolon())) return true
|
73 | if (node.key) this.unexpected()
|
74 | node.computed = false
|
75 | node.key = this.startNodeAt(start, startLoc)
|
76 | node.key.name = k
|
77 | this.finishNode(node.key, "Identifier")
|
78 | return false
|
79 | }
|
80 |
|
81 | node.static = tryContextual("static")
|
82 | if (!node.static) return super.parseClassElement()
|
83 |
|
84 | let isGenerator = this.eat(tt.star)
|
85 | let isAsync = false
|
86 | if (!isGenerator) {
|
87 |
|
88 |
|
89 | if (this.options.ecmaVersion >= 8 && this.isContextual("async")) {
|
90 | skipWhiteSpace.lastIndex = this.pos
|
91 | let skip = skipWhiteSpace.exec(this.input)
|
92 | let next = this.input.charAt(this.pos + skip[0].length)
|
93 | if (next === ";" || next === "=") {
|
94 | node.key = this.parseIdent(true)
|
95 | node.computed = false
|
96 | maybeParseFieldValue.call(this, node)
|
97 | this.finishNode(node, "FieldDefinition")
|
98 | this.semicolon()
|
99 | return node
|
100 | } else if (this.options.ecmaVersion >= 8 && tryContextual("async", true)) {
|
101 | isAsync = true
|
102 | isGenerator = this.options.ecmaVersion >= 9 && this.eat(tt.star)
|
103 | }
|
104 | } else if (tryContextual("get")) {
|
105 | node.kind = "get"
|
106 | } else if (tryContextual("set")) {
|
107 | node.kind = "set"
|
108 | }
|
109 | }
|
110 | if (this.type.label === privateNameToken.label) {
|
111 | node.key = parsePrivateName.call(this)
|
112 | node.computed = false
|
113 | if (node.key.name === "constructor") {
|
114 | this.raise(node.key.start, "Classes may not have a private static property named constructor")
|
115 | }
|
116 |
|
117 | const privateBoundNames = this._privateBoundNamesStack[this._privateBoundNamesStack.length - 1]
|
118 | if (Object.prototype.hasOwnProperty.call(privateBoundNames, node.key.name) && !(node.kind === "get" && privateBoundNames[node.key.name] === "set") && !(node.kind === "set" && privateBoundNames[node.key.name] === "get")) this.raise(node.start, "Duplicate private element")
|
119 | privateBoundNames[node.key.name] = node.kind || true
|
120 |
|
121 | delete this._unresolvedPrivateNamesStack[this._unresolvedPrivateNamesStack.length - 1][node.key.name]
|
122 | if (this.type !== tt.parenL) {
|
123 | if (node.key.name === "prototype") {
|
124 | this.raise(node.key.start, "Classes may not have a private static property named prototype")
|
125 | }
|
126 | maybeParseFieldValue.call(this, node)
|
127 | this.finishNode(node, "FieldDefinition")
|
128 | this.semicolon()
|
129 | return node
|
130 | }
|
131 | } else if (!node.key) {
|
132 | this.parsePropertyName(node)
|
133 | if ((node.key.name || node.key.value) === "prototype" && !node.computed) {
|
134 | this.raise(node.key.start, "Classes may not have a static property named prototype")
|
135 | }
|
136 | }
|
137 | if (!node.kind) node.kind = "method"
|
138 | this.parseClassMethod(node, isGenerator, isAsync)
|
139 | if (!node.kind && (node.key.name || node.key.value) === "constructor" && !node.computed) {
|
140 | this.raise(node.key.start, "Classes may not have a static field named constructor")
|
141 | }
|
142 | if (node.kind === "get" && node.value.params.length !== 0) {
|
143 | this.raiseRecoverable(node.value.start, "getter should have no params")
|
144 | }
|
145 | if (node.kind === "set" && node.value.params.length !== 1) {
|
146 | this.raiseRecoverable(node.value.start, "setter should have exactly one param")
|
147 | }
|
148 | if (node.kind === "set" && node.value.params[0].type === "RestElement") {
|
149 | this.raiseRecoverable(node.value.params[0].start, "Setter cannot use rest params")
|
150 | }
|
151 |
|
152 | return node
|
153 |
|
154 | }
|
155 |
|
156 |
|
157 | parseClassMethod(method, isGenerator, isAsync) {
|
158 | if (isGenerator || isAsync || method.kind != "method" || !method.static || this.options.ecmaVersion < 8 || this.type == tt.parenL) {
|
159 | const oldInPrivateClassMethod = this._inPrivateClassMethod
|
160 | this._inPrivateClassMethod = method.key.type == "PrivateName"
|
161 | const ret = super.parseClassMethod(method, isGenerator, isAsync)
|
162 | this._inPrivateClassMethod = oldInPrivateClassMethod
|
163 | return ret
|
164 | }
|
165 | maybeParseFieldValue.call(this, method)
|
166 | delete method.kind
|
167 | method = this.finishNode(method, "FieldDefinition")
|
168 | this.semicolon()
|
169 | return method
|
170 | }
|
171 |
|
172 |
|
173 | parseSubscripts(base, startPos, startLoc, noCalls) {
|
174 | for (let computed; ;) {
|
175 | if ((computed = this.eat(tt.bracketL)) || this.eat(tt.dot)) {
|
176 | let node = this.startNodeAt(startPos, startLoc)
|
177 | node.object = base
|
178 | if (computed) {
|
179 | node.property = this.parseExpression()
|
180 | } else if (this.type.label === privateNameToken.label) {
|
181 | node.property = parsePrivateName.call(this)
|
182 | if (!this._privateBoundNamesStack.length || !this._privateBoundNamesStack[this._privateBoundNamesStack.length - 1][node.property.name]) {
|
183 | this._unresolvedPrivateNamesStack[this._unresolvedPrivateNamesStack.length - 1][node.property.name] = node.property.start
|
184 | }
|
185 | } else {
|
186 | node.property = this.parseIdent(true)
|
187 | }
|
188 | node.computed = Boolean(computed)
|
189 | if (computed) this.expect(tt.bracketR)
|
190 | base = this.finishNode(node, "MemberExpression")
|
191 | } else {
|
192 | return super.parseSubscripts(base, startPos, startLoc, noCalls)
|
193 | }
|
194 | }
|
195 | }
|
196 |
|
197 |
|
198 | parseMaybeUnary(refDestructuringErrors, sawUnary) {
|
199 | const _return = super.parseMaybeUnary(refDestructuringErrors, sawUnary)
|
200 | if (_return.operator == "delete") {
|
201 | if (_return.argument.type == "MemberExpression" && _return.argument.property.type == "PrivateName") {
|
202 | this.raise(_return.start, "Private elements may not be deleted")
|
203 | }
|
204 | }
|
205 | return _return
|
206 | }
|
207 |
|
208 |
|
209 | parseIdent(liberal, isBinding) {
|
210 | const ident = super.parseIdent(liberal, isBinding)
|
211 | if (this._inStaticFieldValue && ident.name == "arguments") this.raise(ident.start, "A static class field initializer may not contain arguments")
|
212 | return ident
|
213 | }
|
214 |
|
215 |
|
216 |
|
217 | parseExprAtom(refDestructuringErrors) {
|
218 | const atom = super.parseExprAtom(refDestructuringErrors)
|
219 | if (this._inStaticFieldValue && atom.type == "Super") this.raise(atom.start, "A static class field initializer may not contain super")
|
220 | if (this._inPrivateClassMethod && atom.type == "Super" && this.type == tt.parenL) this.raise(atom.start, "A class method that is not a constructor may not contain a direct super")
|
221 | return atom
|
222 | }
|
223 | }
|
224 | }
|