Example #1
0
// addTopLevelExpression generates the source for the given expression and adds it to the
// state machine.
func (sg *stateGenerator) addTopLevelExpression(expression codedom.Expression) esbuilder.SourceBuilder {
	// Generate the expression.
	result := expressiongenerator.GenerateExpression(expression, expressiongenerator.AllowedSync,
		sg.scopegraph, sg.positionMapper,
		sg.generateMachine)

	// Add any variables generated by the expression.
	for _, varName := range result.Variables() {
		sg.addVariable(varName)
	}

	// If the expression generated is asynchronous, then it will have at least one
	// promise callback. In order to ensure continued execution, we have to wrap
	// the resulting expression in a jump to a new state once it has finished executing.
	if result.IsAsync() {
		// Add $result, as it is needed below.
		sg.addVariable("$result")
		sg.hasAsyncJump = true

		// Save the current state and create a new state to jump to once the expression's
		// promise has resolved.
		currentState := sg.currentState
		targetState := sg.newState()

		// Wrap the expression's promise result expression to be stored in $result,
		// followed by a jump to the new state.
		data := struct {
			ResolutionStateId stateId
			Snippets          snippets
			IsGenerator       bool
		}{targetState.ID, sg.snippets(), sg.isGeneratorFunction}

		wrappingTemplateStr := `
			$result = {{ emit .ResultExpr }};
			{{ .Data.Snippets.SetState .Data.ResolutionStateId }}
			{{ .Data.Snippets.Continue .Data.IsGenerator }}
		`
		promise := result.BuildWrapped(wrappingTemplateStr, data)

		// Wrap the expression's promise with a catch, in case it fails.
		catchTemplateStr := `
			({{ emit .Item }}).catch(function(err) {
				{{ .Snippets.Reject "err" }}
			});
			return;
		`

		currentState.pushBuilt(esbuilder.Template("tlecatch", catchTemplateStr, generatingItem{promise, sg}))
		return esbuilder.Identifier("$result")
	} else {
		// Otherwise, the expression is synchronous and we can just invoke it.
		return result.Build()
	}
}
Example #2
0
// generateResolveExpression generates the code for an expression resolution.
func (sg *stateGenerator) generateResolveExpression(resolveExpression *codedom.ResolveExpressionNode) {
	// Generate the resolved expression, requiring that it is asynchronous to ensure it becomes
	// a Promise.
	result := expressiongenerator.GenerateExpression(resolveExpression.ChildExpression,
		expressiongenerator.EnsureAsync,
		sg.scopegraph, sg.positionMapper,
		sg.generateMachine)

	var resolutionName = ""
	var rejectionName = ""

	if resolveExpression.ResolutionName != "" {
		resolutionName = sg.addVariable(resolveExpression.ResolutionName)
	}

	if resolveExpression.RejectionName != "" {
		rejectionName = sg.addVariable(resolveExpression.RejectionName)
	}

	// Save the current state and create a new state to jump to once the expression's
	// promise has resolved or rejected.
	currentState := sg.currentState
	sg.generateStates(resolveExpression.Target, generateNewState)

	// Build the expression with an assignment of the resolved expression value assigned to
	// the resolution variable (if any) and then a jump to the post-resolution state.
	jumpToTarget := sg.jumpToStatement(resolveExpression.Target)
	resolveData := struct {
		ResolutionName string
		RejectionName  string
		JumpToTarget   string
		Snippets       snippets
		IsGenerator    bool
	}{resolutionName, rejectionName, jumpToTarget, sg.snippets(), sg.isGeneratorFunction}

	wrappingTemplateStr := `
		{{ if .Data.ResolutionName }}
		{{ .Data.ResolutionName }} = {{ emit .ResultExpr }};
		{{ end }}

		{{ if .Data.RejectionName }}
		{{ .Data.RejectionName }} = null;
		{{ end }}

		{{ .Data.JumpToTarget }}
		{{ .Data.Snippets.Continue .Data.IsGenerator }}
	`

	promise := result.BuildWrapped(wrappingTemplateStr, resolveData)

	// Similarly, add a .catch onto the Promise with an assignment of the rejected error (if any)
	// to the rejection variable (if any) and then a jump to the post-rejection state.
	rejectData := struct {
		Promise        esbuilder.SourceBuilder
		ResolutionName string
		RejectionName  string
		JumpToTarget   string
		Snippets       snippets
		IsGenerator    bool
	}{promise, resolutionName, rejectionName, jumpToTarget, sg.snippets(), sg.isGeneratorFunction}

	catchTemplateStr := `
		({{ emit .Promise }}).catch(function($rejected) {
			{{ if .RejectionName }}
			{{ .RejectionName }} = $rejected;
			{{ end }}
			{{ if .ResolutionName }}
			{{ .ResolutionName }} = null;
			{{ end }}

			{{ .JumpToTarget }}
			{{ .Snippets.Continue .IsGenerator }}
		});
		return;
	`

	currentState.pushBuilt(esbuilder.Template("resolvecatch", catchTemplateStr, rejectData))
}