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