Skip to content

AsyncMethodAttribute

rollynoel edited this page Jun 14, 2013 · 3 revisions

Added by dholton dholton

See the Async attribute in the examples folder included with boo.

Below is an updated version of this attribute that could be included with Boo.Lang.Useful: ``boo #region license // Copyright (c) 2005, Sorin Ionescu // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of Sorin Ionescu nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion

namespace Boo.Lang.Useful.Attributes

import System; import System.Runtime.Remoting.Messaging import Boo.Lang.Compiler import Boo.Lang.Compiler.Ast import Boo.Lang.Compiler.Steps

class AsyncMethodAttribute(AbstractAstAttribute): """ Adds the asynchronous helpers Begin and End for a method. """ _method as Method _accessModifiers as TypeMemberModifiers _disposed as ReferenceExpression

def constructor():
	pass
	
def constructor(disposed as ReferenceExpression):
	_disposed = disposed

override def Apply(node as Node):	
	assert node isa Method
	
	_method = node
	
	# Set the wrapper methods to the same accessibility
	# as the wrappedmethod.
	if _method.IsProtected and _method.IsInternal:
		_accessModifiers = TypeMemberModifiers.Protected
		_accessModifiers = _accessModifiers | TypeMemberModifiers.Internal
		
	elif _method.IsPublic:
		_accessModifiers = TypeMemberModifiers.Public
		
	elif _method.IsProtected:
		_accessModifiers = TypeMemberModifiers.Protected
		
	elif _method.IsInternal:
		_accessModifiers = TypeMemberModifiers.Internal
		
	elif _method.IsPrivate:
		_accessModifiers = TypeMemberModifiers.Private
	
	CreateBeginMethod()
	CreateEndMethod()

private def CreateBeginMethod():
	beginMethod = Method(
		LexicalInfo: LexicalInfo,
		Name: "Begin${_method.Name}",
		Modifiers: _accessModifiers,
		ReturnType: SimpleTypeReference(LexicalInfo, "System.IAsyncResult"))
	
	# Copy the BeginInvoke parameters:
	beginMethod.Parameters.ExtendWithClones(_method.Parameters)
	beginMethod.Parameters.Add(ParameterDeclaration(
		LexicalInfo: LexicalInfo,
		"callback",
		SimpleTypeReference(LexicalInfo, "System.AsyncCallback")))
		
	beginMethod.Parameters.Add(
		ParameterDeclaration(
			LexicalInfo: LexicalInfo,
			"state",
			SimpleTypeReference(LexicalInfo, "object")))
						 
	# Creates the delegate that will call the wrapped method asynchronously.
	asyncInvocation = MethodInvocationExpression(
		LexicalInfo: LexicalInfo,
		Target: AstUtil.CreateReferenceExpression(
			"${_method.Name}.BeginInvoke"))
	
	for parameter in beginMethod.Parameters:
		asyncInvocation.Arguments.Add(
			ReferenceExpression(LexicalInfo, parameter.Name))
	
	if _disposed is not null:
		CreateDisposedObjectCheck(beginMethod)
		
	beginMethod.Body.Add(
		ReturnStatement(
			LexicalInfo: LexicalInfo,
			Expression: asyncInvocation))
	
	_method.DeclaringType.Members.Add(beginMethod)
	
private def CreateEndMethod():
	endMethod = Method(
		LexicalInfo: LexicalInfo,
		Name: "End${_method.Name}",
		Modifiers: _accessModifiers)
	
	# Copy the EndInvoke parameters: EndMethod(result as IAsyncResult).
	endMethod.Parameters.Add(
		ParameterDeclaration(
			LexicalInfo: LexicalInfo,
			"result",
			SimpleTypeReference(LexicalInfo, "System.IAsyncResult")))
	
	# Creates the delegate that will call the wrapped method asynchronously.
	asyncInvocation = MethodInvocationExpression(
		LexicalInfo: LexicalInfo,
		Target: AstUtil.CreateReferenceExpression(
			"${_method.Name}.EndInvoke"))
	
	asyncInvocation.Arguments.Add(
		ReferenceExpression(LexicalInfo, "result"))
	
	if _disposed is not null:
		CreateDisposedObjectCheck(endMethod)
	
	endMethod.Body.Add(
		ReturnStatement(
			LexicalInfo: LexicalInfo,
			Expression: asyncInvocation))
	
	_method.DeclaringType.Members.Add(endMethod)
	
	# Cache the voidType reference because the CompilerContext
	# will be lost after this method returns.
	# See AbstractCompilerComponent.Dispose().
	voidType = Context.TypeSystemServices.VoidType
	
	Context.Parameters.Pipeline.AfterStep += def (
		sender as object,
		e as CompilerStepEventArgs):
			
		if e.Step isa ProcessMethodBodies:
			if _method.ReturnType.Entity is voidType:
				returnStatement = endMethod.Body.Statements[-1] as ReturnStatement
				
				endMethod.Body.Statements.Replace(
					returnStatement,
					ExpressionStatement(
						LexicalInfo: LexicalInfo,
						Expression: returnStatement.Expression))
						
private def CreateDisposedObjectCheck(method as Method):
	exceptionCreation = MethodInvocationExpression(
		LexicalInfo: LexicalInfo,
		Target: AstUtil.CreateReferenceExpression(
			"System.ObjectDisposedException"))
	
	exceptionCreation.Arguments.Add(
		StringLiteralExpression(LexicalInfo, _method.DeclaringType.Name))
	
	trueBlock = Block()
	trueBlock.Add(
		RaiseStatement(
			LexicalInfo: LexicalInfo,
			Exception: exceptionCreation))
	
	method.Body.Add(
		IfStatement(LexicalInfo, _disposed.CloneNode(), trueBlock, null))
Clone this wiki locally