예제 #1
0
// jumpToStatement generates an unconditional jump to the target statement.
func (sg *stateGenerator) jumpToStatement(target codedom.Statement) string {
	// Check for resources that will be out of scope once the jump occurs.
	resources := sg.resources.OutOfScope(target.BasisNode())
	if len(resources) == 0 {
		// No resources are moving out of scope, so simply set the next state.
		targetState := sg.generateStates(target, generateNewState)
		return sg.snippets().SetState(targetState.ID)
	}

	// Pop off any resources out of scope.
	data := struct {
		PopFunction codedom.RuntimeFunction
		Resources   []resource
		Snippets    snippets
		TargetState *state
		IsGenerator bool
	}{codedom.StatePopResourceFunction, resources, sg.snippets(), sg.generateStates(target, generateNewState), sg.isGeneratorFunction}

	popTemplateStr := `
		{{ .PopFunction }}({{ range $index, $resource := .Resources }}{{ if $index }}, {{ end }} '{{ $resource.Name }}' {{ end }}).then(function() {
			{{ .Snippets.SetState .TargetState.ID }}
			{{ .Snippets.Continue .IsGenerator }}
		}).catch(function(err) {
			{{ .Snippets.Reject "err" }}
		});
	`

	return sg.templater.Execute("popjump", popTemplateStr, data)
}
예제 #2
0
// 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
}