// generateStates generates the ES5 states for the given CodeDOM statement. func (sg *stateGenerator) generateStates(statement codedom.Statement, option generateStatesOption) *state { // If already constructed, just return the same state. This handles states that callback to // earlier states. if startState, ok := sg.stateStartMap[statement]; ok { return startState } // Determine whether we have to create a new state for this statement. A new state is required if the // statement is marked as referencable (which means another state will need to jump to it) or if a new // state was explicitly requested. currentState := sg.currentState if option == generateNewState || statement.IsReferenceable() { // Add the new state. newState := sg.newState() // If the statement is referencable, then the reason we added the new state was to allow // for jumping "in between" and otherwise single state. Therefore, control flow must immediately // go from the existing state to the new state (as it would normally naturally flow) if statement.IsReferenceable() { currentState.pushSnippet(sg.snippets().SetState(newState.ID)) currentState.pushSnippet("continue;") } } // Cache the state for the statement. startState := sg.currentState sg.stateStartMap[statement] = startState // Add the mapping comment for the statement. switch e := statement.(type) { case *codedom.EmptyStatementNode: // Nothing to do. break case *codedom.ResolutionNode: sg.generateResolution(e) case *codedom.RejectionNode: sg.generateRejection(e) case *codedom.YieldNode: sg.generateYield(e) case *codedom.ExpressionStatementNode: sg.generateExpressionStatement(e) case *codedom.VarDefinitionNode: sg.generateVarDefinition(e) case *codedom.ResourceBlockNode: sg.generateResourceBlock(e) case *codedom.ConditionalJumpNode: sg.generateConditionalJump(e) case *codedom.UnconditionalJumpNode: sg.generateUnconditionalJump(e) case *codedom.ArrowPromiseNode: sg.generateArrowPromise(e) case *codedom.ResolveExpressionNode: sg.generateResolveExpression(e) default: panic(fmt.Sprintf("Unknown CodeDOM statement: %T", statement)) } // If the statement releases flow, then we immediately add a new state and jump to it // before returning. if statement.ReleasesFlow() { endState := sg.currentState newState := sg.newState() endState.pushSnippet(sg.snippets().SetState(newState.ID)) endState.pushSnippet("return;") } // Handle generation of chained statements. if n, ok := statement.(codedom.HasNextStatement); ok && n.GetNext() != nil { next := n.GetNext() sg.generateStates(next, generateNextState) } else if option != generateImplicitState { sg.currentState.leafState = true } return startState }