go.ketch.com/lib/goja@v0.0.1/compiler_stmt.go (about)

     1  package goja
     2  
     3  import (
     4  	"go.ketch.com/lib/goja/ast"
     5  	"go.ketch.com/lib/goja/file"
     6  	"go.ketch.com/lib/goja/token"
     7  	"go.ketch.com/lib/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  	c.emit(halt)
   131  	lbl2 := len(c.p.code)
   132  	c.emit(nil)
   133  	var catchOffset int
   134  	if v.Catch != nil {
   135  		catchOffset = len(c.p.code) - lbl
   136  		if v.Catch.Parameter != nil {
   137  			c.block = &block{
   138  				typ:   blockScope,
   139  				outer: c.block,
   140  			}
   141  			c.newBlockScope()
   142  			list := v.Catch.Body.List
   143  			funcs := c.extractFunctions(list)
   144  			if _, ok := v.Catch.Parameter.(ast.Pattern); ok {
   145  				// add anonymous binding for the catch parameter, note it must be first
   146  				c.scope.addBinding(int(v.Catch.Idx0()) - 1)
   147  			}
   148  			c.createBindings(v.Catch.Parameter, func(name unistring.String, offset int) {
   149  				if c.scope.strict {
   150  					switch name {
   151  					case "arguments", "eval":
   152  						c.throwSyntaxError(offset, "Catch variable may not be eval or arguments in strict mode")
   153  					}
   154  				}
   155  				c.scope.bindNameLexical(name, true, offset)
   156  			})
   157  			enter := &enterBlock{}
   158  			c.emit(enter)
   159  			if pattern, ok := v.Catch.Parameter.(ast.Pattern); ok {
   160  				c.scope.bindings[0].emitGet()
   161  				c.emitPattern(pattern, func(target, init compiledExpr) {
   162  					c.emitPatternLexicalAssign(target, init)
   163  				}, false)
   164  			}
   165  			for _, decl := range funcs {
   166  				c.scope.bindNameLexical(decl.Function.Name.Name, true, int(decl.Function.Name.Idx1())-1)
   167  			}
   168  			c.compileLexicalDeclarations(list, true)
   169  			c.compileFunctions(funcs)
   170  			c.compileStatements(list, bodyNeedResult)
   171  			c.leaveScopeBlock(enter)
   172  			if c.scope.dynLookup || c.scope.bindings[0].inStash {
   173  				c.p.code[lbl+catchOffset] = &enterCatchBlock{
   174  					names:     enter.names,
   175  					stashSize: enter.stashSize,
   176  					stackSize: enter.stackSize,
   177  				}
   178  			} else {
   179  				enter.stackSize--
   180  			}
   181  			c.popScope()
   182  		} else {
   183  			c.emit(pop)
   184  			c.compileBlockStatement(v.Catch.Body, bodyNeedResult)
   185  		}
   186  		c.emit(halt)
   187  	}
   188  	var finallyOffset int
   189  	if v.Finally != nil {
   190  		lbl1 := len(c.p.code)
   191  		c.emit(nil)
   192  		finallyOffset = len(c.p.code) - lbl
   193  		if bodyNeedResult && finallyBreaking != nil && lp == -1 {
   194  			c.emit(clearResult)
   195  		}
   196  		c.compileBlockStatement(v.Finally, false)
   197  		c.emit(halt, retFinally)
   198  
   199  		c.p.code[lbl1] = jump(len(c.p.code) - lbl1)
   200  	}
   201  	c.p.code[lbl] = try{catchOffset: int32(catchOffset), finallyOffset: int32(finallyOffset)}
   202  	c.p.code[lbl2] = jump(len(c.p.code) - lbl2)
   203  	c.leaveBlock()
   204  }
   205  
   206  func (c *compiler) addSrcMap(node ast.Node) {
   207  	c.p.addSrcMap(int(node.Idx0()) - 1)
   208  }
   209  
   210  func (c *compiler) compileThrowStatement(v *ast.ThrowStatement) {
   211  	c.compileExpression(v.Argument).emitGetter(true)
   212  	c.addSrcMap(v)
   213  	c.emit(throw)
   214  }
   215  
   216  func (c *compiler) compileDoWhileStatement(v *ast.DoWhileStatement, needResult bool) {
   217  	c.compileLabeledDoWhileStatement(v, needResult, "")
   218  }
   219  
   220  func (c *compiler) compileLabeledDoWhileStatement(v *ast.DoWhileStatement, needResult bool, label unistring.String) {
   221  	c.block = &block{
   222  		typ:        blockLoop,
   223  		outer:      c.block,
   224  		label:      label,
   225  		needResult: needResult,
   226  	}
   227  
   228  	start := len(c.p.code)
   229  	c.compileStatement(v.Body, needResult)
   230  	c.block.cont = len(c.p.code)
   231  	c.emitExpr(c.compileExpression(v.Test), true)
   232  	c.emit(jeq(start - len(c.p.code)))
   233  	c.leaveBlock()
   234  }
   235  
   236  func (c *compiler) compileForStatement(v *ast.ForStatement, needResult bool) {
   237  	c.compileLabeledForStatement(v, needResult, "")
   238  }
   239  
   240  func (c *compiler) compileForHeadLexDecl(decl *ast.LexicalDeclaration, needResult bool) *enterBlock {
   241  	c.block = &block{
   242  		typ:        blockIterScope,
   243  		outer:      c.block,
   244  		needResult: needResult,
   245  	}
   246  
   247  	c.newBlockScope()
   248  	enterIterBlock := &enterBlock{}
   249  	c.emit(enterIterBlock)
   250  	c.createLexicalBindings(decl)
   251  	c.compileLexicalDeclaration(decl)
   252  	return enterIterBlock
   253  }
   254  
   255  func (c *compiler) compileLabeledForStatement(v *ast.ForStatement, needResult bool, label unistring.String) {
   256  	loopBlock := &block{
   257  		typ:        blockLoop,
   258  		outer:      c.block,
   259  		label:      label,
   260  		needResult: needResult,
   261  	}
   262  	c.block = loopBlock
   263  
   264  	var enterIterBlock *enterBlock
   265  	switch init := v.Initializer.(type) {
   266  	case nil:
   267  		// no-op
   268  	case *ast.ForLoopInitializerLexicalDecl:
   269  		enterIterBlock = c.compileForHeadLexDecl(&init.LexicalDeclaration, needResult)
   270  	case *ast.ForLoopInitializerVarDeclList:
   271  		for _, expr := range init.List {
   272  			c.compileVarBinding(expr)
   273  		}
   274  	case *ast.ForLoopInitializerExpression:
   275  		c.compileExpression(init.Expression).emitGetter(false)
   276  	default:
   277  		c.assert(false, int(v.For)-1, "Unsupported for loop initializer: %T", init)
   278  		panic("unreachable")
   279  	}
   280  
   281  	if needResult {
   282  		c.emit(clearResult) // initial result
   283  	}
   284  
   285  	if enterIterBlock != nil {
   286  		c.emit(jump(1))
   287  	}
   288  
   289  	start := len(c.p.code)
   290  	var j int
   291  	testConst := false
   292  	if v.Test != nil {
   293  		expr := c.compileExpression(v.Test)
   294  		if expr.constant() {
   295  			r, ex := c.evalConst(expr)
   296  			if ex == nil {
   297  				if r.ToBoolean() {
   298  					testConst = true
   299  				} else {
   300  					leave := c.enterDummyMode()
   301  					c.compileStatement(v.Body, false)
   302  					if v.Update != nil {
   303  						c.compileExpression(v.Update).emitGetter(false)
   304  					}
   305  					leave()
   306  					goto end
   307  				}
   308  			} else {
   309  				expr.addSrcMap()
   310  				c.emitThrow(ex.val)
   311  				goto end
   312  			}
   313  		} else {
   314  			expr.emitGetter(true)
   315  			j = len(c.p.code)
   316  			c.emit(nil)
   317  		}
   318  	}
   319  	if needResult {
   320  		c.emit(clearResult)
   321  	}
   322  	c.compileStatement(v.Body, needResult)
   323  	loopBlock.cont = len(c.p.code)
   324  	if enterIterBlock != nil {
   325  		c.emit(jump(1))
   326  	}
   327  	if v.Update != nil {
   328  		c.compileExpression(v.Update).emitGetter(false)
   329  	}
   330  	if enterIterBlock != nil {
   331  		if c.scope.needStash || c.scope.isDynamic() {
   332  			c.p.code[start-1] = copyStash{}
   333  			c.p.code[loopBlock.cont] = copyStash{}
   334  		} else {
   335  			if l := len(c.p.code); l > loopBlock.cont {
   336  				loopBlock.cont++
   337  			} else {
   338  				c.p.code = c.p.code[:l-1]
   339  			}
   340  		}
   341  	}
   342  	c.emit(jump(start - len(c.p.code)))
   343  	if v.Test != nil {
   344  		if !testConst {
   345  			c.p.code[j] = jne(len(c.p.code) - j)
   346  		}
   347  	}
   348  end:
   349  	if enterIterBlock != nil {
   350  		c.leaveScopeBlock(enterIterBlock)
   351  		c.popScope()
   352  	}
   353  	c.leaveBlock()
   354  }
   355  
   356  func (c *compiler) compileForInStatement(v *ast.ForInStatement, needResult bool) {
   357  	c.compileLabeledForInStatement(v, needResult, "")
   358  }
   359  
   360  func (c *compiler) compileForInto(into ast.ForInto, needResult bool) (enter *enterBlock) {
   361  	switch into := into.(type) {
   362  	case *ast.ForIntoExpression:
   363  		c.compileExpression(into.Expression).emitSetter(&c.enumGetExpr, false)
   364  	case *ast.ForIntoVar:
   365  		if c.scope.strict && into.Binding.Initializer != nil {
   366  			c.throwSyntaxError(int(into.Binding.Initializer.Idx0())-1, "for-in loop variable declaration may not have an initializer.")
   367  		}
   368  		switch target := into.Binding.Target.(type) {
   369  		case *ast.Identifier:
   370  			c.compileIdentifierExpression(target).emitSetter(&c.enumGetExpr, false)
   371  		case ast.Pattern:
   372  			c.emit(enumGet)
   373  			c.emitPattern(target, c.emitPatternVarAssign, false)
   374  		default:
   375  			c.throwSyntaxError(int(target.Idx0()-1), "unsupported for-in var target: %T", target)
   376  		}
   377  	case *ast.ForDeclaration:
   378  
   379  		c.block = &block{
   380  			typ:        blockIterScope,
   381  			outer:      c.block,
   382  			needResult: needResult,
   383  		}
   384  
   385  		c.newBlockScope()
   386  		enter = &enterBlock{}
   387  		c.emit(enter)
   388  		switch target := into.Target.(type) {
   389  		case *ast.Identifier:
   390  			b := c.createLexicalIdBinding(target.Name, into.IsConst, int(into.Idx)-1)
   391  			c.emit(enumGet)
   392  			b.emitInitP()
   393  		case ast.Pattern:
   394  			c.createLexicalBinding(target, into.IsConst)
   395  			c.emit(enumGet)
   396  			c.emitPattern(target, func(target, init compiledExpr) {
   397  				c.emitPatternLexicalAssign(target, init)
   398  			}, false)
   399  		default:
   400  			c.assert(false, int(into.Idx)-1, "Unsupported ForBinding: %T", into.Target)
   401  		}
   402  	default:
   403  		c.assert(false, int(into.Idx0())-1, "Unsupported for-into: %T", into)
   404  		panic("unreachable")
   405  	}
   406  
   407  	return
   408  }
   409  
   410  func (c *compiler) compileLabeledForInOfStatement(into ast.ForInto, source ast.Expression, body ast.Statement, iter, needResult bool, label unistring.String) {
   411  	c.block = &block{
   412  		typ:        blockLoopEnum,
   413  		outer:      c.block,
   414  		label:      label,
   415  		needResult: needResult,
   416  	}
   417  	enterPos := -1
   418  	if forDecl, ok := into.(*ast.ForDeclaration); ok {
   419  		c.block = &block{
   420  			typ:        blockScope,
   421  			outer:      c.block,
   422  			needResult: false,
   423  		}
   424  		c.newBlockScope()
   425  		enterPos = len(c.p.code)
   426  		c.emit(jump(1))
   427  		c.createLexicalBinding(forDecl.Target, forDecl.IsConst)
   428  	}
   429  	c.compileExpression(source).emitGetter(true)
   430  	if enterPos != -1 {
   431  		s := c.scope
   432  		used := len(c.block.breaks) > 0 || s.isDynamic()
   433  		if !used {
   434  			for _, b := range s.bindings {
   435  				if b.useCount() > 0 {
   436  					used = true
   437  					break
   438  				}
   439  			}
   440  		}
   441  		if used {
   442  			// We need the stack untouched because it contains the source.
   443  			// This is not the most optimal way, but it's an edge case, hopefully quite rare.
   444  			for _, b := range s.bindings {
   445  				b.moveToStash()
   446  			}
   447  			enter := &enterBlock{}
   448  			c.p.code[enterPos] = enter
   449  			c.leaveScopeBlock(enter)
   450  		} else {
   451  			c.block = c.block.outer
   452  		}
   453  		c.popScope()
   454  	}
   455  	if iter {
   456  		c.emit(iterateP)
   457  	} else {
   458  		c.emit(enumerate)
   459  	}
   460  	if needResult {
   461  		c.emit(clearResult)
   462  	}
   463  	start := len(c.p.code)
   464  	c.block.cont = start
   465  	c.emit(nil)
   466  	enterIterBlock := c.compileForInto(into, needResult)
   467  	if needResult {
   468  		c.emit(clearResult)
   469  	}
   470  	c.compileStatement(body, needResult)
   471  	if enterIterBlock != nil {
   472  		c.leaveScopeBlock(enterIterBlock)
   473  		c.popScope()
   474  	}
   475  	c.emit(jump(start - len(c.p.code)))
   476  	if iter {
   477  		c.p.code[start] = iterNext(len(c.p.code) - start)
   478  	} else {
   479  		c.p.code[start] = enumNext(len(c.p.code) - start)
   480  	}
   481  	c.emit(enumPop, jump(2))
   482  	c.leaveBlock()
   483  	c.emit(enumPopClose)
   484  }
   485  
   486  func (c *compiler) compileLabeledForInStatement(v *ast.ForInStatement, needResult bool, label unistring.String) {
   487  	c.compileLabeledForInOfStatement(v.Into, v.Source, v.Body, false, needResult, label)
   488  }
   489  
   490  func (c *compiler) compileForOfStatement(v *ast.ForOfStatement, needResult bool) {
   491  	c.compileLabeledForOfStatement(v, needResult, "")
   492  }
   493  
   494  func (c *compiler) compileLabeledForOfStatement(v *ast.ForOfStatement, needResult bool, label unistring.String) {
   495  	c.compileLabeledForInOfStatement(v.Into, v.Source, v.Body, true, needResult, label)
   496  }
   497  
   498  func (c *compiler) compileWhileStatement(v *ast.WhileStatement, needResult bool) {
   499  	c.compileLabeledWhileStatement(v, needResult, "")
   500  }
   501  
   502  func (c *compiler) compileLabeledWhileStatement(v *ast.WhileStatement, needResult bool, label unistring.String) {
   503  	c.block = &block{
   504  		typ:        blockLoop,
   505  		outer:      c.block,
   506  		label:      label,
   507  		needResult: needResult,
   508  	}
   509  
   510  	if needResult {
   511  		c.emit(clearResult)
   512  	}
   513  	start := len(c.p.code)
   514  	c.block.cont = start
   515  	expr := c.compileExpression(v.Test)
   516  	testTrue := false
   517  	var j int
   518  	if expr.constant() {
   519  		if t, ex := c.evalConst(expr); ex == nil {
   520  			if t.ToBoolean() {
   521  				testTrue = true
   522  			} else {
   523  				c.compileStatementDummy(v.Body)
   524  				goto end
   525  			}
   526  		} else {
   527  			c.emitThrow(ex.val)
   528  			goto end
   529  		}
   530  	} else {
   531  		expr.emitGetter(true)
   532  		j = len(c.p.code)
   533  		c.emit(nil)
   534  	}
   535  	if needResult {
   536  		c.emit(clearResult)
   537  	}
   538  	c.compileStatement(v.Body, needResult)
   539  	c.emit(jump(start - len(c.p.code)))
   540  	if !testTrue {
   541  		c.p.code[j] = jne(len(c.p.code) - j)
   542  	}
   543  end:
   544  	c.leaveBlock()
   545  }
   546  
   547  func (c *compiler) compileEmptyStatement(needResult bool) {
   548  	if needResult {
   549  		c.emit(clearResult)
   550  	}
   551  }
   552  
   553  func (c *compiler) compileBranchStatement(v *ast.BranchStatement) {
   554  	switch v.Token {
   555  	case token.BREAK:
   556  		c.compileBreak(v.Label, v.Idx)
   557  	case token.CONTINUE:
   558  		c.compileContinue(v.Label, v.Idx)
   559  	default:
   560  		c.assert(false, int(v.Idx0())-1, "Unknown branch statement token: %s", v.Token.String())
   561  		panic("unreachable")
   562  	}
   563  }
   564  
   565  func (c *compiler) findBranchBlock(st *ast.BranchStatement) *block {
   566  	switch st.Token {
   567  	case token.BREAK:
   568  		return c.findBreakBlock(st.Label, true)
   569  	case token.CONTINUE:
   570  		return c.findBreakBlock(st.Label, false)
   571  	}
   572  	return nil
   573  }
   574  
   575  func (c *compiler) findBreakBlock(label *ast.Identifier, isBreak bool) (res *block) {
   576  	if label != nil {
   577  		var found *block
   578  		for b := c.block; b != nil; b = b.outer {
   579  			if res == nil {
   580  				if bb := b.breaking; bb != nil {
   581  					res = bb
   582  					if isBreak {
   583  						return
   584  					}
   585  				}
   586  			}
   587  			if b.label == label.Name {
   588  				found = b
   589  				break
   590  			}
   591  		}
   592  		if !isBreak && found != nil && found.typ != blockLoop && found.typ != blockLoopEnum {
   593  			c.throwSyntaxError(int(label.Idx)-1, "Illegal continue statement: '%s' does not denote an iteration statement", label.Name)
   594  		}
   595  		if res == nil {
   596  			res = found
   597  		}
   598  	} else {
   599  		// find the nearest loop or switch (if break)
   600  	L:
   601  		for b := c.block; b != nil; b = b.outer {
   602  			if bb := b.breaking; bb != nil {
   603  				return bb
   604  			}
   605  			switch b.typ {
   606  			case blockLoop, blockLoopEnum:
   607  				res = b
   608  				break L
   609  			case blockSwitch:
   610  				if isBreak {
   611  					res = b
   612  					break L
   613  				}
   614  			}
   615  		}
   616  	}
   617  
   618  	return
   619  }
   620  
   621  func (c *compiler) emitBlockExitCode(label *ast.Identifier, idx file.Idx, isBreak bool) *block {
   622  	block := c.findBreakBlock(label, isBreak)
   623  	if block == nil {
   624  		c.throwSyntaxError(int(idx)-1, "Could not find block")
   625  		panic("unreachable")
   626  	}
   627  L:
   628  	for b := c.block; b != block; b = b.outer {
   629  		switch b.typ {
   630  		case blockIterScope:
   631  			if !isBreak && 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(halt)
   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 {
   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(halt)
   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  }