UNPKG

9.73 kBJavaScriptView Raw
1"use strict"
2
3const skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g
4
5const acorn = require("acorn")
6const tt = acorn.tokTypes
7const TokenType = acorn.TokenType
8
9function 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
18function 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
27const privateNameToken = new TokenType("privateName")
28
29module.exports = function(Parser) {
30 return class extends Parser {
31 // Parse # token
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 // Manage stacks and check for undeclared private static names
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 // Parse private fields
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 // Special-case for `async`, since `parseClassMember` currently looks
88 // for `(` to determine whether `async` is a method name
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) { // Don't use object identity for interop with private-methods
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 // Parse public static fields
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 // Parse private element access
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) { // Don't use object identity for interop with private-methods
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 // Prohibit delete of private class elements
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 // Prohibit arguments in class field initializers
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 // Prohibit super in class field initializers
216 // Prohibit direct super in private methods
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}