github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/compiler_stmt.go (about)

     1  package goja
     2  
     3  import (
     4  	"github.com/nuvolaris/goja/ast"
     5  	"github.com/nuvolaris/goja/file"
     6  	"github.com/nuvolaris/goja/token"
     7  	"github.com/nuvolaris/goja/unistring"
     8  )
     9  
    10  func (c *compiler) compileStatement(v ast.Statement, needResult bool) {
    11  
    12  	switch v := v.(type) {
    13  	case *ast.BlockStatement:
    14  		c.compileBlockStatement(v, needResult)
    15  	case *ast.ExpressionStatement:
    16  		c.compileExpressionStatement(v, needResult)
    17  	case *ast.VariableStatement:
    18  		c.compileVariableStatement(v)
    19  	case *ast.LexicalDeclaration:
    20  		c.compileLexicalDeclaration(v)
    21  	case *ast.ReturnStatement:
    22  		c.compileReturnStatement(v)
    23  	case *ast.IfStatement:
    24  		c.compileIfStatement(v, needResult)
    25  	case *ast.DoWhileStatement:
    26  		c.compileDoWhileStatement(v, needResult)
    27  	case *ast.ForStatement:
    28  		c.compileForStatement(v, needResult)
    29  	case *ast.ForInStatement:
    30  		c.compileForInStatement(v, needResult)
    31  	case *ast.ForOfStatement:
    32  		c.compileForOfStatement(v, needResult)
    33  	case *ast.WhileStatement:
    34  		c.compileWhileStatement(v, needResult)
    35  	case *ast.BranchStatement:
    36  		c.compileBranchStatement(v)
    37  	case *ast.TryStatement:
    38  		c.compileTryStatement(v, needResult)
    39  	case *ast.ThrowStatement:
    40  		c.compileThrowStatement(v)
    41  	case *ast.SwitchStatement:
    42  		c.compileSwitchStatement(v, needResult)
    43  	case *ast.LabelledStatement:
    44  		c.compileLabeledStatement(v, needResult)
    45  	case *ast.EmptyStatement:
    46  		c.compileEmptyStatement(needResult)
    47  	case *ast.FunctionDeclaration:
    48  		c.compileStandaloneFunctionDecl(v)
    49  		// note functions inside blocks are hoisted to the top of the block and are compiled using compileFunctions()
    50  	case *ast.ClassDeclaration:
    51  		c.compileClassDeclaration(v)
    52  	case *ast.WithStatement:
    53  		c.compileWithStatement(v, needResult)
    54  	case *ast.DebuggerStatement:
    55  	default:
    56  		c.assert(false, int(v.Idx0())-1, "Unknown statement type: %T", v)
    57  		panic("unreachable")
    58  	}
    59  }
    60  
    61  func (c *compiler) compileLabeledStatement(v *ast.LabelledStatement, needResult bool) {
    62  	label := v.Label.Name
    63  	if c.scope.strict {
    64  		c.checkIdentifierName(label, int(v.Label.Idx)-1)
    65  	}
    66  	for b := c.block; b != nil; b = b.outer {
    67  		if b.label == label {
    68  			c.throwSyntaxError(int(v.Label.Idx-1), "Label '%s' has already been declared", label)
    69  		}
    70  	}
    71  	switch s := v.Statement.(type) {
    72  	case *ast.ForInStatement:
    73  		c.compileLabeledForInStatement(s, needResult, label)
    74  	case *ast.ForOfStatement:
    75  		c.compileLabeledForOfStatement(s, needResult, label)
    76  	case *ast.ForStatement:
    77  		c.compileLabeledForStatement(s, needResult, label)
    78  	case *ast.WhileStatement:
    79  		c.compileLabeledWhileStatement(s, needResult, label)
    80  	case *ast.DoWhileStatement:
    81  		c.compileLabeledDoWhileStatement(s, needResult, label)
    82  	default:
    83  		c.compileGenericLabeledStatement(s, needResult, label)
    84  	}
    85  }
    86  
    87  func (c *compiler) updateEnterBlock(enter *enterBlock) {
    88  	scope := c.scope
    89  	stashSize, stackSize := 0, 0
    90  	if scope.dynLookup {
    91  		stashSize = len(scope.bindings)
    92  		enter.names = scope.makeNamesMap()
    93  	} else {
    94  		for _, b := range scope.bindings {
    95  			if b.inStash {
    96  				stashSize++
    97  			} else {
    98  				stackSize++
    99  			}
   100  		}
   101  	}
   102  	enter.stashSize, enter.stackSize = uint32(stashSize), uint32(stackSize)
   103  }
   104  
   105  func (c *compiler) compileTryStatement(v *ast.TryStatement, needResult bool) {
   106  	c.block = &block{
   107  		typ:   blockTry,
   108  		outer: c.block,
   109  	}
   110  	var lp int
   111  	var bodyNeedResult bool
   112  	var finallyBreaking *block
   113  	if v.Finally != nil {
   114  		lp, finallyBreaking = c.scanStatements(v.Finally.List)
   115  	}
   116  	if finallyBreaking != nil {
   117  		c.block.breaking = finallyBreaking
   118  		if lp == -1 {
   119  			bodyNeedResult = finallyBreaking.needResult
   120  		}
   121  	} else {
   122  		bodyNeedResult = needResult
   123  	}
   124  	lbl := len(c.p.code)
   125  	c.emit(nil)
   126  	if needResult {
   127  		c.emit(clearResult)
   128  	}
   129  	c.compileBlockStatement(v.Body, bodyNeedResult)
   130  	var catchOffset int
   131  	if v.Catch != nil {
   132  		lbl2 := len(c.p.code) // jump over the catch block
   133  		c.emit(nil)
   134  		catchOffset = len(c.p.code) - lbl
   135  		if v.Catch.Parameter != nil {
   136  			c.block = &block{
   137  				typ:   blockScope,
   138  				outer: c.block,
   139  			}
   140  			c.newBlockScope()
   141  			list := v.Catch.Body.List
   142  			funcs := c.extractFunctions(list)
   143  			if _, ok := v.Catch.Parameter.(ast.Pattern); ok {
   144  				// add anonymous binding for the catch parameter, note it must be first
   145  				c.scope.addBinding(int(v.Catch.Idx0()) - 1)
   146  			}
   147  			c.createBindings(v.Catch.Parameter, func(name unistring.String, offset int) {
   148  				if c.scope.strict {
   149  					switch name {
   150  					case "arguments", "eval":
   151  						c.throwSyntaxError(offset, "Catch variable may not be eval or arguments in strict mode")
   152  					}
   153  				}
   154  				c.scope.bindNameLexical(name, true, offset)
   155  			})
   156  			enter := &enterBlock{}
   157  			c.emit(enter)
   158  			if pattern, ok := v.Catch.Parameter.(ast.Pattern); ok {
   159  				c.scope.bindings[0].emitGet()
   160  				c.emitPattern(pattern, func(target, init compiledExpr) {
   161  					c.emitPatternLexicalAssign(target, init)
   162  				}, false)
   163  			}
   164  			for _, decl := range funcs {
   165  				c.scope.bindNameLexical(decl.Function.Name.Name, true, int(decl.Function.Name.Idx1())-1)
   166  			}
   167  			c.compileLexicalDeclarations(list, true)
   168  			c.compileFunctions(funcs)
   169  			c.compileStatements(list, bodyNeedResult)
   170  			c.leaveScopeBlock(enter)
   171  			if c.scope.dynLookup || c.scope.bindings[0].inStash {
   172  				c.p.code[lbl+catchOffset] = &enterCatchBlock{
   173  					names:     enter.names,
   174  					stashSize: enter.stashSize,
   175  					stackSize: enter.stackSize,
   176  				}
   177  			} else {
   178  				enter.stackSize--
   179  			}
   180  			c.popScope()
   181  		} else {
   182  			c.emit(pop)
   183  			c.compileBlockStatement(v.Catch.Body, bodyNeedResult)
   184  		}
   185  		c.p.code[lbl2] = jump(len(c.p.code) - lbl2)
   186  	}
   187  	var finallyOffset int
   188  	if v.Finally != nil {
   189  		c.emit(enterFinally{})
   190  		finallyOffset = len(c.p.code) - lbl // finallyOffset should not include enterFinally
   191  		if bodyNeedResult && finallyBreaking != nil && lp == -1 {
   192  			c.emit(clearResult)
   193  		}
   194  		c.compileBlockStatement(v.Finally, false)
   195  		c.emit(leaveFinally{})
   196  	} else {
   197  		c.emit(leaveTry{})
   198  	}
   199  	c.p.code[lbl] = try{catchOffset: int32(catchOffset), finallyOffset: int32(finallyOffset)}
   200  	c.leaveBlock()
   201  }
   202  
   203  func (c *compiler) addSrcMap(node ast.Node) {
   204  	c.p.addSrcMap(int(node.Idx0()) - 1)
   205  }
   206  
   207  func (c *compiler) compileThrowStatement(v *ast.ThrowStatement) {
   208  	c.compileExpression(v.Argument).emitGetter(true)
   209  	c.addSrcMap(v)
   210  	c.emit(throw)
   211  }
   212  
   213  func (c *compiler) compileDoWhileStatement(v *ast.DoWhileStatement, needResult bool) {
   214  	c.compileLabeledDoWhileStatement(v, needResult, "")
   215  }
   216  
   217  func (c *compiler) compileLabeledDoWhileStatement(v *ast.DoWhileStatement, needResult bool, label unistring.String) {
   218  	c.block = &block{
   219  		typ:        blockLoop,
   220  		outer:      c.block,
   221  		label:      label,
   222  		needResult: needResult,
   223  	}
   224  
   225  	start := len(c.p.code)
   226  	c.compileStatement(v.Body, needResult)
   227  	c.block.cont = len(c.p.code)
   228  	c.emitExpr(c.compileExpression(v.Test), true)
   229  	c.emit(jeq(start - len(c.p.code)))
   230  	c.leaveBlock()
   231  }
   232  
   233  func (c *compiler) compileForStatement(v *ast.ForStatement, needResult bool) {
   234  	c.compileLabeledForStatement(v, needResult, "")
   235  }
   236  
   237  func (c *compiler) compileForHeadLexDecl(decl *ast.LexicalDeclaration, needResult bool) *enterBlock {
   238  	c.block = &block{
   239  		typ:        blockIterScope,
   240  		outer:      c.block,
   241  		needResult: needResult,
   242  	}
   243  
   244  	c.newBlockScope()
   245  	enterIterBlock := &enterBlock{}
   246  	c.emit(enterIterBlock)
   247  	c.createLexicalBindings(decl)
   248  	c.compileLexicalDeclaration(decl)
   249  	return enterIterBlock
   250  }
   251  
   252  func (c *compiler) compileLabeledForStatement(v *ast.ForStatement, needResult bool, label unistring.String) {
   253  	loopBlock := &block{
   254  		typ:        blockLoop,
   255  		outer:      c.block,
   256  		label:      label,
   257  		needResult: needResult,
   258  	}
   259  	c.block = loopBlock
   260  
   261  	var enterIterBlock *enterBlock
   262  	switch init := v.Initializer.(type) {
   263  	case nil:
   264  		// no-op
   265  	case *ast.ForLoopInitializerLexicalDecl:
   266  		enterIterBlock = c.compileForHeadLexDecl(&init.LexicalDeclaration, needResult)
   267  	case *ast.ForLoopInitializerVarDeclList:
   268  		for _, expr := range init.List {
   269  			c.compileVarBinding(expr)
   270  		}
   271  	case *ast.ForLoopInitializerExpression:
   272  		c.compileExpression(init.Expression).emitGetter(false)
   273  	default:
   274  		c.assert(false, int(v.For)-1, "Unsupported for loop initializer: %T", init)
   275  		panic("unreachable")
   276  	}
   277  
   278  	if needResult {
   279  		c.emit(clearResult) // initial result
   280  	}
   281  
   282  	if enterIterBlock != nil {
   283  		c.emit(jump(1))
   284  	}
   285  
   286  	start := len(c.p.code)
   287  	var j int
   288  	testConst := false
   289  	if v.Test != nil {
   290  		expr := c.compileExpression(v.Test)
   291  		if expr.constant() {
   292  			r, ex := c.evalConst(expr)
   293  			if ex == nil {
   294  				if r.ToBoolean() {
   295  					testConst = true
   296  				} else {
   297  					leave := c.enterDummyMode()
   298  					c.compileStatement(v.Body, false)
   299  					if v.Update != nil {
   300  						c.compileExpression(v.Update).emitGetter(false)
   301  					}
   302  					leave()
   303  					goto end
   304  				}
   305  			} else {
   306  				expr.addSrcMap()
   307  				c.emitThrow(ex.val)
   308  				goto end
   309  			}
   310  		} else {
   311  			expr.emitGetter(true)
   312  			j = len(c.p.code)
   313  			c.emit(nil)
   314  		}
   315  	}
   316  	if needResult {
   317  		c.emit(clearResult)
   318  	}
   319  	c.compileStatement(v.Body, needResult)
   320  	loopBlock.cont = len(c.p.code)
   321  	if enterIterBlock != nil {
   322  		c.emit(jump(1))
   323  	}
   324  	if v.Update != nil {
   325  		c.compileExpression(v.Update).emitGetter(false)
   326  	}
   327  	if enterIterBlock != nil {
   328  		if c.scope.needStash || c.scope.isDynamic() {
   329  			c.p.code[start-1] = copyStash{}
   330  			c.p.code[loopBlock.cont] = copyStash{}
   331  		} else {
   332  			if l := len(c.p.code); l > loopBlock.cont {
   333  				loopBlock.cont++
   334  			} else {
   335  				c.p.code = c.p.code[:l-1]
   336  			}
   337  		}
   338  	}
   339  	c.emit(jump(start - len(c.p.code)))
   340  	if v.Test != nil {
   341  		if !testConst {
   342  			c.p.code[j] = jne(len(c.p.code) - j)
   343  		}
   344  	}
   345  end:
   346  	if enterIterBlock != nil {
   347  		c.leaveScopeBlock(enterIterBlock)
   348  		c.popScope()
   349  	}
   350  	c.leaveBlock()
   351  }
   352  
   353  func (c *compiler) compileForInStatement(v *ast.ForInStatement, needResult bool) {
   354  	c.compileLabeledForInStatement(v, needResult, "")
   355  }
   356  
   357  func (c *compiler) compileForInto(into ast.ForInto, needResult bool) (enter *enterBlock) {
   358  	switch into := into.(type) {
   359  	case *ast.ForIntoExpression:
   360  		c.compileExpression(into.Expression).emitSetter(&c.enumGetExpr, false)
   361  	case *ast.ForIntoVar:
   362  		if c.scope.strict && into.Binding.Initializer != nil {
   363  			c.throwSyntaxError(int(into.Binding.Initializer.Idx0())-1, "for-in loop variable declaration may not have an initializer.")
   364  		}
   365  		switch target := into.Binding.Target.(type) {
   366  		case *ast.Identifier:
   367  			c.compileIdentifierExpression(target).emitSetter(&c.enumGetExpr, false)
   368  		case ast.Pattern:
   369  			c.emit(enumGet)
   370  			c.emitPattern(target, c.emitPatternVarAssign, false)
   371  		default:
   372  			c.throwSyntaxError(int(target.Idx0()-1), "unsupported for-in var target: %T", target)
   373  		}
   374  	case *ast.ForDeclaration:
   375  
   376  		c.block = &block{
   377  			typ:        blockIterScope,
   378  			outer:      c.block,
   379  			needResult: needResult,
   380  		}
   381  
   382  		c.newBlockScope()
   383  		enter = &enterBlock{}
   384  		c.emit(enter)
   385  		switch target := into.Target.(type) {
   386  		case *ast.Identifier:
   387  			b := c.createLexicalIdBinding(target.Name, into.IsConst, int(into.Idx)-1)
   388  			c.emit(enumGet)
   389  			b.emitInitP()
   390  		case ast.Pattern:
   391  			c.createLexicalBinding(target, into.IsConst)
   392  			c.emit(enumGet)
   393  			c.emitPattern(target, func(target, init compiledExpr) {
   394  				c.emitPatternLexicalAssign(target, init)
   395  			}, false)
   396  		default:
   397  			c.assert(false, int(into.Idx)-1, "Unsupported ForBinding: %T", into.Target)
   398  		}
   399  	default:
   400  		c.assert(false, int(into.Idx0())-1, "Unsupported for-into: %T", into)
   401  		panic("unreachable")
   402  	}
   403  
   404  	return
   405  }
   406  
   407  func (c *compiler) compileLabeledForInOfStatement(into ast.ForInto, source ast.Expression, body ast.Statement, iter, needResult bool, label unistring.String) {
   408  	c.block = &block{
   409  		typ:        blockLoopEnum,
   410  		outer:      c.block,
   411  		label:      label,
   412  		needResult: needResult,
   413  	}
   414  	enterPos := -1
   415  	if forDecl, ok := into.(*ast.ForDeclaration); ok {
   416  		c.block = &block{
   417  			typ:        blockScope,
   418  			outer:      c.block,
   419  			needResult: false,
   420  		}
   421  		c.newBlockScope()
   422  		enterPos = len(c.p.code)
   423  		c.emit(jump(1))
   424  		c.createLexicalBinding(forDecl.Target, forDecl.IsConst)
   425  	}
   426  	c.compileExpression(source).emitGetter(true)
   427  	if enterPos != -1 {
   428  		s := c.scope
   429  		used := len(c.block.breaks) > 0 || s.isDynamic()
   430  		if !used {
   431  			for _, b := range s.bindings {
   432  				if b.useCount() > 0 {
   433  					used = true
   434  					break
   435  				}
   436  			}
   437  		}
   438  		if used {
   439  			// We need the stack untouched because it contains the source.
   440  			// This is not the most optimal way, but it's an edge case, hopefully quite rare.
   441  			for _, b := range s.bindings {
   442  				b.moveToStash()
   443  			}
   444  			enter := &enterBlock{}
   445  			c.p.code[enterPos] = enter
   446  			c.leaveScopeBlock(enter)
   447  		} else {
   448  			c.block = c.block.outer
   449  		}
   450  		c.popScope()
   451  	}
   452  	if iter {
   453  		c.emit(iterateP)
   454  	} else {
   455  		c.emit(enumerate)
   456  	}
   457  	if needResult {
   458  		c.emit(clearResult)
   459  	}
   460  	start := len(c.p.code)
   461  	c.block.cont = start
   462  	c.emit(nil)
   463  	enterIterBlock := c.compileForInto(into, needResult)
   464  	if needResult {
   465  		c.emit(clearResult)
   466  	}
   467  	c.compileStatement(body, needResult)
   468  	if enterIterBlock != nil {
   469  		c.leaveScopeBlock(enterIterBlock)
   470  		c.popScope()
   471  	}
   472  	c.emit(jump(start - len(c.p.code)))
   473  	if iter {
   474  		c.p.code[start] = iterNext(len(c.p.code) - start)
   475  	} else {
   476  		c.p.code[start] = enumNext(len(c.p.code) - start)
   477  	}
   478  	c.emit(enumPop, jump(2))
   479  	c.leaveBlock()
   480  	c.emit(enumPopClose)
   481  }
   482  
   483  func (c *compiler) compileLabeledForInStatement(v *ast.ForInStatement, needResult bool, label unistring.String) {
   484  	c.compileLabeledForInOfStatement(v.Into, v.Source, v.Body, false, needResult, label)
   485  }
   486  
   487  func (c *compiler) compileForOfStatement(v *ast.ForOfStatement, needResult bool) {
   488  	c.compileLabeledForOfStatement(v, needResult, "")
   489  }
   490  
   491  func (c *compiler) compileLabeledForOfStatement(v *ast.ForOfStatement, needResult bool, label unistring.String) {
   492  	c.compileLabeledForInOfStatement(v.Into, v.Source, v.Body, true, needResult, label)
   493  }
   494  
   495  func (c *compiler) compileWhileStatement(v *ast.WhileStatement, needResult bool) {
   496  	c.compileLabeledWhileStatement(v, needResult, "")
   497  }
   498  
   499  func (c *compiler) compileLabeledWhileStatement(v *ast.WhileStatement, needResult bool, label unistring.String) {
   500  	c.block = &block{
   501  		typ:        blockLoop,
   502  		outer:      c.block,
   503  		label:      label,
   504  		needResult: needResult,
   505  	}
   506  
   507  	if needResult {
   508  		c.emit(clearResult)
   509  	}
   510  	start := len(c.p.code)
   511  	c.block.cont = start
   512  	expr := c.compileExpression(v.Test)
   513  	testTrue := false
   514  	var j int
   515  	if expr.constant() {
   516  		if t, ex := c.evalConst(expr); ex == nil {
   517  			if t.ToBoolean() {
   518  				testTrue = true
   519  			} else {
   520  				c.compileStatementDummy(v.Body)
   521  				goto end
   522  			}
   523  		} else {
   524  			c.emitThrow(ex.val)
   525  			goto end
   526  		}
   527  	} else {
   528  		expr.emitGetter(true)
   529  		j = len(c.p.code)
   530  		c.emit(nil)
   531  	}
   532  	if needResult {
   533  		c.emit(clearResult)
   534  	}
   535  	c.compileStatement(v.Body, needResult)
   536  	c.emit(jump(start - len(c.p.code)))
   537  	if !testTrue {
   538  		c.p.code[j] = jne(len(c.p.code) - j)
   539  	}
   540  end:
   541  	c.leaveBlock()
   542  }
   543  
   544  func (c *compiler) compileEmptyStatement(needResult bool) {
   545  	if needResult {
   546  		c.emit(clearResult)
   547  	}
   548  }
   549  
   550  func (c *compiler) compileBranchStatement(v *ast.BranchStatement) {
   551  	switch v.Token {
   552  	case token.BREAK:
   553  		c.compileBreak(v.Label, v.Idx)
   554  	case token.CONTINUE:
   555  		c.compileContinue(v.Label, v.Idx)
   556  	default:
   557  		c.assert(false, int(v.Idx0())-1, "Unknown branch statement token: %s", v.Token.String())
   558  		panic("unreachable")
   559  	}
   560  }
   561  
   562  func (c *compiler) findBranchBlock(st *ast.BranchStatement) *block {
   563  	switch st.Token {
   564  	case token.BREAK:
   565  		return c.findBreakBlock(st.Label, true)
   566  	case token.CONTINUE:
   567  		return c.findBreakBlock(st.Label, false)
   568  	}
   569  	return nil
   570  }
   571  
   572  func (c *compiler) findBreakBlock(label *ast.Identifier, isBreak bool) (res *block) {
   573  	if label != nil {
   574  		var found *block
   575  		for b := c.block; b != nil; b = b.outer {
   576  			if res == nil {
   577  				if bb := b.breaking; bb != nil {
   578  					res = bb
   579  					if isBreak {
   580  						return
   581  					}
   582  				}
   583  			}
   584  			if b.label == label.Name {
   585  				found = b
   586  				break
   587  			}
   588  		}
   589  		if !isBreak && found != nil && found.typ != blockLoop && found.typ != blockLoopEnum {
   590  			c.throwSyntaxError(int(label.Idx)-1, "Illegal continue statement: '%s' does not denote an iteration statement", label.Name)
   591  		}
   592  		if res == nil {
   593  			res = found
   594  		}
   595  	} else {
   596  		// find the nearest loop or switch (if break)
   597  	L:
   598  		for b := c.block; b != nil; b = b.outer {
   599  			if bb := b.breaking; bb != nil {
   600  				return bb
   601  			}
   602  			switch b.typ {
   603  			case blockLoop, blockLoopEnum:
   604  				res = b
   605  				break L
   606  			case blockSwitch:
   607  				if isBreak {
   608  					res = b
   609  					break L
   610  				}
   611  			}
   612  		}
   613  	}
   614  
   615  	return
   616  }
   617  
   618  func (c *compiler) emitBlockExitCode(label *ast.Identifier, idx file.Idx, isBreak bool) *block {
   619  	block := c.findBreakBlock(label, isBreak)
   620  	if block == nil {
   621  		c.throwSyntaxError(int(idx)-1, "Could not find block")
   622  		panic("unreachable")
   623  	}
   624  	contForLoop := !isBreak && block.typ == blockLoop
   625  L:
   626  	for b := c.block; b != block; b = b.outer {
   627  		switch b.typ {
   628  		case blockIterScope:
   629  			// blockIterScope in 'for' loops is shared across iterations, so
   630  			// continue should not pop it.
   631  			if contForLoop && b.outer == block {
   632  				break L
   633  			}
   634  			fallthrough
   635  		case blockScope:
   636  			b.breaks = append(b.breaks, len(c.p.code))
   637  			c.emit(nil)
   638  		case blockTry:
   639  			c.emit(leaveTry{})
   640  		case blockWith:
   641  			c.emit(leaveWith)
   642  		case blockLoopEnum:
   643  			c.emit(enumPopClose)
   644  		}
   645  	}
   646  	return block
   647  }
   648  
   649  func (c *compiler) compileBreak(label *ast.Identifier, idx file.Idx) {
   650  	block := c.emitBlockExitCode(label, idx, true)
   651  	block.breaks = append(block.breaks, len(c.p.code))
   652  	c.emit(nil)
   653  }
   654  
   655  func (c *compiler) compileContinue(label *ast.Identifier, idx file.Idx) {
   656  	block := c.emitBlockExitCode(label, idx, false)
   657  	block.conts = append(block.conts, len(c.p.code))
   658  	c.emit(nil)
   659  }
   660  
   661  func (c *compiler) compileIfBody(s ast.Statement, needResult bool) {
   662  	if !c.scope.strict {
   663  		if s, ok := s.(*ast.FunctionDeclaration); ok && !s.Function.Async && !s.Function.Generator {
   664  			c.compileFunction(s)
   665  			if needResult {
   666  				c.emit(clearResult)
   667  			}
   668  			return
   669  		}
   670  	}
   671  	c.compileStatement(s, needResult)
   672  }
   673  
   674  func (c *compiler) compileIfBodyDummy(s ast.Statement) {
   675  	leave := c.enterDummyMode()
   676  	defer leave()
   677  	c.compileIfBody(s, false)
   678  }
   679  
   680  func (c *compiler) compileIfStatement(v *ast.IfStatement, needResult bool) {
   681  	test := c.compileExpression(v.Test)
   682  	if needResult {
   683  		c.emit(clearResult)
   684  	}
   685  	if test.constant() {
   686  		r, ex := c.evalConst(test)
   687  		if ex != nil {
   688  			test.addSrcMap()
   689  			c.emitThrow(ex.val)
   690  			return
   691  		}
   692  		if r.ToBoolean() {
   693  			c.compileIfBody(v.Consequent, needResult)
   694  			if v.Alternate != nil {
   695  				c.compileIfBodyDummy(v.Alternate)
   696  			}
   697  		} else {
   698  			c.compileIfBodyDummy(v.Consequent)
   699  			if v.Alternate != nil {
   700  				c.compileIfBody(v.Alternate, needResult)
   701  			} else {
   702  				if needResult {
   703  					c.emit(clearResult)
   704  				}
   705  			}
   706  		}
   707  		return
   708  	}
   709  	test.emitGetter(true)
   710  	jmp := len(c.p.code)
   711  	c.emit(nil)
   712  	c.compileIfBody(v.Consequent, needResult)
   713  	if v.Alternate != nil {
   714  		jmp1 := len(c.p.code)
   715  		c.emit(nil)
   716  		c.p.code[jmp] = jne(len(c.p.code) - jmp)
   717  		c.compileIfBody(v.Alternate, needResult)
   718  		c.p.code[jmp1] = jump(len(c.p.code) - jmp1)
   719  	} else {
   720  		if needResult {
   721  			c.emit(jump(2))
   722  			c.p.code[jmp] = jne(len(c.p.code) - jmp)
   723  			c.emit(clearResult)
   724  		} else {
   725  			c.p.code[jmp] = jne(len(c.p.code) - jmp)
   726  		}
   727  	}
   728  }
   729  
   730  func (c *compiler) compileReturnStatement(v *ast.ReturnStatement) {
   731  	if s := c.scope.nearestFunction(); s != nil && s.funcType == funcClsInit {
   732  		c.throwSyntaxError(int(v.Return)-1, "Illegal return statement")
   733  	}
   734  	if v.Argument != nil {
   735  		c.emitExpr(c.compileExpression(v.Argument), true)
   736  	} else {
   737  		c.emit(loadUndef)
   738  	}
   739  	for b := c.block; b != nil; b = b.outer {
   740  		switch b.typ {
   741  		case blockTry:
   742  			c.emit(leaveTry{})
   743  		case blockLoopEnum:
   744  			c.emit(enumPopClose)
   745  		}
   746  	}
   747  	if s := c.scope.nearestFunction(); s != nil && s.funcType == funcDerivedCtor {
   748  		b := s.boundNames[thisBindingName]
   749  		c.assert(b != nil, int(v.Return)-1, "Derived constructor, but no 'this' binding")
   750  		b.markAccessPoint()
   751  	}
   752  	c.emit(ret)
   753  }
   754  
   755  func (c *compiler) checkVarConflict(name unistring.String, offset int) {
   756  	for sc := c.scope; sc != nil; sc = sc.outer {
   757  		if b, exists := sc.boundNames[name]; exists && !b.isVar && !(b.isArg && sc != c.scope) {
   758  			c.throwSyntaxError(offset, "Identifier '%s' has already been declared", name)
   759  		}
   760  		if sc.isFunction() {
   761  			break
   762  		}
   763  	}
   764  }
   765  
   766  func (c *compiler) emitVarAssign(name unistring.String, offset int, init compiledExpr) {
   767  	c.checkVarConflict(name, offset)
   768  	if init != nil {
   769  		b, noDyn := c.scope.lookupName(name)
   770  		if noDyn {
   771  			c.emitNamedOrConst(init, name)
   772  			c.p.addSrcMap(offset)
   773  			b.emitInitP()
   774  		} else {
   775  			c.emitVarRef(name, offset, b)
   776  			c.emitNamedOrConst(init, name)
   777  			c.p.addSrcMap(offset)
   778  			c.emit(initValueP)
   779  		}
   780  	}
   781  }
   782  
   783  func (c *compiler) compileVarBinding(expr *ast.Binding) {
   784  	switch target := expr.Target.(type) {
   785  	case *ast.Identifier:
   786  		c.emitVarAssign(target.Name, int(target.Idx)-1, c.compileExpression(expr.Initializer))
   787  	case ast.Pattern:
   788  		c.compileExpression(expr.Initializer).emitGetter(true)
   789  		c.emitPattern(target, c.emitPatternVarAssign, false)
   790  	default:
   791  		c.throwSyntaxError(int(target.Idx0()-1), "unsupported variable binding target: %T", target)
   792  	}
   793  }
   794  
   795  func (c *compiler) emitLexicalAssign(name unistring.String, offset int, init compiledExpr) {
   796  	b := c.scope.boundNames[name]
   797  	c.assert(b != nil, offset, "Lexical declaration for an unbound name")
   798  	if init != nil {
   799  		c.emitNamedOrConst(init, name)
   800  		c.p.addSrcMap(offset)
   801  	} else {
   802  		if b.isConst {
   803  			c.throwSyntaxError(offset, "Missing initializer in const declaration")
   804  		}
   805  		c.emit(loadUndef)
   806  	}
   807  	b.emitInitP()
   808  }
   809  
   810  func (c *compiler) emitPatternVarAssign(target, init compiledExpr) {
   811  	id := target.(*compiledIdentifierExpr)
   812  	c.emitVarAssign(id.name, id.offset, init)
   813  }
   814  
   815  func (c *compiler) emitPatternLexicalAssign(target, init compiledExpr) {
   816  	id := target.(*compiledIdentifierExpr)
   817  	c.emitLexicalAssign(id.name, id.offset, init)
   818  }
   819  
   820  func (c *compiler) emitPatternAssign(target, init compiledExpr) {
   821  	if id, ok := target.(*compiledIdentifierExpr); ok {
   822  		b, noDyn := c.scope.lookupName(id.name)
   823  		if noDyn {
   824  			c.emitNamedOrConst(init, id.name)
   825  			b.emitSetP()
   826  		} else {
   827  			c.emitVarRef(id.name, id.offset, b)
   828  			c.emitNamedOrConst(init, id.name)
   829  			c.emit(putValueP)
   830  		}
   831  	} else {
   832  		target.emitRef()
   833  		c.emitExpr(init, true)
   834  		c.emit(putValueP)
   835  	}
   836  }
   837  
   838  func (c *compiler) compileLexicalBinding(expr *ast.Binding) {
   839  	switch target := expr.Target.(type) {
   840  	case *ast.Identifier:
   841  		c.emitLexicalAssign(target.Name, int(target.Idx)-1, c.compileExpression(expr.Initializer))
   842  	case ast.Pattern:
   843  		c.compileExpression(expr.Initializer).emitGetter(true)
   844  		c.emitPattern(target, func(target, init compiledExpr) {
   845  			c.emitPatternLexicalAssign(target, init)
   846  		}, false)
   847  	default:
   848  		c.throwSyntaxError(int(target.Idx0()-1), "unsupported lexical binding target: %T", target)
   849  	}
   850  }
   851  
   852  func (c *compiler) compileVariableStatement(v *ast.VariableStatement) {
   853  	for _, expr := range v.List {
   854  		c.compileVarBinding(expr)
   855  	}
   856  }
   857  
   858  func (c *compiler) compileLexicalDeclaration(v *ast.LexicalDeclaration) {
   859  	for _, e := range v.List {
   860  		c.compileLexicalBinding(e)
   861  	}
   862  }
   863  
   864  func (c *compiler) isEmptyResult(st ast.Statement) bool {
   865  	switch st := st.(type) {
   866  	case *ast.EmptyStatement, *ast.VariableStatement, *ast.LexicalDeclaration, *ast.FunctionDeclaration,
   867  		*ast.ClassDeclaration, *ast.BranchStatement, *ast.DebuggerStatement:
   868  		return true
   869  	case *ast.LabelledStatement:
   870  		return c.isEmptyResult(st.Statement)
   871  	case *ast.BlockStatement:
   872  		for _, s := range st.List {
   873  			if _, ok := s.(*ast.BranchStatement); ok {
   874  				return true
   875  			}
   876  			if !c.isEmptyResult(s) {
   877  				return false
   878  			}
   879  		}
   880  		return true
   881  	}
   882  	return false
   883  }
   884  
   885  func (c *compiler) scanStatements(list []ast.Statement) (lastProducingIdx int, breakingBlock *block) {
   886  	lastProducingIdx = -1
   887  	for i, st := range list {
   888  		if bs, ok := st.(*ast.BranchStatement); ok {
   889  			if blk := c.findBranchBlock(bs); blk != nil {
   890  				breakingBlock = blk
   891  			}
   892  			break
   893  		}
   894  		if !c.isEmptyResult(st) {
   895  			lastProducingIdx = i
   896  		}
   897  	}
   898  	return
   899  }
   900  
   901  func (c *compiler) compileStatementsNeedResult(list []ast.Statement, lastProducingIdx int) {
   902  	if lastProducingIdx >= 0 {
   903  		for _, st := range list[:lastProducingIdx] {
   904  			if _, ok := st.(*ast.FunctionDeclaration); ok {
   905  				continue
   906  			}
   907  			c.compileStatement(st, false)
   908  		}
   909  		c.compileStatement(list[lastProducingIdx], true)
   910  	}
   911  	var leave func()
   912  	defer func() {
   913  		if leave != nil {
   914  			leave()
   915  		}
   916  	}()
   917  	for _, st := range list[lastProducingIdx+1:] {
   918  		if _, ok := st.(*ast.FunctionDeclaration); ok {
   919  			continue
   920  		}
   921  		c.compileStatement(st, false)
   922  		if leave == nil {
   923  			if _, ok := st.(*ast.BranchStatement); ok {
   924  				leave = c.enterDummyMode()
   925  			}
   926  		}
   927  	}
   928  }
   929  
   930  func (c *compiler) compileStatements(list []ast.Statement, needResult bool) {
   931  	lastProducingIdx, blk := c.scanStatements(list)
   932  	if blk != nil {
   933  		needResult = blk.needResult
   934  	}
   935  	if needResult {
   936  		c.compileStatementsNeedResult(list, lastProducingIdx)
   937  		return
   938  	}
   939  	for _, st := range list {
   940  		if _, ok := st.(*ast.FunctionDeclaration); ok {
   941  			continue
   942  		}
   943  		c.compileStatement(st, false)
   944  	}
   945  }
   946  
   947  func (c *compiler) compileGenericLabeledStatement(v ast.Statement, needResult bool, label unistring.String) {
   948  	c.block = &block{
   949  		typ:        blockLabel,
   950  		outer:      c.block,
   951  		label:      label,
   952  		needResult: needResult,
   953  	}
   954  	c.compileStatement(v, needResult)
   955  	c.leaveBlock()
   956  }
   957  
   958  func (c *compiler) compileBlockStatement(v *ast.BlockStatement, needResult bool) {
   959  	var scopeDeclared bool
   960  	funcs := c.extractFunctions(v.List)
   961  	if len(funcs) > 0 {
   962  		c.newBlockScope()
   963  		scopeDeclared = true
   964  	}
   965  	c.createFunctionBindings(funcs)
   966  	scopeDeclared = c.compileLexicalDeclarations(v.List, scopeDeclared)
   967  
   968  	var enter *enterBlock
   969  	if scopeDeclared {
   970  		c.block = &block{
   971  			outer:      c.block,
   972  			typ:        blockScope,
   973  			needResult: needResult,
   974  		}
   975  		enter = &enterBlock{}
   976  		c.emit(enter)
   977  	}
   978  	c.compileFunctions(funcs)
   979  	c.compileStatements(v.List, needResult)
   980  	if scopeDeclared {
   981  		c.leaveScopeBlock(enter)
   982  		c.popScope()
   983  	}
   984  }
   985  
   986  func (c *compiler) compileExpressionStatement(v *ast.ExpressionStatement, needResult bool) {
   987  	c.emitExpr(c.compileExpression(v.Expression), needResult)
   988  	if needResult {
   989  		c.emit(saveResult)
   990  	}
   991  }
   992  
   993  func (c *compiler) compileWithStatement(v *ast.WithStatement, needResult bool) {
   994  	if c.scope.strict {
   995  		c.throwSyntaxError(int(v.With)-1, "Strict mode code may not include a with statement")
   996  		return
   997  	}
   998  	c.compileExpression(v.Object).emitGetter(true)
   999  	c.emit(enterWith)
  1000  	c.block = &block{
  1001  		outer:      c.block,
  1002  		typ:        blockWith,
  1003  		needResult: needResult,
  1004  	}
  1005  	c.newBlockScope()
  1006  	c.scope.dynamic = true
  1007  	c.compileStatement(v.Body, needResult)
  1008  	c.emit(leaveWith)
  1009  	c.leaveBlock()
  1010  	c.popScope()
  1011  }
  1012  
  1013  func (c *compiler) compileSwitchStatement(v *ast.SwitchStatement, needResult bool) {
  1014  	c.block = &block{
  1015  		typ:        blockSwitch,
  1016  		outer:      c.block,
  1017  		needResult: needResult,
  1018  	}
  1019  
  1020  	c.compileExpression(v.Discriminant).emitGetter(true)
  1021  
  1022  	var funcs []*ast.FunctionDeclaration
  1023  	for _, s := range v.Body {
  1024  		f := c.extractFunctions(s.Consequent)
  1025  		funcs = append(funcs, f...)
  1026  	}
  1027  	var scopeDeclared bool
  1028  	if len(funcs) > 0 {
  1029  		c.newBlockScope()
  1030  		scopeDeclared = true
  1031  		c.createFunctionBindings(funcs)
  1032  	}
  1033  
  1034  	for _, s := range v.Body {
  1035  		scopeDeclared = c.compileLexicalDeclarations(s.Consequent, scopeDeclared)
  1036  	}
  1037  
  1038  	var enter *enterBlock
  1039  	var db *binding
  1040  	if scopeDeclared {
  1041  		c.block = &block{
  1042  			typ:        blockScope,
  1043  			outer:      c.block,
  1044  			needResult: needResult,
  1045  		}
  1046  		enter = &enterBlock{}
  1047  		c.emit(enter)
  1048  		// create anonymous variable for the discriminant
  1049  		bindings := c.scope.bindings
  1050  		var bb []*binding
  1051  		if cap(bindings) == len(bindings) {
  1052  			bb = make([]*binding, len(bindings)+1)
  1053  		} else {
  1054  			bb = bindings[:len(bindings)+1]
  1055  		}
  1056  		copy(bb[1:], bindings)
  1057  		db = &binding{
  1058  			scope:    c.scope,
  1059  			isConst:  true,
  1060  			isStrict: true,
  1061  		}
  1062  		bb[0] = db
  1063  		c.scope.bindings = bb
  1064  	}
  1065  
  1066  	c.compileFunctions(funcs)
  1067  
  1068  	if needResult {
  1069  		c.emit(clearResult)
  1070  	}
  1071  
  1072  	jumps := make([]int, len(v.Body))
  1073  
  1074  	for i, s := range v.Body {
  1075  		if s.Test != nil {
  1076  			if db != nil {
  1077  				db.emitGet()
  1078  			} else {
  1079  				c.emit(dup)
  1080  			}
  1081  			c.compileExpression(s.Test).emitGetter(true)
  1082  			c.emit(op_strict_eq)
  1083  			if db != nil {
  1084  				c.emit(jne(2))
  1085  			} else {
  1086  				c.emit(jne(3), pop)
  1087  			}
  1088  			jumps[i] = len(c.p.code)
  1089  			c.emit(nil)
  1090  		}
  1091  	}
  1092  
  1093  	if db == nil {
  1094  		c.emit(pop)
  1095  	}
  1096  	jumpNoMatch := -1
  1097  	if v.Default != -1 {
  1098  		if v.Default != 0 {
  1099  			jumps[v.Default] = len(c.p.code)
  1100  			c.emit(nil)
  1101  		}
  1102  	} else {
  1103  		jumpNoMatch = len(c.p.code)
  1104  		c.emit(nil)
  1105  	}
  1106  
  1107  	for i, s := range v.Body {
  1108  		if s.Test != nil || i != 0 {
  1109  			c.p.code[jumps[i]] = jump(len(c.p.code) - jumps[i])
  1110  		}
  1111  		c.compileStatements(s.Consequent, needResult)
  1112  	}
  1113  
  1114  	if jumpNoMatch != -1 {
  1115  		c.p.code[jumpNoMatch] = jump(len(c.p.code) - jumpNoMatch)
  1116  	}
  1117  	if enter != nil {
  1118  		c.leaveScopeBlock(enter)
  1119  		enter.stackSize--
  1120  		c.popScope()
  1121  	}
  1122  	c.leaveBlock()
  1123  }
  1124  
  1125  func (c *compiler) compileClassDeclaration(v *ast.ClassDeclaration) {
  1126  	c.emitLexicalAssign(v.Class.Name.Name, int(v.Class.Class)-1, c.compileClassLiteral(v.Class, false))
  1127  }