package goja

import (
	
	
	
	
)

func ( *compiler) ( ast.Statement,  bool) {

	switch v := .(type) {
	case *ast.BlockStatement:
		.compileBlockStatement(, )
	case *ast.ExpressionStatement:
		.compileExpressionStatement(, )
	case *ast.VariableStatement:
		.compileVariableStatement()
	case *ast.LexicalDeclaration:
		.compileLexicalDeclaration()
	case *ast.ReturnStatement:
		.compileReturnStatement()
	case *ast.IfStatement:
		.compileIfStatement(, )
	case *ast.DoWhileStatement:
		.compileDoWhileStatement(, )
	case *ast.ForStatement:
		.compileForStatement(, )
	case *ast.ForInStatement:
		.compileForInStatement(, )
	case *ast.ForOfStatement:
		.compileForOfStatement(, )
	case *ast.WhileStatement:
		.compileWhileStatement(, )
	case *ast.BranchStatement:
		.compileBranchStatement()
	case *ast.TryStatement:
		.compileTryStatement(, )
	case *ast.ThrowStatement:
		.compileThrowStatement()
	case *ast.SwitchStatement:
		.compileSwitchStatement(, )
	case *ast.LabelledStatement:
		.compileLabeledStatement(, )
	case *ast.EmptyStatement:
		.compileEmptyStatement()
	case *ast.FunctionDeclaration:
		.compileStandaloneFunctionDecl()
		// note functions inside blocks are hoisted to the top of the block and are compiled using compileFunctions()
	case *ast.ClassDeclaration:
		.compileClassDeclaration()
	case *ast.WithStatement:
		.compileWithStatement(, )
	case *ast.DebuggerStatement:
	default:
		.assert(false, int(.Idx0())-1, "Unknown statement type: %T", )
		panic("unreachable")
	}
}

func ( *compiler) ( *ast.LabelledStatement,  bool) {
	 := .Label.Name
	if .scope.strict {
		.checkIdentifierName(, int(.Label.Idx)-1)
	}
	for  := .block;  != nil;  = .outer {
		if .label ==  {
			.throwSyntaxError(int(.Label.Idx-1), "Label '%s' has already been declared", )
		}
	}
	switch s := .Statement.(type) {
	case *ast.ForInStatement:
		.compileLabeledForInStatement(, , )
	case *ast.ForOfStatement:
		.compileLabeledForOfStatement(, , )
	case *ast.ForStatement:
		.compileLabeledForStatement(, , )
	case *ast.WhileStatement:
		.compileLabeledWhileStatement(, , )
	case *ast.DoWhileStatement:
		.compileLabeledDoWhileStatement(, , )
	default:
		.compileGenericLabeledStatement(, , )
	}
}

func ( *compiler) ( *enterBlock) {
	 := .scope
	,  := 0, 0
	if .dynLookup {
		 = len(.bindings)
		.names = .makeNamesMap()
	} else {
		for ,  := range .bindings {
			if .inStash {
				++
			} else {
				++
			}
		}
	}
	.stashSize, .stackSize = uint32(), uint32()
}

func ( *compiler) ( *ast.TryStatement,  bool) {
	.block = &block{
		typ:   blockTry,
		outer: .block,
	}
	var  int
	var  bool
	var  *block
	if .Finally != nil {
		,  = .scanStatements(.Finally.List)
	}
	if  != nil {
		.block.breaking = 
		if  == -1 {
			 = .needResult
		}
	} else {
		 = 
	}
	 := len(.p.code)
	.emit(nil)
	if  {
		.emit(clearResult)
	}
	.compileBlockStatement(.Body, )
	var  int
	if .Catch != nil {
		 := len(.p.code) // jump over the catch block
		.emit(nil)
		 = len(.p.code) - 
		if .Catch.Parameter != nil {
			.block = &block{
				typ:   blockScope,
				outer: .block,
			}
			.newBlockScope()
			 := .Catch.Body.List
			 := .extractFunctions()
			if ,  := .Catch.Parameter.(ast.Pattern);  {
				// add anonymous binding for the catch parameter, note it must be first
				.scope.addBinding(int(.Catch.Idx0()) - 1)
			}
			.createBindings(.Catch.Parameter, func( unistring.String,  int) {
				if .scope.strict {
					switch  {
					case "arguments", "eval":
						.throwSyntaxError(, "Catch variable may not be eval or arguments in strict mode")
					}
				}
				.scope.bindNameLexical(, true, )
			})
			 := &enterBlock{}
			.emit()
			if ,  := .Catch.Parameter.(ast.Pattern);  {
				.scope.bindings[0].emitGet()
				.emitPattern(, func(,  compiledExpr) {
					.emitPatternLexicalAssign(, )
				}, false)
			}
			for ,  := range  {
				.scope.bindNameLexical(.Function.Name.Name, true, int(.Function.Name.Idx1())-1)
			}
			.compileLexicalDeclarations(, true)
			.compileFunctions()
			.compileStatements(, )
			.leaveScopeBlock()
			if .scope.dynLookup || .scope.bindings[0].inStash {
				.p.code[+] = &enterCatchBlock{
					names:     .names,
					stashSize: .stashSize,
					stackSize: .stackSize,
				}
			} else {
				.stackSize--
			}
			.popScope()
		} else {
			.emit(pop)
			.compileBlockStatement(.Catch.Body, )
		}
		.p.code[] = jump(len(.p.code) - )
	}
	var  int
	if .Finally != nil {
		.emit(enterFinally{})
		 = len(.p.code) -  // finallyOffset should not include enterFinally
		if  &&  != nil &&  == -1 {
			.emit(clearResult)
		}
		.compileBlockStatement(.Finally, false)
		.emit(leaveFinally{})
	} else {
		.emit(leaveTry{})
	}
	.p.code[] = try{catchOffset: int32(), finallyOffset: int32()}
	.leaveBlock()
}

func ( *compiler) ( ast.Node) {
	.p.addSrcMap(int(.Idx0()) - 1)
}

func ( *compiler) ( *ast.ThrowStatement) {
	.compileExpression(.Argument).emitGetter(true)
	.addSrcMap()
	.emit(throw)
}

func ( *compiler) ( *ast.DoWhileStatement,  bool) {
	.compileLabeledDoWhileStatement(, , "")
}

func ( *compiler) ( *ast.DoWhileStatement,  bool,  unistring.String) {
	.block = &block{
		typ:        blockLoop,
		outer:      .block,
		label:      ,
		needResult: ,
	}

	 := len(.p.code)
	.compileStatement(.Body, )
	.block.cont = len(.p.code)
	.emitExpr(.compileExpression(.Test), true)
	.emit(jeq( - len(.p.code)))
	.leaveBlock()
}

func ( *compiler) ( *ast.ForStatement,  bool) {
	.compileLabeledForStatement(, , "")
}

func ( *compiler) ( *ast.LexicalDeclaration,  bool) *enterBlock {
	.block = &block{
		typ:        blockIterScope,
		outer:      .block,
		needResult: ,
	}

	.newBlockScope()
	 := &enterBlock{}
	.emit()
	.createLexicalBindings()
	.compileLexicalDeclaration()
	return 
}

func ( *compiler) ( *ast.ForStatement,  bool,  unistring.String) {
	 := &block{
		typ:        blockLoop,
		outer:      .block,
		label:      ,
		needResult: ,
	}
	.block = 

	var  *enterBlock
	switch init := .Initializer.(type) {
	case nil:
		// no-op
	case *ast.ForLoopInitializerLexicalDecl:
		 = .compileForHeadLexDecl(&.LexicalDeclaration, )
	case *ast.ForLoopInitializerVarDeclList:
		for ,  := range .List {
			.compileVarBinding()
		}
	case *ast.ForLoopInitializerExpression:
		.compileExpression(.Expression).emitGetter(false)
	default:
		.assert(false, int(.For)-1, "Unsupported for loop initializer: %T", )
		panic("unreachable")
	}

	if  {
		.emit(clearResult) // initial result
	}

	if  != nil {
		.emit(jump(1))
	}

	 := len(.p.code)
	var  int
	 := false
	if .Test != nil {
		 := .compileExpression(.Test)
		if .constant() {
			,  := .evalConst()
			if  == nil {
				if .ToBoolean() {
					 = true
				} else {
					 := .enterDummyMode()
					.compileStatement(.Body, false)
					if .Update != nil {
						.compileExpression(.Update).emitGetter(false)
					}
					()
					goto 
				}
			} else {
				.addSrcMap()
				.emitThrow(.val)
				goto 
			}
		} else {
			.emitGetter(true)
			 = len(.p.code)
			.emit(nil)
		}
	}
	if  {
		.emit(clearResult)
	}
	.compileStatement(.Body, )
	.cont = len(.p.code)
	if  != nil {
		.emit(jump(1))
	}
	if .Update != nil {
		.compileExpression(.Update).emitGetter(false)
	}
	if  != nil {
		if .scope.needStash || .scope.isDynamic() {
			.p.code[-1] = copyStash{}
			.p.code[.cont] = copyStash{}
		} else {
			if  := len(.p.code);  > .cont {
				.cont++
			} else {
				.p.code = .p.code[:-1]
			}
		}
	}
	.emit(jump( - len(.p.code)))
	if .Test != nil {
		if ! {
			.p.code[] = jne(len(.p.code) - )
		}
	}
:
	if  != nil {
		.leaveScopeBlock()
		.popScope()
	}
	.leaveBlock()
}

func ( *compiler) ( *ast.ForInStatement,  bool) {
	.compileLabeledForInStatement(, , "")
}

func ( *compiler) ( ast.ForInto,  bool) ( *enterBlock) {
	switch into := .(type) {
	case *ast.ForIntoExpression:
		.compileExpression(.Expression).emitSetter(&.enumGetExpr, false)
	case *ast.ForIntoVar:
		if .scope.strict && .Binding.Initializer != nil {
			.throwSyntaxError(int(.Binding.Initializer.Idx0())-1, "for-in loop variable declaration may not have an initializer.")
		}
		switch target := .Binding.Target.(type) {
		case *ast.Identifier:
			.compileIdentifierExpression().emitSetter(&.enumGetExpr, false)
		case ast.Pattern:
			.emit(enumGet)
			.emitPattern(, .emitPatternVarAssign, false)
		default:
			.throwSyntaxError(int(.Idx0()-1), "unsupported for-in var target: %T", )
		}
	case *ast.ForDeclaration:

		.block = &block{
			typ:        blockIterScope,
			outer:      .block,
			needResult: ,
		}

		.newBlockScope()
		 = &enterBlock{}
		.emit()
		switch target := .Target.(type) {
		case *ast.Identifier:
			 := .createLexicalIdBinding(.Name, .IsConst, int(.Idx)-1)
			.emit(enumGet)
			.emitInitP()
		case ast.Pattern:
			.createLexicalBinding(, .IsConst)
			.emit(enumGet)
			.emitPattern(, func(,  compiledExpr) {
				.emitPatternLexicalAssign(, )
			}, false)
		default:
			.assert(false, int(.Idx)-1, "Unsupported ForBinding: %T", .Target)
		}
	default:
		.assert(false, int(.Idx0())-1, "Unsupported for-into: %T", )
		panic("unreachable")
	}

	return
}

func ( *compiler) ( ast.ForInto,  ast.Expression,  ast.Statement, ,  bool,  unistring.String) {
	.block = &block{
		typ:        blockLoopEnum,
		outer:      .block,
		label:      ,
		needResult: ,
	}
	 := -1
	if ,  := .(*ast.ForDeclaration);  {
		.block = &block{
			typ:        blockScope,
			outer:      .block,
			needResult: false,
		}
		.newBlockScope()
		 = len(.p.code)
		.emit(jump(1))
		.createLexicalBinding(.Target, .IsConst)
	}
	.compileExpression().emitGetter(true)
	if  != -1 {
		 := .scope
		 := len(.block.breaks) > 0 || .isDynamic()
		if ! {
			for ,  := range .bindings {
				if .useCount() > 0 {
					 = true
					break
				}
			}
		}
		if  {
			// We need the stack untouched because it contains the source.
			// This is not the most optimal way, but it's an edge case, hopefully quite rare.
			for ,  := range .bindings {
				.moveToStash()
			}
			 := &enterBlock{}
			.p.code[] = 
			.leaveScopeBlock()
		} else {
			.block = .block.outer
		}
		.popScope()
	}
	if  {
		.emit(iterateP)
	} else {
		.emit(enumerate)
	}
	if  {
		.emit(clearResult)
	}
	 := len(.p.code)
	.block.cont = 
	.emit(nil)
	 := .compileForInto(, )
	if  {
		.emit(clearResult)
	}
	.compileStatement(, )
	if  != nil {
		.leaveScopeBlock()
		.popScope()
	}
	.emit(jump( - len(.p.code)))
	if  {
		.p.code[] = iterNext(len(.p.code) - )
	} else {
		.p.code[] = enumNext(len(.p.code) - )
	}
	.emit(enumPop, jump(2))
	.leaveBlock()
	.emit(enumPopClose)
}

func ( *compiler) ( *ast.ForInStatement,  bool,  unistring.String) {
	.compileLabeledForInOfStatement(.Into, .Source, .Body, false, , )
}

func ( *compiler) ( *ast.ForOfStatement,  bool) {
	.compileLabeledForOfStatement(, , "")
}

func ( *compiler) ( *ast.ForOfStatement,  bool,  unistring.String) {
	.compileLabeledForInOfStatement(.Into, .Source, .Body, true, , )
}

func ( *compiler) ( *ast.WhileStatement,  bool) {
	.compileLabeledWhileStatement(, , "")
}

func ( *compiler) ( *ast.WhileStatement,  bool,  unistring.String) {
	.block = &block{
		typ:        blockLoop,
		outer:      .block,
		label:      ,
		needResult: ,
	}

	if  {
		.emit(clearResult)
	}
	 := len(.p.code)
	.block.cont = 
	 := .compileExpression(.Test)
	 := false
	var  int
	if .constant() {
		if ,  := .evalConst();  == nil {
			if .ToBoolean() {
				 = true
			} else {
				.compileStatementDummy(.Body)
				goto 
			}
		} else {
			.emitThrow(.val)
			goto 
		}
	} else {
		.emitGetter(true)
		 = len(.p.code)
		.emit(nil)
	}
	if  {
		.emit(clearResult)
	}
	.compileStatement(.Body, )
	.emit(jump( - len(.p.code)))
	if ! {
		.p.code[] = jne(len(.p.code) - )
	}
:
	.leaveBlock()
}

func ( *compiler) ( bool) {
	if  {
		.emit(clearResult)
	}
}

func ( *compiler) ( *ast.BranchStatement) {
	switch .Token {
	case token.BREAK:
		.compileBreak(.Label, .Idx)
	case token.CONTINUE:
		.compileContinue(.Label, .Idx)
	default:
		.assert(false, int(.Idx0())-1, "Unknown branch statement token: %s", .Token.String())
		panic("unreachable")
	}
}

func ( *compiler) ( *ast.BranchStatement) *block {
	switch .Token {
	case token.BREAK:
		return .findBreakBlock(.Label, true)
	case token.CONTINUE:
		return .findBreakBlock(.Label, false)
	}
	return nil
}

func ( *compiler) ( *ast.Identifier,  bool) ( *block) {
	if  != nil {
		var  *block
		for  := .block;  != nil;  = .outer {
			if  == nil {
				if  := .breaking;  != nil {
					 = 
					if  {
						return
					}
				}
			}
			if .label == .Name {
				 = 
				break
			}
		}
		if ! &&  != nil && .typ != blockLoop && .typ != blockLoopEnum {
			.throwSyntaxError(int(.Idx)-1, "Illegal continue statement: '%s' does not denote an iteration statement", .Name)
		}
		if  == nil {
			 = 
		}
	} else {
		// find the nearest loop or switch (if break)
	:
		for  := .block;  != nil;  = .outer {
			if  := .breaking;  != nil {
				return 
			}
			switch .typ {
			case blockLoop, blockLoopEnum:
				 = 
				break 
			case blockSwitch:
				if  {
					 = 
					break 
				}
			}
		}
	}

	return
}

func ( *compiler) ( *ast.Identifier,  file.Idx,  bool) *block {
	 := .findBreakBlock(, )
	if  == nil {
		.throwSyntaxError(int()-1, "Could not find block")
		panic("unreachable")
	}
	 := ! && .typ == blockLoop
:
	for  := .block;  != ;  = .outer {
		switch .typ {
		case blockIterScope:
			// blockIterScope in 'for' loops is shared across iterations, so
			// continue should not pop it.
			if  && .outer ==  {
				break 
			}
			fallthrough
		case blockScope:
			.breaks = append(.breaks, len(.p.code))
			.emit(nil)
		case blockTry:
			.emit(leaveTry{})
		case blockWith:
			.emit(leaveWith)
		case blockLoopEnum:
			.emit(enumPopClose)
		}
	}
	return 
}

func ( *compiler) ( *ast.Identifier,  file.Idx) {
	 := .emitBlockExitCode(, , true)
	.breaks = append(.breaks, len(.p.code))
	.emit(nil)
}

func ( *compiler) ( *ast.Identifier,  file.Idx) {
	 := .emitBlockExitCode(, , false)
	.conts = append(.conts, len(.p.code))
	.emit(nil)
}

func ( *compiler) ( ast.Statement,  bool) {
	if !.scope.strict {
		if ,  := .(*ast.FunctionDeclaration);  && !.Function.Async && !.Function.Generator {
			.compileFunction()
			if  {
				.emit(clearResult)
			}
			return
		}
	}
	.compileStatement(, )
}

func ( *compiler) ( ast.Statement) {
	 := .enterDummyMode()
	defer ()
	.compileIfBody(, false)
}

func ( *compiler) ( *ast.IfStatement,  bool) {
	 := .compileExpression(.Test)
	if  {
		.emit(clearResult)
	}
	if .constant() {
		,  := .evalConst()
		if  != nil {
			.addSrcMap()
			.emitThrow(.val)
			return
		}
		if .ToBoolean() {
			.compileIfBody(.Consequent, )
			if .Alternate != nil {
				.compileIfBodyDummy(.Alternate)
			}
		} else {
			.compileIfBodyDummy(.Consequent)
			if .Alternate != nil {
				.compileIfBody(.Alternate, )
			} else {
				if  {
					.emit(clearResult)
				}
			}
		}
		return
	}
	.emitGetter(true)
	 := len(.p.code)
	.emit(nil)
	.compileIfBody(.Consequent, )
	if .Alternate != nil {
		 := len(.p.code)
		.emit(nil)
		.p.code[] = jne(len(.p.code) - )
		.compileIfBody(.Alternate, )
		.p.code[] = jump(len(.p.code) - )
	} else {
		if  {
			.emit(jump(2))
			.p.code[] = jne(len(.p.code) - )
			.emit(clearResult)
		} else {
			.p.code[] = jne(len(.p.code) - )
		}
	}
}

func ( *compiler) ( *ast.ReturnStatement) {
	if  := .scope.nearestFunction();  != nil && .funcType == funcClsInit {
		.throwSyntaxError(int(.Return)-1, "Illegal return statement")
	}
	if .Argument != nil {
		.emitExpr(.compileExpression(.Argument), true)
	} else {
		.emit(loadUndef)
	}
	for  := .block;  != nil;  = .outer {
		switch .typ {
		case blockTry:
			.emit(saveResult, leaveTry{}, loadResult)
		case blockLoopEnum:
			.emit(enumPopClose)
		}
	}
	if  := .scope.nearestFunction();  != nil && .funcType == funcDerivedCtor {
		 := .boundNames[thisBindingName]
		.assert( != nil, int(.Return)-1, "Derived constructor, but no 'this' binding")
		.markAccessPoint()
	}
	.emit(ret)
}

func ( *compiler) ( unistring.String,  int) {
	for  := .scope;  != nil;  = .outer {
		if ,  := .boundNames[];  && !.isVar && !(.isArg &&  != .scope) {
			.throwSyntaxError(, "Identifier '%s' has already been declared", )
		}
		if .isFunction() {
			break
		}
	}
}

func ( *compiler) ( unistring.String,  int,  compiledExpr) {
	.checkVarConflict(, )
	if  != nil {
		,  := .scope.lookupName()
		if  {
			.emitNamedOrConst(, )
			.p.addSrcMap()
			.emitInitP()
		} else {
			.emitVarRef(, , )
			.emitNamedOrConst(, )
			.p.addSrcMap()
			.emit(initValueP)
		}
	}
}

func ( *compiler) ( *ast.Binding) {
	switch target := .Target.(type) {
	case *ast.Identifier:
		.emitVarAssign(.Name, int(.Idx)-1, .compileExpression(.Initializer))
	case ast.Pattern:
		.compileExpression(.Initializer).emitGetter(true)
		.emitPattern(, .emitPatternVarAssign, false)
	default:
		.throwSyntaxError(int(.Idx0()-1), "unsupported variable binding target: %T", )
	}
}

func ( *compiler) ( unistring.String,  int,  compiledExpr) {
	 := .scope.boundNames[]
	.assert( != nil, , "Lexical declaration for an unbound name")
	if  != nil {
		.emitNamedOrConst(, )
		.p.addSrcMap()
	} else {
		if .isConst {
			.throwSyntaxError(, "Missing initializer in const declaration")
		}
		.emit(loadUndef)
	}
	.emitInitP()
}

func ( *compiler) (,  compiledExpr) {
	 := .(*compiledIdentifierExpr)
	.emitVarAssign(.name, .offset, )
}

func ( *compiler) (,  compiledExpr) {
	 := .(*compiledIdentifierExpr)
	.emitLexicalAssign(.name, .offset, )
}

func ( *compiler) (,  compiledExpr) {
	if ,  := .(*compiledIdentifierExpr);  {
		,  := .scope.lookupName(.name)
		if  {
			.emitNamedOrConst(, .name)
			.emitSetP()
		} else {
			.emitVarRef(.name, .offset, )
			.emitNamedOrConst(, .name)
			.emit(putValueP)
		}
	} else {
		.emitRef()
		.emitExpr(, true)
		.emit(putValueP)
	}
}

func ( *compiler) ( *ast.Binding) {
	switch target := .Target.(type) {
	case *ast.Identifier:
		.emitLexicalAssign(.Name, int(.Idx)-1, .compileExpression(.Initializer))
	case ast.Pattern:
		.compileExpression(.Initializer).emitGetter(true)
		.emitPattern(, func(,  compiledExpr) {
			.emitPatternLexicalAssign(, )
		}, false)
	default:
		.throwSyntaxError(int(.Idx0()-1), "unsupported lexical binding target: %T", )
	}
}

func ( *compiler) ( *ast.VariableStatement) {
	for ,  := range .List {
		.compileVarBinding()
	}
}

func ( *compiler) ( *ast.LexicalDeclaration) {
	for ,  := range .List {
		.compileLexicalBinding()
	}
}

func ( *compiler) ( ast.Statement) bool {
	switch st := .(type) {
	case *ast.EmptyStatement, *ast.VariableStatement, *ast.LexicalDeclaration, *ast.FunctionDeclaration,
		*ast.ClassDeclaration, *ast.BranchStatement, *ast.DebuggerStatement:
		return true
	case *ast.LabelledStatement:
		return .(.Statement)
	case *ast.BlockStatement:
		for ,  := range .List {
			if ,  := .(*ast.BranchStatement);  {
				return true
			}
			if !.() {
				return false
			}
		}
		return true
	}
	return false
}

func ( *compiler) ( []ast.Statement) ( int,  *block) {
	 = -1
	for ,  := range  {
		if ,  := .(*ast.BranchStatement);  {
			if  := .findBranchBlock();  != nil {
				 = 
			}
			break
		}
		if !.isEmptyResult() {
			 = 
		}
	}
	return
}

func ( *compiler) ( []ast.Statement,  int) {
	if  >= 0 {
		for ,  := range [:] {
			if ,  := .(*ast.FunctionDeclaration);  {
				continue
			}
			.compileStatement(, false)
		}
		.compileStatement([], true)
	}
	var  func()
	defer func() {
		if  != nil {
			()
		}
	}()
	for ,  := range [+1:] {
		if ,  := .(*ast.FunctionDeclaration);  {
			continue
		}
		.compileStatement(, false)
		if  == nil {
			if ,  := .(*ast.BranchStatement);  {
				 = .enterDummyMode()
			}
		}
	}
}

func ( *compiler) ( []ast.Statement,  bool) {
	,  := .scanStatements()
	if  != nil {
		 = .needResult
	}
	if  {
		.compileStatementsNeedResult(, )
		return
	}
	for ,  := range  {
		if ,  := .(*ast.FunctionDeclaration);  {
			continue
		}
		.compileStatement(, false)
	}
}

func ( *compiler) ( ast.Statement,  bool,  unistring.String) {
	.block = &block{
		typ:        blockLabel,
		outer:      .block,
		label:      ,
		needResult: ,
	}
	.compileStatement(, )
	.leaveBlock()
}

func ( *compiler) ( *ast.BlockStatement,  bool) {
	var  bool
	 := .extractFunctions(.List)
	if len() > 0 {
		.newBlockScope()
		 = true
	}
	.createFunctionBindings()
	 = .compileLexicalDeclarations(.List, )

	var  *enterBlock
	if  {
		.block = &block{
			outer:      .block,
			typ:        blockScope,
			needResult: ,
		}
		 = &enterBlock{}
		.emit()
	}
	.compileFunctions()
	.compileStatements(.List, )
	if  {
		.leaveScopeBlock()
		.popScope()
	}
}

func ( *compiler) ( *ast.ExpressionStatement,  bool) {
	.emitExpr(.compileExpression(.Expression), )
	if  {
		.emit(saveResult)
	}
}

func ( *compiler) ( *ast.WithStatement,  bool) {
	if .scope.strict {
		.throwSyntaxError(int(.With)-1, "Strict mode code may not include a with statement")
		return
	}
	.compileExpression(.Object).emitGetter(true)
	.emit(enterWith)
	.block = &block{
		outer:      .block,
		typ:        blockWith,
		needResult: ,
	}
	.newBlockScope()
	.scope.dynamic = true
	.compileStatement(.Body, )
	.emit(leaveWith)
	.leaveBlock()
	.popScope()
}

func ( *compiler) ( *ast.SwitchStatement,  bool) {
	.block = &block{
		typ:        blockSwitch,
		outer:      .block,
		needResult: ,
	}

	.compileExpression(.Discriminant).emitGetter(true)

	var  []*ast.FunctionDeclaration
	for ,  := range .Body {
		 := .extractFunctions(.Consequent)
		 = append(, ...)
	}
	var  bool
	if len() > 0 {
		.newBlockScope()
		 = true
		.createFunctionBindings()
	}

	for ,  := range .Body {
		 = .compileLexicalDeclarations(.Consequent, )
	}

	var  *enterBlock
	var  *binding
	if  {
		.block = &block{
			typ:        blockScope,
			outer:      .block,
			needResult: ,
		}
		 = &enterBlock{}
		.emit()
		// create anonymous variable for the discriminant
		 := .scope.bindings
		var  []*binding
		if cap() == len() {
			 = make([]*binding, len()+1)
		} else {
			 = [:len()+1]
		}
		copy([1:], )
		 = &binding{
			scope:    .scope,
			isConst:  true,
			isStrict: true,
		}
		[0] = 
		.scope.bindings = 
	}

	.compileFunctions()

	if  {
		.emit(clearResult)
	}

	 := make([]int, len(.Body))

	for ,  := range .Body {
		if .Test != nil {
			if  != nil {
				.emitGet()
			} else {
				.emit(dup)
			}
			.compileExpression(.Test).emitGetter(true)
			.emit(op_strict_eq)
			if  != nil {
				.emit(jne(2))
			} else {
				.emit(jne(3), pop)
			}
			[] = len(.p.code)
			.emit(nil)
		}
	}

	if  == nil {
		.emit(pop)
	}
	 := -1
	if .Default != -1 {
		if .Default != 0 {
			[.Default] = len(.p.code)
			.emit(nil)
		}
	} else {
		 = len(.p.code)
		.emit(nil)
	}

	for ,  := range .Body {
		if .Test != nil ||  != 0 {
			.p.code[[]] = jump(len(.p.code) - [])
		}
		.compileStatements(.Consequent, )
	}

	if  != -1 {
		.p.code[] = jump(len(.p.code) - )
	}
	if  != nil {
		.leaveScopeBlock()
		.stackSize--
		.popScope()
	}
	.leaveBlock()
}

func ( *compiler) ( *ast.ClassDeclaration) {
	.emitLexicalAssign(.Class.Name.Name, int(.Class.Class)-1, .compileClassLiteral(.Class, false))
}