...

Source file src/github.com/dop251/goja/compiler.go

Documentation: github.com/dop251/goja

     1  package goja
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/dop251/goja/token"
     6  	"sort"
     7  
     8  	"github.com/dop251/goja/ast"
     9  	"github.com/dop251/goja/file"
    10  	"github.com/dop251/goja/unistring"
    11  )
    12  
    13  type blockType int
    14  
    15  const (
    16  	blockLoop blockType = iota
    17  	blockLoopEnum
    18  	blockTry
    19  	blockLabel
    20  	blockSwitch
    21  	blockWith
    22  	blockScope
    23  	blockIterScope
    24  	blockOptChain
    25  )
    26  
    27  const (
    28  	maskConst     = 1 << 31
    29  	maskVar       = 1 << 30
    30  	maskDeletable = 1 << 29
    31  	maskStrict    = maskDeletable
    32  
    33  	maskTyp = maskConst | maskVar | maskDeletable
    34  )
    35  
    36  type varType byte
    37  
    38  const (
    39  	varTypeVar varType = iota
    40  	varTypeLet
    41  	varTypeStrictConst
    42  	varTypeConst
    43  )
    44  
    45  const thisBindingName = " this" // must not be a valid identifier
    46  
    47  type CompilerError struct {
    48  	Message string
    49  	File    *file.File
    50  	Offset  int
    51  }
    52  
    53  type CompilerSyntaxError struct {
    54  	CompilerError
    55  }
    56  
    57  type CompilerReferenceError struct {
    58  	CompilerError
    59  }
    60  
    61  type srcMapItem struct {
    62  	pc     int
    63  	srcPos int
    64  }
    65  
    66  // Program is an internal, compiled representation of code which is produced by the Compile function.
    67  // This representation is not linked to a runtime in any way and can be used concurrently.
    68  // It is always preferable to use a Program over a string when running code as it skips the compilation step.
    69  type Program struct {
    70  	code   []instruction
    71  	values []Value
    72  
    73  	funcName unistring.String
    74  	src      *file.File
    75  	srcMap   []srcMapItem
    76  }
    77  
    78  type compiler struct {
    79  	p     *Program
    80  	scope *scope
    81  	block *block
    82  
    83  	classScope *classScope
    84  
    85  	enumGetExpr compiledEnumGetExpr
    86  
    87  	evalVM *vm // VM used to evaluate constant expressions
    88  	ctxVM  *vm // VM in which an eval() code is compiled
    89  
    90  	codeScratchpad []instruction
    91  }
    92  
    93  type binding struct {
    94  	scope        *scope
    95  	name         unistring.String
    96  	accessPoints map[*scope]*[]int
    97  	isConst      bool
    98  	isStrict     bool
    99  	isArg        bool
   100  	isVar        bool
   101  	inStash      bool
   102  }
   103  
   104  func (b *binding) getAccessPointsForScope(s *scope) *[]int {
   105  	m := b.accessPoints[s]
   106  	if m == nil {
   107  		a := make([]int, 0, 1)
   108  		m = &a
   109  		if b.accessPoints == nil {
   110  			b.accessPoints = make(map[*scope]*[]int)
   111  		}
   112  		b.accessPoints[s] = m
   113  	}
   114  	return m
   115  }
   116  
   117  func (b *binding) markAccessPointAt(pos int) {
   118  	scope := b.scope.c.scope
   119  	m := b.getAccessPointsForScope(scope)
   120  	*m = append(*m, pos-scope.base)
   121  }
   122  
   123  func (b *binding) markAccessPointAtScope(scope *scope, pos int) {
   124  	m := b.getAccessPointsForScope(scope)
   125  	*m = append(*m, pos-scope.base)
   126  }
   127  
   128  func (b *binding) markAccessPoint() {
   129  	scope := b.scope.c.scope
   130  	m := b.getAccessPointsForScope(scope)
   131  	*m = append(*m, len(scope.prg.code)-scope.base)
   132  }
   133  
   134  func (b *binding) emitGet() {
   135  	b.markAccessPoint()
   136  	if b.isVar && !b.isArg {
   137  		b.scope.c.emit(loadStack(0))
   138  	} else {
   139  		b.scope.c.emit(loadStackLex(0))
   140  	}
   141  }
   142  
   143  func (b *binding) emitGetAt(pos int) {
   144  	b.markAccessPointAt(pos)
   145  	if b.isVar && !b.isArg {
   146  		b.scope.c.p.code[pos] = loadStack(0)
   147  	} else {
   148  		b.scope.c.p.code[pos] = loadStackLex(0)
   149  	}
   150  }
   151  
   152  func (b *binding) emitGetP() {
   153  	if b.isVar && !b.isArg {
   154  		// no-op
   155  	} else {
   156  		// make sure TDZ is checked
   157  		b.markAccessPoint()
   158  		b.scope.c.emit(loadStackLex(0), pop)
   159  	}
   160  }
   161  
   162  func (b *binding) emitSet() {
   163  	if b.isConst {
   164  		if b.isStrict || b.scope.c.scope.strict {
   165  			b.scope.c.emit(throwAssignToConst)
   166  		}
   167  		return
   168  	}
   169  	b.markAccessPoint()
   170  	if b.isVar && !b.isArg {
   171  		b.scope.c.emit(storeStack(0))
   172  	} else {
   173  		b.scope.c.emit(storeStackLex(0))
   174  	}
   175  }
   176  
   177  func (b *binding) emitSetP() {
   178  	if b.isConst {
   179  		if b.isStrict || b.scope.c.scope.strict {
   180  			b.scope.c.emit(throwAssignToConst)
   181  		}
   182  		return
   183  	}
   184  	b.markAccessPoint()
   185  	if b.isVar && !b.isArg {
   186  		b.scope.c.emit(storeStackP(0))
   187  	} else {
   188  		b.scope.c.emit(storeStackLexP(0))
   189  	}
   190  }
   191  
   192  func (b *binding) emitInitP() {
   193  	if !b.isVar && b.scope.outer == nil {
   194  		b.scope.c.emit(initGlobalP(b.name))
   195  	} else {
   196  		b.markAccessPoint()
   197  		b.scope.c.emit(initStackP(0))
   198  	}
   199  }
   200  
   201  func (b *binding) emitInit() {
   202  	if !b.isVar && b.scope.outer == nil {
   203  		b.scope.c.emit(initGlobal(b.name))
   204  	} else {
   205  		b.markAccessPoint()
   206  		b.scope.c.emit(initStack(0))
   207  	}
   208  }
   209  
   210  func (b *binding) emitInitAt(pos int) {
   211  	if !b.isVar && b.scope.outer == nil {
   212  		b.scope.c.p.code[pos] = initGlobal(b.name)
   213  	} else {
   214  		b.markAccessPointAt(pos)
   215  		b.scope.c.p.code[pos] = initStack(0)
   216  	}
   217  }
   218  
   219  func (b *binding) emitInitAtScope(scope *scope, pos int) {
   220  	if !b.isVar && scope.outer == nil {
   221  		scope.c.p.code[pos] = initGlobal(b.name)
   222  	} else {
   223  		b.markAccessPointAtScope(scope, pos)
   224  		scope.c.p.code[pos] = initStack(0)
   225  	}
   226  }
   227  
   228  func (b *binding) emitInitPAtScope(scope *scope, pos int) {
   229  	if !b.isVar && scope.outer == nil {
   230  		scope.c.p.code[pos] = initGlobalP(b.name)
   231  	} else {
   232  		b.markAccessPointAtScope(scope, pos)
   233  		scope.c.p.code[pos] = initStackP(0)
   234  	}
   235  }
   236  
   237  func (b *binding) emitGetVar(callee bool) {
   238  	b.markAccessPoint()
   239  	if b.isVar && !b.isArg {
   240  		b.scope.c.emit(&loadMixed{name: b.name, callee: callee})
   241  	} else {
   242  		b.scope.c.emit(&loadMixedLex{name: b.name, callee: callee})
   243  	}
   244  }
   245  
   246  func (b *binding) emitResolveVar(strict bool) {
   247  	b.markAccessPoint()
   248  	if b.isVar && !b.isArg {
   249  		b.scope.c.emit(&resolveMixed{name: b.name, strict: strict, typ: varTypeVar})
   250  	} else {
   251  		var typ varType
   252  		if b.isConst {
   253  			if b.isStrict {
   254  				typ = varTypeStrictConst
   255  			} else {
   256  				typ = varTypeConst
   257  			}
   258  		} else {
   259  			typ = varTypeLet
   260  		}
   261  		b.scope.c.emit(&resolveMixed{name: b.name, strict: strict, typ: typ})
   262  	}
   263  }
   264  
   265  func (b *binding) moveToStash() {
   266  	if b.isArg && !b.scope.argsInStash {
   267  		b.scope.moveArgsToStash()
   268  	} else {
   269  		b.inStash = true
   270  		b.scope.needStash = true
   271  	}
   272  }
   273  
   274  func (b *binding) useCount() (count int) {
   275  	for _, a := range b.accessPoints {
   276  		count += len(*a)
   277  	}
   278  	return
   279  }
   280  
   281  type scope struct {
   282  	c          *compiler
   283  	prg        *Program
   284  	outer      *scope
   285  	nested     []*scope
   286  	boundNames map[unistring.String]*binding
   287  	bindings   []*binding
   288  	base       int
   289  	numArgs    int
   290  
   291  	// function type. If not funcNone, this is a function or a top-level lexical environment
   292  	funcType funcType
   293  
   294  	// in strict mode
   295  	strict bool
   296  	// eval top-level scope
   297  	eval bool
   298  	// at least one inner scope has direct eval() which can lookup names dynamically (by name)
   299  	dynLookup bool
   300  	// at least one binding has been marked for placement in stash
   301  	needStash bool
   302  
   303  	// is a variable environment, i.e. the target for dynamically created var bindings
   304  	variable bool
   305  	// a function scope that has at least one direct eval() and non-strict, so the variables can be added dynamically
   306  	dynamic bool
   307  	// arguments have been marked for placement in stash (functions only)
   308  	argsInStash bool
   309  	// need 'arguments' object (functions only)
   310  	argsNeeded bool
   311  }
   312  
   313  type block struct {
   314  	typ        blockType
   315  	label      unistring.String
   316  	cont       int
   317  	breaks     []int
   318  	conts      []int
   319  	outer      *block
   320  	breaking   *block // set when the 'finally' block is an empty break statement sequence
   321  	needResult bool
   322  }
   323  
   324  func (c *compiler) leaveScopeBlock(enter *enterBlock) {
   325  	c.updateEnterBlock(enter)
   326  	leave := &leaveBlock{
   327  		stackSize: enter.stackSize,
   328  		popStash:  enter.stashSize > 0,
   329  	}
   330  	c.emit(leave)
   331  	for _, pc := range c.block.breaks {
   332  		c.p.code[pc] = leave
   333  	}
   334  	c.block.breaks = nil
   335  	c.leaveBlock()
   336  }
   337  
   338  func (c *compiler) leaveBlock() {
   339  	lbl := len(c.p.code)
   340  	for _, item := range c.block.breaks {
   341  		c.p.code[item] = jump(lbl - item)
   342  	}
   343  	if t := c.block.typ; t == blockLoop || t == blockLoopEnum {
   344  		for _, item := range c.block.conts {
   345  			c.p.code[item] = jump(c.block.cont - item)
   346  		}
   347  	}
   348  	c.block = c.block.outer
   349  }
   350  
   351  func (e *CompilerSyntaxError) Error() string {
   352  	if e.File != nil {
   353  		return fmt.Sprintf("SyntaxError: %s at %s", e.Message, e.File.Position(e.Offset))
   354  	}
   355  	return fmt.Sprintf("SyntaxError: %s", e.Message)
   356  }
   357  
   358  func (e *CompilerReferenceError) Error() string {
   359  	return fmt.Sprintf("ReferenceError: %s", e.Message)
   360  }
   361  
   362  func (c *compiler) newScope() {
   363  	strict := false
   364  	if c.scope != nil {
   365  		strict = c.scope.strict
   366  	}
   367  	c.scope = &scope{
   368  		c:      c,
   369  		prg:    c.p,
   370  		outer:  c.scope,
   371  		strict: strict,
   372  	}
   373  }
   374  
   375  func (c *compiler) newBlockScope() {
   376  	c.newScope()
   377  	if outer := c.scope.outer; outer != nil {
   378  		outer.nested = append(outer.nested, c.scope)
   379  	}
   380  	c.scope.base = len(c.p.code)
   381  }
   382  
   383  func (c *compiler) popScope() {
   384  	c.scope = c.scope.outer
   385  }
   386  
   387  func newCompiler() *compiler {
   388  	c := &compiler{
   389  		p: &Program{},
   390  	}
   391  
   392  	c.enumGetExpr.init(c, file.Idx(0))
   393  
   394  	return c
   395  }
   396  
   397  func (p *Program) defineLiteralValue(val Value) uint32 {
   398  	for idx, v := range p.values {
   399  		if v.SameAs(val) {
   400  			return uint32(idx)
   401  		}
   402  	}
   403  	idx := uint32(len(p.values))
   404  	p.values = append(p.values, val)
   405  	return idx
   406  }
   407  
   408  func (p *Program) dumpCode(logger func(format string, args ...interface{})) {
   409  	p._dumpCode("", logger)
   410  }
   411  
   412  func (p *Program) _dumpCode(indent string, logger func(format string, args ...interface{})) {
   413  	logger("values: %+v", p.values)
   414  	dumpInitFields := func(initFields *Program) {
   415  		i := indent + ">"
   416  		logger("%s ---- init_fields:", i)
   417  		initFields._dumpCode(i, logger)
   418  		logger("%s ----", i)
   419  	}
   420  	for pc, ins := range p.code {
   421  		logger("%s %d: %T(%v)", indent, pc, ins, ins)
   422  		var prg *Program
   423  		switch f := ins.(type) {
   424  		case newFuncInstruction:
   425  			prg = f.getPrg()
   426  		case *newDerivedClass:
   427  			if f.initFields != nil {
   428  				dumpInitFields(f.initFields)
   429  			}
   430  			prg = f.ctor
   431  		case *newClass:
   432  			if f.initFields != nil {
   433  				dumpInitFields(f.initFields)
   434  			}
   435  			prg = f.ctor
   436  		case *newStaticFieldInit:
   437  			if f.initFields != nil {
   438  				dumpInitFields(f.initFields)
   439  			}
   440  		}
   441  		if prg != nil {
   442  			prg._dumpCode(indent+">", logger)
   443  		}
   444  	}
   445  }
   446  
   447  func (p *Program) sourceOffset(pc int) int {
   448  	i := sort.Search(len(p.srcMap), func(idx int) bool {
   449  		return p.srcMap[idx].pc > pc
   450  	}) - 1
   451  	if i >= 0 {
   452  		return p.srcMap[i].srcPos
   453  	}
   454  
   455  	return 0
   456  }
   457  
   458  func (p *Program) addSrcMap(srcPos int) {
   459  	if len(p.srcMap) > 0 && p.srcMap[len(p.srcMap)-1].srcPos == srcPos {
   460  		return
   461  	}
   462  	p.srcMap = append(p.srcMap, srcMapItem{pc: len(p.code), srcPos: srcPos})
   463  }
   464  
   465  func (s *scope) lookupName(name unistring.String) (binding *binding, noDynamics bool) {
   466  	noDynamics = true
   467  	toStash := false
   468  	for curScope := s; ; curScope = curScope.outer {
   469  		if curScope.outer != nil {
   470  			if b, exists := curScope.boundNames[name]; exists {
   471  				if toStash && !b.inStash {
   472  					b.moveToStash()
   473  				}
   474  				binding = b
   475  				return
   476  			}
   477  		} else {
   478  			noDynamics = false
   479  			return
   480  		}
   481  		if curScope.dynamic {
   482  			noDynamics = false
   483  		}
   484  		if name == "arguments" && curScope.funcType != funcNone && curScope.funcType != funcArrow {
   485  			if curScope.funcType == funcClsInit {
   486  				s.c.throwSyntaxError(0, "'arguments' is not allowed in class field initializer or static initialization block")
   487  			}
   488  			curScope.argsNeeded = true
   489  			binding, _ = curScope.bindName(name)
   490  			return
   491  		}
   492  		if curScope.isFunction() {
   493  			toStash = true
   494  		}
   495  	}
   496  }
   497  
   498  func (s *scope) lookupThis() (*binding, bool) {
   499  	toStash := false
   500  	for curScope := s; curScope != nil; curScope = curScope.outer {
   501  		if curScope.outer == nil {
   502  			if curScope.eval {
   503  				return nil, true
   504  			}
   505  		}
   506  		if b, exists := curScope.boundNames[thisBindingName]; exists {
   507  			if toStash && !b.inStash {
   508  				b.moveToStash()
   509  			}
   510  			return b, false
   511  		}
   512  		if curScope.isFunction() {
   513  			toStash = true
   514  		}
   515  	}
   516  	return nil, false
   517  }
   518  
   519  func (s *scope) ensureBoundNamesCreated() {
   520  	if s.boundNames == nil {
   521  		s.boundNames = make(map[unistring.String]*binding)
   522  	}
   523  }
   524  
   525  func (s *scope) addBinding(offset int) *binding {
   526  	if len(s.bindings) >= (1<<24)-1 {
   527  		s.c.throwSyntaxError(offset, "Too many variables")
   528  	}
   529  	b := &binding{
   530  		scope: s,
   531  	}
   532  	s.bindings = append(s.bindings, b)
   533  	return b
   534  }
   535  
   536  func (s *scope) bindNameLexical(name unistring.String, unique bool, offset int) (*binding, bool) {
   537  	if b := s.boundNames[name]; b != nil {
   538  		if unique {
   539  			s.c.throwSyntaxError(offset, "Identifier '%s' has already been declared", name)
   540  		}
   541  		return b, false
   542  	}
   543  	b := s.addBinding(offset)
   544  	b.name = name
   545  	s.ensureBoundNamesCreated()
   546  	s.boundNames[name] = b
   547  	return b, true
   548  }
   549  
   550  func (s *scope) createThisBinding() *binding {
   551  	thisBinding, _ := s.bindNameLexical(thisBindingName, false, 0)
   552  	thisBinding.isVar = true // don't check on load
   553  	return thisBinding
   554  }
   555  
   556  func (s *scope) bindName(name unistring.String) (*binding, bool) {
   557  	if !s.isFunction() && !s.variable && s.outer != nil {
   558  		return s.outer.bindName(name)
   559  	}
   560  	b, created := s.bindNameLexical(name, false, 0)
   561  	if created {
   562  		b.isVar = true
   563  	}
   564  	return b, created
   565  }
   566  
   567  func (s *scope) bindNameShadow(name unistring.String) (*binding, bool) {
   568  	if !s.isFunction() && s.outer != nil {
   569  		return s.outer.bindNameShadow(name)
   570  	}
   571  
   572  	_, exists := s.boundNames[name]
   573  	b := &binding{
   574  		scope: s,
   575  		name:  name,
   576  	}
   577  	s.bindings = append(s.bindings, b)
   578  	s.ensureBoundNamesCreated()
   579  	s.boundNames[name] = b
   580  	return b, !exists
   581  }
   582  
   583  func (s *scope) nearestFunction() *scope {
   584  	for sc := s; sc != nil; sc = sc.outer {
   585  		if sc.isFunction() {
   586  			return sc
   587  		}
   588  	}
   589  	return nil
   590  }
   591  
   592  func (s *scope) nearestThis() *scope {
   593  	for sc := s; sc != nil; sc = sc.outer {
   594  		if sc.eval || sc.isFunction() && sc.funcType != funcArrow {
   595  			return sc
   596  		}
   597  	}
   598  	return nil
   599  }
   600  
   601  func (s *scope) finaliseVarAlloc(stackOffset int) (stashSize, stackSize int) {
   602  	argsInStash := false
   603  	if f := s.nearestFunction(); f != nil {
   604  		argsInStash = f.argsInStash
   605  	}
   606  	stackIdx, stashIdx := 0, 0
   607  	allInStash := s.isDynamic()
   608  	var derivedCtor bool
   609  	if fs := s.nearestThis(); fs != nil && fs.funcType == funcDerivedCtor {
   610  		derivedCtor = true
   611  	}
   612  	for i, b := range s.bindings {
   613  		var this bool
   614  		if b.name == thisBindingName {
   615  			this = true
   616  		}
   617  		if allInStash || b.inStash {
   618  			for scope, aps := range b.accessPoints {
   619  				var level uint32
   620  				for sc := scope; sc != nil && sc != s; sc = sc.outer {
   621  					if sc.needStash || sc.isDynamic() {
   622  						level++
   623  					}
   624  				}
   625  				if level > 255 {
   626  					s.c.throwSyntaxError(0, "Maximum nesting level (256) exceeded")
   627  				}
   628  				idx := (level << 24) | uint32(stashIdx)
   629  				base := scope.base
   630  				code := scope.prg.code
   631  				if this {
   632  					if derivedCtor {
   633  						for _, pc := range *aps {
   634  							ap := &code[base+pc]
   635  							switch (*ap).(type) {
   636  							case loadStack:
   637  								*ap = loadThisStash(idx)
   638  							case initStack:
   639  								*ap = initStash(idx)
   640  							case resolveThisStack:
   641  								*ap = resolveThisStash(idx)
   642  							case _ret:
   643  								*ap = cret(idx)
   644  							default:
   645  								s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for 'this'")
   646  							}
   647  						}
   648  					} else {
   649  						for _, pc := range *aps {
   650  							ap := &code[base+pc]
   651  							switch (*ap).(type) {
   652  							case loadStack:
   653  								*ap = loadStash(idx)
   654  							case initStack:
   655  								*ap = initStash(idx)
   656  							default:
   657  								s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for 'this'")
   658  							}
   659  						}
   660  					}
   661  				} else {
   662  					for _, pc := range *aps {
   663  						ap := &code[base+pc]
   664  						switch i := (*ap).(type) {
   665  						case loadStack:
   666  							*ap = loadStash(idx)
   667  						case storeStack:
   668  							*ap = storeStash(idx)
   669  						case storeStackP:
   670  							*ap = storeStashP(idx)
   671  						case loadStackLex:
   672  							*ap = loadStashLex(idx)
   673  						case storeStackLex:
   674  							*ap = storeStashLex(idx)
   675  						case storeStackLexP:
   676  							*ap = storeStashLexP(idx)
   677  						case initStackP:
   678  							*ap = initStashP(idx)
   679  						case initStack:
   680  							*ap = initStash(idx)
   681  						case *loadMixed:
   682  							i.idx = idx
   683  						case *loadMixedLex:
   684  							i.idx = idx
   685  						case *resolveMixed:
   686  							i.idx = idx
   687  						default:
   688  							s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for binding: %T", i)
   689  						}
   690  					}
   691  				}
   692  			}
   693  			stashIdx++
   694  		} else {
   695  			var idx int
   696  			if !this {
   697  				if i < s.numArgs {
   698  					idx = -(i + 1)
   699  				} else {
   700  					stackIdx++
   701  					idx = stackIdx + stackOffset
   702  				}
   703  			}
   704  			for scope, aps := range b.accessPoints {
   705  				var level int
   706  				for sc := scope; sc != nil && sc != s; sc = sc.outer {
   707  					if sc.needStash || sc.isDynamic() {
   708  						level++
   709  					}
   710  				}
   711  				if level > 255 {
   712  					s.c.throwSyntaxError(0, "Maximum nesting level (256) exceeded")
   713  				}
   714  				code := scope.prg.code
   715  				base := scope.base
   716  				if this {
   717  					if derivedCtor {
   718  						for _, pc := range *aps {
   719  							ap := &code[base+pc]
   720  							switch (*ap).(type) {
   721  							case loadStack:
   722  								*ap = loadThisStack{}
   723  							case initStack:
   724  								// no-op
   725  							case resolveThisStack:
   726  								// no-op
   727  							case _ret:
   728  								// no-op, already in the right place
   729  							default:
   730  								s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for 'this'")
   731  							}
   732  						}
   733  					} /*else {
   734  						no-op
   735  					}*/
   736  				} else if argsInStash {
   737  					for _, pc := range *aps {
   738  						ap := &code[base+pc]
   739  						switch i := (*ap).(type) {
   740  						case loadStack:
   741  							*ap = loadStack1(idx)
   742  						case storeStack:
   743  							*ap = storeStack1(idx)
   744  						case storeStackP:
   745  							*ap = storeStack1P(idx)
   746  						case loadStackLex:
   747  							*ap = loadStack1Lex(idx)
   748  						case storeStackLex:
   749  							*ap = storeStack1Lex(idx)
   750  						case storeStackLexP:
   751  							*ap = storeStack1LexP(idx)
   752  						case initStackP:
   753  							*ap = initStack1P(idx)
   754  						case initStack:
   755  							*ap = initStack1(idx)
   756  						case *loadMixed:
   757  							*ap = &loadMixedStack1{name: i.name, idx: idx, level: uint8(level), callee: i.callee}
   758  						case *loadMixedLex:
   759  							*ap = &loadMixedStack1Lex{name: i.name, idx: idx, level: uint8(level), callee: i.callee}
   760  						case *resolveMixed:
   761  							*ap = &resolveMixedStack1{typ: i.typ, name: i.name, idx: idx, level: uint8(level), strict: i.strict}
   762  						default:
   763  							s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for binding: %T", i)
   764  						}
   765  					}
   766  				} else {
   767  					for _, pc := range *aps {
   768  						ap := &code[base+pc]
   769  						switch i := (*ap).(type) {
   770  						case loadStack:
   771  							*ap = loadStack(idx)
   772  						case storeStack:
   773  							*ap = storeStack(idx)
   774  						case storeStackP:
   775  							*ap = storeStackP(idx)
   776  						case loadStackLex:
   777  							*ap = loadStackLex(idx)
   778  						case storeStackLex:
   779  							*ap = storeStackLex(idx)
   780  						case storeStackLexP:
   781  							*ap = storeStackLexP(idx)
   782  						case initStack:
   783  							*ap = initStack(idx)
   784  						case initStackP:
   785  							*ap = initStackP(idx)
   786  						case *loadMixed:
   787  							*ap = &loadMixedStack{name: i.name, idx: idx, level: uint8(level), callee: i.callee}
   788  						case *loadMixedLex:
   789  							*ap = &loadMixedStackLex{name: i.name, idx: idx, level: uint8(level), callee: i.callee}
   790  						case *resolveMixed:
   791  							*ap = &resolveMixedStack{typ: i.typ, name: i.name, idx: idx, level: uint8(level), strict: i.strict}
   792  						default:
   793  							s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for binding: %T", i)
   794  						}
   795  					}
   796  				}
   797  			}
   798  		}
   799  	}
   800  	for _, nested := range s.nested {
   801  		nested.finaliseVarAlloc(stackIdx + stackOffset)
   802  	}
   803  	return stashIdx, stackIdx
   804  }
   805  
   806  func (s *scope) moveArgsToStash() {
   807  	for _, b := range s.bindings {
   808  		if !b.isArg {
   809  			break
   810  		}
   811  		b.inStash = true
   812  	}
   813  	s.argsInStash = true
   814  	s.needStash = true
   815  }
   816  
   817  func (c *compiler) trimCode(delta int) {
   818  	src := c.p.code[delta:]
   819  	newCode := make([]instruction, len(src))
   820  	copy(newCode, src)
   821  	if cap(c.codeScratchpad) < cap(c.p.code) {
   822  		c.codeScratchpad = c.p.code[:0]
   823  	}
   824  	c.p.code = newCode
   825  }
   826  
   827  func (s *scope) trimCode(delta int) {
   828  	s.c.trimCode(delta)
   829  	if delta != 0 {
   830  		srcMap := s.c.p.srcMap
   831  		for i := range srcMap {
   832  			srcMap[i].pc -= delta
   833  		}
   834  		s.adjustBase(-delta)
   835  	}
   836  }
   837  
   838  func (s *scope) adjustBase(delta int) {
   839  	s.base += delta
   840  	for _, nested := range s.nested {
   841  		nested.adjustBase(delta)
   842  	}
   843  }
   844  
   845  func (s *scope) makeNamesMap() map[unistring.String]uint32 {
   846  	l := len(s.bindings)
   847  	if l == 0 {
   848  		return nil
   849  	}
   850  	names := make(map[unistring.String]uint32, l)
   851  	for i, b := range s.bindings {
   852  		idx := uint32(i)
   853  		if b.isConst {
   854  			idx |= maskConst
   855  			if b.isStrict {
   856  				idx |= maskStrict
   857  			}
   858  		}
   859  		if b.isVar {
   860  			idx |= maskVar
   861  		}
   862  		names[b.name] = idx
   863  	}
   864  	return names
   865  }
   866  
   867  func (s *scope) isDynamic() bool {
   868  	return s.dynLookup || s.dynamic
   869  }
   870  
   871  func (s *scope) isFunction() bool {
   872  	return s.funcType != funcNone && !s.eval
   873  }
   874  
   875  func (s *scope) deleteBinding(b *binding) {
   876  	idx := 0
   877  	for i, bb := range s.bindings {
   878  		if bb == b {
   879  			idx = i
   880  			goto found
   881  		}
   882  	}
   883  	return
   884  found:
   885  	delete(s.boundNames, b.name)
   886  	copy(s.bindings[idx:], s.bindings[idx+1:])
   887  	l := len(s.bindings) - 1
   888  	s.bindings[l] = nil
   889  	s.bindings = s.bindings[:l]
   890  }
   891  
   892  func (c *compiler) compile(in *ast.Program, strict, inGlobal bool, evalVm *vm) {
   893  	c.ctxVM = evalVm
   894  
   895  	eval := evalVm != nil
   896  	c.p.src = in.File
   897  	c.newScope()
   898  	scope := c.scope
   899  	scope.dynamic = true
   900  	scope.eval = eval
   901  	if !strict && len(in.Body) > 0 {
   902  		strict = c.isStrict(in.Body) != nil
   903  	}
   904  	scope.strict = strict
   905  	ownVarScope := eval && strict
   906  	ownLexScope := !inGlobal || eval
   907  	if ownVarScope {
   908  		c.newBlockScope()
   909  		scope = c.scope
   910  		scope.variable = true
   911  	}
   912  	if eval && !inGlobal {
   913  		for s := evalVm.stash; s != nil; s = s.outer {
   914  			if ft := s.funcType; ft != funcNone && ft != funcArrow {
   915  				scope.funcType = ft
   916  				break
   917  			}
   918  		}
   919  	}
   920  	funcs := c.extractFunctions(in.Body)
   921  	c.createFunctionBindings(funcs)
   922  	numFuncs := len(scope.bindings)
   923  	if inGlobal && !ownVarScope {
   924  		if numFuncs == len(funcs) {
   925  			c.compileFunctionsGlobalAllUnique(funcs)
   926  		} else {
   927  			c.compileFunctionsGlobal(funcs)
   928  		}
   929  	}
   930  	c.compileDeclList(in.DeclarationList, false)
   931  	numVars := len(scope.bindings) - numFuncs
   932  	vars := make([]unistring.String, len(scope.bindings))
   933  	for i, b := range scope.bindings {
   934  		vars[i] = b.name
   935  	}
   936  	if len(vars) > 0 && !ownVarScope && ownLexScope {
   937  		if inGlobal {
   938  			c.emit(&bindGlobal{
   939  				vars:      vars[numFuncs:],
   940  				funcs:     vars[:numFuncs],
   941  				deletable: eval,
   942  			})
   943  		} else {
   944  			c.emit(&bindVars{names: vars, deletable: eval})
   945  		}
   946  	}
   947  	var enter *enterBlock
   948  	if c.compileLexicalDeclarations(in.Body, ownVarScope || !ownLexScope) {
   949  		if ownLexScope {
   950  			c.block = &block{
   951  				outer:      c.block,
   952  				typ:        blockScope,
   953  				needResult: true,
   954  			}
   955  			enter = &enterBlock{}
   956  			c.emit(enter)
   957  		}
   958  	}
   959  	if len(scope.bindings) > 0 && !ownLexScope {
   960  		var lets, consts []unistring.String
   961  		for _, b := range c.scope.bindings[numFuncs+numVars:] {
   962  			if b.isConst {
   963  				consts = append(consts, b.name)
   964  			} else {
   965  				lets = append(lets, b.name)
   966  			}
   967  		}
   968  		c.emit(&bindGlobal{
   969  			vars:   vars[numFuncs:],
   970  			funcs:  vars[:numFuncs],
   971  			lets:   lets,
   972  			consts: consts,
   973  		})
   974  	}
   975  	if !inGlobal || ownVarScope {
   976  		c.compileFunctions(funcs)
   977  	}
   978  	c.compileStatements(in.Body, true)
   979  	if enter != nil {
   980  		c.leaveScopeBlock(enter)
   981  		c.popScope()
   982  	}
   983  
   984  	scope.finaliseVarAlloc(0)
   985  }
   986  
   987  func (c *compiler) compileDeclList(v []*ast.VariableDeclaration, inFunc bool) {
   988  	for _, value := range v {
   989  		c.createVarBindings(value, inFunc)
   990  	}
   991  }
   992  
   993  func (c *compiler) extractLabelled(st ast.Statement) ast.Statement {
   994  	if st, ok := st.(*ast.LabelledStatement); ok {
   995  		return c.extractLabelled(st.Statement)
   996  	}
   997  	return st
   998  }
   999  
  1000  func (c *compiler) extractFunctions(list []ast.Statement) (funcs []*ast.FunctionDeclaration) {
  1001  	for _, st := range list {
  1002  		var decl *ast.FunctionDeclaration
  1003  		switch st := c.extractLabelled(st).(type) {
  1004  		case *ast.FunctionDeclaration:
  1005  			decl = st
  1006  		case *ast.LabelledStatement:
  1007  			if st1, ok := st.Statement.(*ast.FunctionDeclaration); ok {
  1008  				decl = st1
  1009  			} else {
  1010  				continue
  1011  			}
  1012  		default:
  1013  			continue
  1014  		}
  1015  		funcs = append(funcs, decl)
  1016  	}
  1017  	return
  1018  }
  1019  
  1020  func (c *compiler) createFunctionBindings(funcs []*ast.FunctionDeclaration) {
  1021  	s := c.scope
  1022  	if s.outer != nil {
  1023  		unique := !s.isFunction() && !s.variable && s.strict
  1024  		if !unique {
  1025  			hasNonStandard := false
  1026  			for _, decl := range funcs {
  1027  				if !decl.Function.Async && !decl.Function.Generator {
  1028  					s.bindNameLexical(decl.Function.Name.Name, false, int(decl.Function.Name.Idx1())-1)
  1029  				} else {
  1030  					hasNonStandard = true
  1031  				}
  1032  			}
  1033  			if hasNonStandard {
  1034  				for _, decl := range funcs {
  1035  					if decl.Function.Async || decl.Function.Generator {
  1036  						s.bindNameLexical(decl.Function.Name.Name, true, int(decl.Function.Name.Idx1())-1)
  1037  					}
  1038  				}
  1039  			}
  1040  		} else {
  1041  			for _, decl := range funcs {
  1042  				s.bindNameLexical(decl.Function.Name.Name, true, int(decl.Function.Name.Idx1())-1)
  1043  			}
  1044  		}
  1045  	} else {
  1046  		for _, decl := range funcs {
  1047  			s.bindName(decl.Function.Name.Name)
  1048  		}
  1049  	}
  1050  }
  1051  
  1052  func (c *compiler) compileFunctions(list []*ast.FunctionDeclaration) {
  1053  	for _, decl := range list {
  1054  		c.compileFunction(decl)
  1055  	}
  1056  }
  1057  
  1058  func (c *compiler) compileFunctionsGlobalAllUnique(list []*ast.FunctionDeclaration) {
  1059  	for _, decl := range list {
  1060  		c.compileFunctionLiteral(decl.Function, false).emitGetter(true)
  1061  	}
  1062  }
  1063  
  1064  func (c *compiler) compileFunctionsGlobal(list []*ast.FunctionDeclaration) {
  1065  	m := make(map[unistring.String]int, len(list))
  1066  	for i := len(list) - 1; i >= 0; i-- {
  1067  		name := list[i].Function.Name.Name
  1068  		if _, exists := m[name]; !exists {
  1069  			m[name] = i
  1070  		}
  1071  	}
  1072  	idx := 0
  1073  	for i, decl := range list {
  1074  		name := decl.Function.Name.Name
  1075  		if m[name] == i {
  1076  			c.compileFunctionLiteral(decl.Function, false).emitGetter(true)
  1077  			c.scope.bindings[idx] = c.scope.boundNames[name]
  1078  			idx++
  1079  		} else {
  1080  			leave := c.enterDummyMode()
  1081  			c.compileFunctionLiteral(decl.Function, false).emitGetter(false)
  1082  			leave()
  1083  		}
  1084  	}
  1085  }
  1086  
  1087  func (c *compiler) createVarIdBinding(name unistring.String, offset int, inFunc bool) {
  1088  	if c.scope.strict {
  1089  		c.checkIdentifierLName(name, offset)
  1090  		c.checkIdentifierName(name, offset)
  1091  	}
  1092  	if !inFunc || name != "arguments" {
  1093  		c.scope.bindName(name)
  1094  	}
  1095  }
  1096  
  1097  func (c *compiler) createBindings(target ast.Expression, createIdBinding func(name unistring.String, offset int)) {
  1098  	switch target := target.(type) {
  1099  	case *ast.Identifier:
  1100  		createIdBinding(target.Name, int(target.Idx)-1)
  1101  	case *ast.ObjectPattern:
  1102  		for _, prop := range target.Properties {
  1103  			switch prop := prop.(type) {
  1104  			case *ast.PropertyShort:
  1105  				createIdBinding(prop.Name.Name, int(prop.Name.Idx)-1)
  1106  			case *ast.PropertyKeyed:
  1107  				c.createBindings(prop.Value, createIdBinding)
  1108  			default:
  1109  				c.throwSyntaxError(int(target.Idx0()-1), "unsupported property type in ObjectPattern: %T", prop)
  1110  			}
  1111  		}
  1112  		if target.Rest != nil {
  1113  			c.createBindings(target.Rest, createIdBinding)
  1114  		}
  1115  	case *ast.ArrayPattern:
  1116  		for _, elt := range target.Elements {
  1117  			if elt != nil {
  1118  				c.createBindings(elt, createIdBinding)
  1119  			}
  1120  		}
  1121  		if target.Rest != nil {
  1122  			c.createBindings(target.Rest, createIdBinding)
  1123  		}
  1124  	case *ast.AssignExpression:
  1125  		c.createBindings(target.Left, createIdBinding)
  1126  	default:
  1127  		c.throwSyntaxError(int(target.Idx0()-1), "unsupported binding target: %T", target)
  1128  	}
  1129  }
  1130  
  1131  func (c *compiler) createVarBinding(target ast.Expression, inFunc bool) {
  1132  	c.createBindings(target, func(name unistring.String, offset int) {
  1133  		c.createVarIdBinding(name, offset, inFunc)
  1134  	})
  1135  }
  1136  
  1137  func (c *compiler) createVarBindings(v *ast.VariableDeclaration, inFunc bool) {
  1138  	for _, item := range v.List {
  1139  		c.createVarBinding(item.Target, inFunc)
  1140  	}
  1141  }
  1142  
  1143  func (c *compiler) createLexicalIdBinding(name unistring.String, isConst bool, offset int) *binding {
  1144  	if name == "let" {
  1145  		c.throwSyntaxError(offset, "let is disallowed as a lexically bound name")
  1146  	}
  1147  	if c.scope.strict {
  1148  		c.checkIdentifierLName(name, offset)
  1149  		c.checkIdentifierName(name, offset)
  1150  	}
  1151  	b, _ := c.scope.bindNameLexical(name, true, offset)
  1152  	if isConst {
  1153  		b.isConst, b.isStrict = true, true
  1154  	}
  1155  	return b
  1156  }
  1157  
  1158  func (c *compiler) createLexicalIdBindingFuncBody(name unistring.String, isConst bool, offset int, calleeBinding *binding) *binding {
  1159  	if name == "let" {
  1160  		c.throwSyntaxError(offset, "let is disallowed as a lexically bound name")
  1161  	}
  1162  	if c.scope.strict {
  1163  		c.checkIdentifierLName(name, offset)
  1164  		c.checkIdentifierName(name, offset)
  1165  	}
  1166  	paramScope := c.scope.outer
  1167  	parentBinding := paramScope.boundNames[name]
  1168  	if parentBinding != nil {
  1169  		if parentBinding != calleeBinding && (name != "arguments" || !paramScope.argsNeeded) {
  1170  			c.throwSyntaxError(offset, "Identifier '%s' has already been declared", name)
  1171  		}
  1172  	}
  1173  	b, _ := c.scope.bindNameLexical(name, true, offset)
  1174  	if isConst {
  1175  		b.isConst, b.isStrict = true, true
  1176  	}
  1177  	return b
  1178  }
  1179  
  1180  func (c *compiler) createLexicalBinding(target ast.Expression, isConst bool) {
  1181  	c.createBindings(target, func(name unistring.String, offset int) {
  1182  		c.createLexicalIdBinding(name, isConst, offset)
  1183  	})
  1184  }
  1185  
  1186  func (c *compiler) createLexicalBindings(lex *ast.LexicalDeclaration) {
  1187  	for _, d := range lex.List {
  1188  		c.createLexicalBinding(d.Target, lex.Token == token.CONST)
  1189  	}
  1190  }
  1191  
  1192  func (c *compiler) compileLexicalDeclarations(list []ast.Statement, scopeDeclared bool) bool {
  1193  	for _, st := range list {
  1194  		if lex, ok := st.(*ast.LexicalDeclaration); ok {
  1195  			if !scopeDeclared {
  1196  				c.newBlockScope()
  1197  				scopeDeclared = true
  1198  			}
  1199  			c.createLexicalBindings(lex)
  1200  		} else if cls, ok := st.(*ast.ClassDeclaration); ok {
  1201  			if !scopeDeclared {
  1202  				c.newBlockScope()
  1203  				scopeDeclared = true
  1204  			}
  1205  			c.createLexicalIdBinding(cls.Class.Name.Name, false, int(cls.Class.Name.Idx)-1)
  1206  		}
  1207  	}
  1208  	return scopeDeclared
  1209  }
  1210  
  1211  func (c *compiler) compileLexicalDeclarationsFuncBody(list []ast.Statement, calleeBinding *binding) {
  1212  	for _, st := range list {
  1213  		if lex, ok := st.(*ast.LexicalDeclaration); ok {
  1214  			isConst := lex.Token == token.CONST
  1215  			for _, d := range lex.List {
  1216  				c.createBindings(d.Target, func(name unistring.String, offset int) {
  1217  					c.createLexicalIdBindingFuncBody(name, isConst, offset, calleeBinding)
  1218  				})
  1219  			}
  1220  		} else if cls, ok := st.(*ast.ClassDeclaration); ok {
  1221  			c.createLexicalIdBindingFuncBody(cls.Class.Name.Name, false, int(cls.Class.Name.Idx)-1, calleeBinding)
  1222  		}
  1223  	}
  1224  }
  1225  
  1226  func (c *compiler) compileFunction(v *ast.FunctionDeclaration) {
  1227  	name := v.Function.Name.Name
  1228  	b := c.scope.boundNames[name]
  1229  	if b == nil || b.isVar {
  1230  		e := &compiledIdentifierExpr{
  1231  			name: v.Function.Name.Name,
  1232  		}
  1233  		e.init(c, v.Function.Idx0())
  1234  		e.emitSetter(c.compileFunctionLiteral(v.Function, false), false)
  1235  	} else {
  1236  		c.compileFunctionLiteral(v.Function, false).emitGetter(true)
  1237  		b.emitInitP()
  1238  	}
  1239  }
  1240  
  1241  func (c *compiler) compileStandaloneFunctionDecl(v *ast.FunctionDeclaration) {
  1242  	if v.Function.Async {
  1243  		c.throwSyntaxError(int(v.Idx0())-1, "Async functions can only be declared at top level or inside a block.")
  1244  	}
  1245  	if v.Function.Generator {
  1246  		c.throwSyntaxError(int(v.Idx0())-1, "Generators can only be declared at top level or inside a block.")
  1247  	}
  1248  	if c.scope.strict {
  1249  		c.throwSyntaxError(int(v.Idx0())-1, "In strict mode code, functions can only be declared at top level or inside a block.")
  1250  	}
  1251  	c.throwSyntaxError(int(v.Idx0())-1, "In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement.")
  1252  }
  1253  
  1254  func (c *compiler) emit(instructions ...instruction) {
  1255  	c.p.code = append(c.p.code, instructions...)
  1256  }
  1257  
  1258  func (c *compiler) throwSyntaxError(offset int, format string, args ...interface{}) {
  1259  	panic(&CompilerSyntaxError{
  1260  		CompilerError: CompilerError{
  1261  			File:    c.p.src,
  1262  			Offset:  offset,
  1263  			Message: fmt.Sprintf(format, args...),
  1264  		},
  1265  	})
  1266  }
  1267  
  1268  func (c *compiler) isStrict(list []ast.Statement) *ast.StringLiteral {
  1269  	for _, st := range list {
  1270  		if st, ok := st.(*ast.ExpressionStatement); ok {
  1271  			if e, ok := st.Expression.(*ast.StringLiteral); ok {
  1272  				if e.Literal == `"use strict"` || e.Literal == `'use strict'` {
  1273  					return e
  1274  				}
  1275  			} else {
  1276  				break
  1277  			}
  1278  		} else {
  1279  			break
  1280  		}
  1281  	}
  1282  	return nil
  1283  }
  1284  
  1285  func (c *compiler) isStrictStatement(s ast.Statement) *ast.StringLiteral {
  1286  	if s, ok := s.(*ast.BlockStatement); ok {
  1287  		return c.isStrict(s.List)
  1288  	}
  1289  	return nil
  1290  }
  1291  
  1292  func (c *compiler) checkIdentifierName(name unistring.String, offset int) {
  1293  	switch name {
  1294  	case "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield":
  1295  		c.throwSyntaxError(offset, "Unexpected strict mode reserved word")
  1296  	}
  1297  }
  1298  
  1299  func (c *compiler) checkIdentifierLName(name unistring.String, offset int) {
  1300  	switch name {
  1301  	case "eval", "arguments":
  1302  		c.throwSyntaxError(offset, "Assignment to eval or arguments is not allowed in strict mode")
  1303  	}
  1304  }
  1305  
  1306  // Enter a 'dummy' compilation mode. Any code produced after this method is called will be discarded after
  1307  // leaveFunc is called with no additional side effects. This is useful for compiling code inside a
  1308  // constant falsy condition 'if' branch or a loop (i.e 'if (false) { ... } or while (false) { ... }).
  1309  // Such code should not be included in the final compilation result as it's never called, but it must
  1310  // still produce compilation errors if there are any.
  1311  // TODO: make sure variable lookups do not de-optimise parent scopes
  1312  func (c *compiler) enterDummyMode() (leaveFunc func()) {
  1313  	savedBlock, savedProgram := c.block, c.p
  1314  	if savedBlock != nil {
  1315  		c.block = &block{
  1316  			typ:      savedBlock.typ,
  1317  			label:    savedBlock.label,
  1318  			outer:    savedBlock.outer,
  1319  			breaking: savedBlock.breaking,
  1320  		}
  1321  	}
  1322  	c.p = &Program{
  1323  		src: c.p.src,
  1324  	}
  1325  	c.newScope()
  1326  	return func() {
  1327  		c.block, c.p = savedBlock, savedProgram
  1328  		c.popScope()
  1329  	}
  1330  }
  1331  
  1332  func (c *compiler) compileStatementDummy(statement ast.Statement) {
  1333  	leave := c.enterDummyMode()
  1334  	c.compileStatement(statement, false)
  1335  	leave()
  1336  }
  1337  
  1338  func (c *compiler) assert(cond bool, offset int, msg string, args ...interface{}) {
  1339  	if !cond {
  1340  		c.throwSyntaxError(offset, "Compiler bug: "+msg, args...)
  1341  	}
  1342  }
  1343  
  1344  func privateIdString(desc unistring.String) unistring.String {
  1345  	return asciiString("#").Concat(stringValueFromRaw(desc)).string()
  1346  }
  1347  
  1348  type privateName struct {
  1349  	idx                  int
  1350  	isStatic             bool
  1351  	isMethod             bool
  1352  	hasGetter, hasSetter bool
  1353  }
  1354  
  1355  type resolvedPrivateName struct {
  1356  	name     unistring.String
  1357  	idx      uint32
  1358  	level    uint8
  1359  	isStatic bool
  1360  	isMethod bool
  1361  }
  1362  
  1363  func (r *resolvedPrivateName) string() unistring.String {
  1364  	return privateIdString(r.name)
  1365  }
  1366  
  1367  type privateEnvRegistry struct {
  1368  	fields, methods []unistring.String
  1369  }
  1370  
  1371  type classScope struct {
  1372  	c            *compiler
  1373  	privateNames map[unistring.String]*privateName
  1374  
  1375  	instanceEnv, staticEnv privateEnvRegistry
  1376  
  1377  	outer *classScope
  1378  }
  1379  
  1380  func (r *privateEnvRegistry) createPrivateMethodId(name unistring.String) int {
  1381  	r.methods = append(r.methods, name)
  1382  	return len(r.methods) - 1
  1383  }
  1384  
  1385  func (r *privateEnvRegistry) createPrivateFieldId(name unistring.String) int {
  1386  	r.fields = append(r.fields, name)
  1387  	return len(r.fields) - 1
  1388  }
  1389  
  1390  func (s *classScope) declarePrivateId(name unistring.String, kind ast.PropertyKind, isStatic bool, offset int) {
  1391  	pn := s.privateNames[name]
  1392  	if pn != nil {
  1393  		if pn.isStatic == isStatic {
  1394  			switch kind {
  1395  			case ast.PropertyKindGet:
  1396  				if pn.hasSetter && !pn.hasGetter {
  1397  					pn.hasGetter = true
  1398  					return
  1399  				}
  1400  			case ast.PropertyKindSet:
  1401  				if pn.hasGetter && !pn.hasSetter {
  1402  					pn.hasSetter = true
  1403  					return
  1404  				}
  1405  			}
  1406  		}
  1407  		s.c.throwSyntaxError(offset, "Identifier '#%s' has already been declared", name)
  1408  		panic("unreachable")
  1409  	}
  1410  	var env *privateEnvRegistry
  1411  	if isStatic {
  1412  		env = &s.staticEnv
  1413  	} else {
  1414  		env = &s.instanceEnv
  1415  	}
  1416  
  1417  	pn = &privateName{
  1418  		isStatic:  isStatic,
  1419  		hasGetter: kind == ast.PropertyKindGet,
  1420  		hasSetter: kind == ast.PropertyKindSet,
  1421  	}
  1422  	if kind != ast.PropertyKindValue {
  1423  		pn.idx = env.createPrivateMethodId(name)
  1424  		pn.isMethod = true
  1425  	} else {
  1426  		pn.idx = env.createPrivateFieldId(name)
  1427  	}
  1428  
  1429  	if s.privateNames == nil {
  1430  		s.privateNames = make(map[unistring.String]*privateName)
  1431  	}
  1432  	s.privateNames[name] = pn
  1433  }
  1434  
  1435  func (s *classScope) getDeclaredPrivateId(name unistring.String) *privateName {
  1436  	if n := s.privateNames[name]; n != nil {
  1437  		return n
  1438  	}
  1439  	s.c.assert(false, 0, "getDeclaredPrivateId() for undeclared id")
  1440  	panic("unreachable")
  1441  }
  1442  
  1443  func (c *compiler) resolvePrivateName(name unistring.String, offset int) (*resolvedPrivateName, *privateId) {
  1444  	level := 0
  1445  	for s := c.classScope; s != nil; s = s.outer {
  1446  		if len(s.privateNames) > 0 {
  1447  			if pn := s.privateNames[name]; pn != nil {
  1448  				return &resolvedPrivateName{
  1449  					name:     name,
  1450  					idx:      uint32(pn.idx),
  1451  					level:    uint8(level),
  1452  					isStatic: pn.isStatic,
  1453  					isMethod: pn.isMethod,
  1454  				}, nil
  1455  			}
  1456  			level++
  1457  		}
  1458  	}
  1459  	if c.ctxVM != nil {
  1460  		for s := c.ctxVM.privEnv; s != nil; s = s.outer {
  1461  			if id := s.names[name]; id != nil {
  1462  				return nil, id
  1463  			}
  1464  		}
  1465  	}
  1466  	c.throwSyntaxError(offset, "Private field '#%s' must be declared in an enclosing class", name)
  1467  	panic("unreachable")
  1468  }
  1469  

View as plain text