/* * Copyright (c) 2006-2008 The University of Wroclaw. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. The name of the University may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY ``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 UNIVERSITY 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. */ using Nemerle.Collections; using Nemerle.Compiler; using Nemerle.Compiler.Parsetree; namespace Nemerle { /** * This macro implements memoization (with the default option Scope = Instance), * as well as something closer to aggressive sharing, when Scope = Class is used. * A Synchronized = true | false option is also available, to set thread safety. */ [Nemerle.MacroUsage (Nemerle.MacroPhase.WithTypedMembers, Nemerle.MacroTargets.Method)] macro Memoize (tb : TypeBuilder, meth : MethodBuilder, params opts : list [PExpr]) { mutable scope = "Instance"; mutable synch = false; foreach (o in opts) { | <[ Scope = Instance ]> => scope = "Instance" | <[ Scope = Class ]> => scope = "Class" | <[ Scope = $other ]> => Message.Error ($"Invalid parameter: Scope = $other. Valid options are Instance (default) and Class.") | <[ Synchronized = $(opt : bool) ]> => synch = opt | <[ Synchronized = $other ]> => Message.Error ($"Invalid parameter: Synchronized = $other. Valid options are true and false (default).") | _ => () // for backwards compatibility } def parms = meth.GetParameters (); def newBody = Util.locate(meth.Body.Location, match (parms) { | [] => def cached_value = Macros.NewSymbol ("cached_value"); def is_cached = Macros.NewSymbol ("is_cached"); match (scope) { | "Instance" => tb.Define (<[ decl: mutable $(cached_value : name) : $(meth.ReturnType : typed); ]>); tb.Define (<[ decl: mutable $(is_cached : name) : bool; ]>) | "Class" => tb.Define (<[ decl: static mutable $(cached_value : name) : $(meth.ReturnType : typed); ]>); tb.Define (<[ decl: static mutable $(is_cached : name) : bool; ]>) | _ => () // unreachable } <[ when (! $(is_cached : name)) { $(cached_value : name) = $(meth.Body); $(is_cached : name) = true; } $(cached_value : name) ]> | (prm :: _) => def cache = Macros.NewSymbol ("cache"); match (MType.ConstructFunctionType (meth.GetHeader ())) { | Fun (t1, t2) => match (scope) { | "Instance" => tb.Define (<[ decl: mutable $(cache : name) : Hashtable [$(t1 : typed), $(t2 : typed)]; ]>); | "Class" => tb.Define (<[ decl: static mutable $(cache : name) : Hashtable [$(t1 : typed), $(t2 : typed)]; ]>); | _ => () // unreachable } } def parm_values = if (parms.Length > 1) <[ (.. $(List.Map (parms, fun (p) { <[ $(p.name : usesite) ]> })) )]> else <[ $(prm.name : usesite) ]>; <[ when ($(cache : name) == null) // Can't this initialization by done without the additional check each time ? $(cache : name) = Hashtable (); match ($(cache : name).Get ($parm_values)) { | Some (ret) => ret | None => def ret = $(meth.Body); // Does that code make sense ?;] I've never used threading stuff before.. $(if (synch) <[ lock ($(cache : name)) { $(cache : name).Add ($parm_values, ret) } ]> else <[ $(cache : name).Add ($parm_values, ret) ]>); ret } ]>; }); meth.Body = newBody; } }