1"use strict" 2 3const acorn = require('internal/deps/acorn/acorn/dist/acorn') 4if (acorn.version.indexOf("6.") != 0 && acorn.version.indexOf("6.0.") == 0 && acorn.version.indexOf("7.") != 0) { 5 throw new Error(`acorn-private-class-elements requires acorn@^6.1.0 or acorn@7.0.0, not ${acorn.version}`) 6} 7const tt = acorn.tokTypes 8const TokenType = acorn.TokenType 9 10module.exports = function(Parser) { 11 // Only load this plugin once. 12 if (Parser.prototype.parsePrivateName) { 13 return Parser 14 } 15 16 // Make sure `Parser` comes from the same acorn as our `tt`, 17 // otherwise the comparisons fail. 18 let cur = Parser 19 while (cur && cur !== acorn.Parser) { 20 cur = cur.__proto__ 21 } 22 if (cur !== acorn.Parser) { 23 throw new Error("acorn-private-class-elements does not support mixing different acorn copies") 24 } 25 26 Parser = class extends Parser { 27 _branch() { 28 this.__branch = this.__branch || new Parser({ecmaVersion: this.options.ecmaVersion}, this.input) 29 this.__branch.end = this.end 30 this.__branch.pos = this.pos 31 this.__branch.type = this.type 32 this.__branch.value = this.value 33 this.__branch.containsEsc = this.containsEsc 34 return this.__branch 35 } 36 37 parsePrivateClassElementName(element) { 38 element.computed = false 39 element.key = this.parsePrivateName() 40 if (element.key.name == "constructor") this.raise(element.key.start, "Classes may not have a private element named constructor") 41 const accept = {get: "set", set: "get"}[element.kind] 42 const privateBoundNames = this._privateBoundNamesStack[this._privateBoundNamesStack.length - 1] 43 if (Object.prototype.hasOwnProperty.call(privateBoundNames, element.key.name) && privateBoundNames[element.key.name] !== accept) { 44 this.raise(element.start, "Duplicate private element") 45 } 46 privateBoundNames[element.key.name] = element.kind || true 47 delete this._unresolvedPrivateNamesStack[this._unresolvedPrivateNamesStack.length - 1][element.key.name] 48 return element.key 49 } 50 51 parsePrivateName() { 52 const node = this.startNode() 53 node.name = this.value 54 this.next() 55 this.finishNode(node, "PrivateName") 56 if (this.options.allowReserved == "never") this.checkUnreserved(node) 57 return node 58 } 59 60 // Parse # token 61 getTokenFromCode(code) { 62 if (code === 35) { 63 ++this.pos 64 const word = this.readWord1() 65 return this.finishToken(this.privateNameToken, word) 66 } 67 return super.getTokenFromCode(code) 68 } 69 70 // Manage stacks and check for undeclared private names 71 parseClass(node, isStatement) { 72 this._privateBoundNamesStack = this._privateBoundNamesStack || [] 73 const privateBoundNames = Object.create(this._privateBoundNamesStack[this._privateBoundNamesStack.length - 1] || null) 74 this._privateBoundNamesStack.push(privateBoundNames) 75 this._unresolvedPrivateNamesStack = this._unresolvedPrivateNamesStack || [] 76 const unresolvedPrivateNames = Object.create(null) 77 this._unresolvedPrivateNamesStack.push(unresolvedPrivateNames) 78 const _return = super.parseClass(node, isStatement) 79 this._privateBoundNamesStack.pop() 80 this._unresolvedPrivateNamesStack.pop() 81 if (!this._unresolvedPrivateNamesStack.length) { 82 const names = Object.keys(unresolvedPrivateNames) 83 if (names.length) { 84 names.sort((n1, n2) => unresolvedPrivateNames[n1] - unresolvedPrivateNames[n2]) 85 this.raise(unresolvedPrivateNames[names[0]], "Usage of undeclared private name") 86 } 87 } else Object.assign(this._unresolvedPrivateNamesStack[this._unresolvedPrivateNamesStack.length - 1], unresolvedPrivateNames) 88 return _return 89 } 90 91 // Parse private element access 92 parseSubscript(base, startPos, startLoc, noCalls, maybeAsyncArrow) { 93 if (!this.eat(tt.dot)) { 94 return super.parseSubscript(base, startPos, startLoc, noCalls, maybeAsyncArrow) 95 } 96 let node = this.startNodeAt(startPos, startLoc) 97 node.object = base 98 node.computed = false 99 if (this.type == this.privateNameToken) { 100 node.property = this.parsePrivateName() 101 if (!this._privateBoundNamesStack.length || !this._privateBoundNamesStack[this._privateBoundNamesStack.length - 1][node.property.name]) { 102 this._unresolvedPrivateNamesStack[this._unresolvedPrivateNamesStack.length - 1][node.property.name] = node.property.start 103 } 104 } else { 105 node.property = this.parseIdent(true) 106 } 107 return this.finishNode(node, "MemberExpression") 108 } 109 110 // Prohibit delete of private class elements 111 parseMaybeUnary(refDestructuringErrors, sawUnary) { 112 const _return = super.parseMaybeUnary(refDestructuringErrors, sawUnary) 113 if (_return.operator == "delete") { 114 if (_return.argument.type == "MemberExpression" && _return.argument.property.type == "PrivateName") { 115 this.raise(_return.start, "Private elements may not be deleted") 116 } 117 } 118 return _return 119 } 120 } 121 Parser.prototype.privateNameToken = new TokenType("privateName") 122 return Parser 123} 124