• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"use strict"
2
3const skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g
4
5const acorn = require('internal/deps/acorn/acorn/dist/acorn')
6const tt = acorn.tokTypes
7
8function maybeParseFieldValue(field) {
9  if (this.eat(tt.eq)) {
10    const oldInFieldValue = this._inStaticFieldValue
11    this._inStaticFieldValue = true
12    field.value = this.parseExpression()
13    this._inStaticFieldValue = oldInFieldValue
14  } else field.value = null
15}
16
17const privateClassElements = require("internal/deps/acorn-plugins/acorn-private-class-elements/index")
18
19module.exports = function(Parser) {
20  const ExtendedParser = privateClassElements(Parser)
21
22  return class extends ExtendedParser {
23    // Parse private fields
24    parseClassElement(_constructorAllowsSuper) {
25      if (this.eat(tt.semi)) return null
26
27      const node = this.startNode()
28
29      const tryContextual = (k, noLineBreak) => {
30        if (typeof noLineBreak == "undefined") noLineBreak = false
31        const start = this.start, startLoc = this.startLoc
32        if (!this.eatContextual(k)) return false
33        if (this.type !== tt.parenL && (!noLineBreak || !this.canInsertSemicolon())) return true
34        if (node.key) this.unexpected()
35        node.computed = false
36        node.key = this.startNodeAt(start, startLoc)
37        node.key.name = k
38        this.finishNode(node.key, "Identifier")
39        return false
40      }
41
42      node.static = tryContextual("static")
43      if (!node.static) return super.parseClassElement.apply(this, arguments)
44
45      let isGenerator = this.eat(tt.star)
46      let isAsync = false
47      if (!isGenerator) {
48        // Special-case for `async`, since `parseClassMember` currently looks
49        // for `(` to determine whether `async` is a method name
50        if (this.options.ecmaVersion >= 8 && this.isContextual("async")) {
51          skipWhiteSpace.lastIndex = this.pos
52          let skip = skipWhiteSpace.exec(this.input)
53          let next = this.input.charAt(this.pos + skip[0].length)
54          if (next === ";" || next === "=") {
55            node.key = this.parseIdent(true)
56            node.computed = false
57            maybeParseFieldValue.call(this, node)
58            this.finishNode(node, "FieldDefinition")
59            this.semicolon()
60            return node
61          } else if (this.options.ecmaVersion >= 8 && tryContextual("async", true)) {
62            isAsync = true
63            isGenerator = this.options.ecmaVersion >= 9 && this.eat(tt.star)
64          }
65        } else if (tryContextual("get")) {
66          node.kind = "get"
67        } else if (tryContextual("set")) {
68          node.kind = "set"
69        }
70      }
71      if (this.type === this.privateNameToken) {
72        this.parsePrivateClassElementName(node)
73        if (this.type !== tt.parenL) {
74          if (node.key.name === "prototype") {
75            this.raise(node.key.start, "Classes may not have a private static property named prototype")
76          }
77          maybeParseFieldValue.call(this, node)
78          this.finishNode(node, "FieldDefinition")
79          this.semicolon()
80          return node
81        }
82      } else if (!node.key) {
83        this.parsePropertyName(node)
84        if ((node.key.name || node.key.value) === "prototype" && !node.computed) {
85          this.raise(node.key.start, "Classes may not have a static property named prototype")
86        }
87      }
88      if (!node.kind) node.kind = "method"
89      this.parseClassMethod(node, isGenerator, isAsync)
90      if (!node.kind && (node.key.name || node.key.value) === "constructor" && !node.computed) {
91        this.raise(node.key.start, "Classes may not have a static field named constructor")
92      }
93      if (node.kind === "get" && node.value.params.length !== 0) {
94        this.raiseRecoverable(node.value.start, "getter should have no params")
95      }
96      if (node.kind === "set" && node.value.params.length !== 1) {
97        this.raiseRecoverable(node.value.start, "setter should have exactly one param")
98      }
99      if (node.kind === "set" && node.value.params[0].type === "RestElement") {
100        this.raiseRecoverable(node.value.params[0].start, "Setter cannot use rest params")
101      }
102
103      return node
104
105    }
106
107    // Parse public static fields
108    parseClassMethod(method, isGenerator, isAsync, _allowsDirectSuper) {
109      if (isGenerator || isAsync || method.kind != "method" || !method.static || this.options.ecmaVersion < 8 || this.type == tt.parenL) {
110        return super.parseClassMethod.apply(this, arguments)
111      }
112      maybeParseFieldValue.call(this, method)
113      delete method.kind
114      method = this.finishNode(method, "FieldDefinition")
115      this.semicolon()
116      return method
117    }
118
119    // Prohibit arguments in class field initializers
120    parseIdent(liberal, isBinding) {
121      const ident = super.parseIdent(liberal, isBinding)
122      if (this._inStaticFieldValue && ident.name == "arguments") this.raise(ident.start, "A static class field initializer may not contain arguments")
123      return ident
124    }
125  }
126}
127