/* * Copyright (c) 2004-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.Utility; using Nemerle.Compiler; using Nemerle.Compiler.Typedtree; using SRE = System.Reflection.Emit; namespace Nemerle.Compiler { /** This module is used to decode and encode Nemerle specific information about types, methods, etc. which are not directly expressible in .NET metadata. We use custom attributes to save / read this data in emitted / loaded assemblies. This module is stateless. */ module TyCodec { variant Term { | App { name : string; args : list [Term]; } } // --------------- DECODING ------------------------- ParseTerm (s : string) : Term { def get_name (pos) { def idx = s.IndexOf ('(', pos); assert (idx != -1); (idx + 1, s.Substring (pos, idx - pos)) }; def maybe_get (pos) { if (s[pos] == ')') (pos + 1, None ()) else { def (pos, name) = get_name (pos); def (pos, args) = get_list ([], pos); (pos, Some (Term.App (name, args))) } } and get_list (acc, pos) { match (maybe_get (pos)) { | (pos, None) => (pos, List.Rev (acc)) | (pos, Some (x)) => get_list (x :: acc, pos) } }; match (maybe_get (0)) { | (pos, Some (ret)) => assert (pos == s.Length); ret | (_, None) => assert (false); } } decode (lib : LibraryReference, tenv : Map [string, StaticTyVar], t : Term) : MType { def self (t) { decode (lib, tenv, t) : TyVar }; match (t) { | Term.App (name, args) when name[0] != '.' => match (lib.Manager.NameTree.LookupExactType (NString.Split (name, array ['.', '+']))) { | Some (tc) => if (tc.FullName == "System.Void") lib.Manager.InternalType.Void else MType.Class (tc, List.Map (args, self)).Expand () | None => match (lib.LookupInternalType (name)) { | Some (tc) => MType.Class (tc, args.Map(self)).Expand () | None => Message.Error($"unbound type name: $name, are you missing a reference?"); lib.Manager.InternalType.Void } } | Term.App (".a", [Term.App (srank, []), t]) => MType.Array (decode (lib, tenv, t), System.Int32.Parse (srank)) | Term.App (".r", [t]) => MType.Ref (decode (lib, tenv, t)) | Term.App (".o", [t]) => MType.Out (decode (lib, tenv, t)) | Term.App (".f", [t1, t2]) => MType.Fun (decode (lib, tenv, t1), decode (lib, tenv, t2)) | Term.App (".v", [Term.App (no, [])]) => match (tenv.Find (no)) { | Some (tv) => MType.TyVarRef (tv) | None => Util.ice ("unboud type variable in encoded type " + no) } | Term.App (".p", types) => MType.Tuple (List.Map (types, self)) | Term.App (name, _) => Util.ice ("invalid encoded type opcode " + name) } } reflect_typarms (_lib : LibraryReference, mutable tenv : Map [string, StaticTyVar], _t : System.Type) : list [StaticTyVar] * Map [string, StaticTyVar] { mutable tyvars = []; def vars = _t.GetGenericArguments (); foreach (gparm in vars) { def tv = StaticTyVar (_lib.Manager, gparm.Name, gparm); tyvars = tv :: tyvars; tenv = tenv.Replace (gparm.Name, tv) } tyvars = List.Rev (tyvars); (tyvars, tenv) } public ReflectConstraints (_lib : LibraryReference, tenv : Map [string, StaticTyVar], sys_type : System.Type, self_type : MType, tyvars : list [StaticTyVar]) : void { def vars = sys_type.GetGenericArguments (); def set_constraints (t, tv : StaticTyVar) { def constraints = t.GetGenericParameterConstraints (); tv.SetConstraints (t.GenericParameterAttributes, List.MapFromArray (constraints, fun (t) { if (t.Equals(sys_type)) self_type else _lib.TypeOfType (tenv, t) })) }; NArray.Iter2 (vars, tyvars, set_constraints); } /** * Used to decode Nemerle types extracted from assembly metadata */ public DecodeType (lib : LibraryReference, tenv : Map [string, StaticTyVar], tokens : string) : MType { decode (lib, tenv, ParseTerm (tokens)) } public ReflectTypeBuilder (lib : LibraryReference, tenv : Map [string, StaticTyVar], original : System.Type) : list [StaticTyVar] * Map [string, StaticTyVar] * list [MType.Class] * list [MType.Class] { def (tyvars, tenv) = reflect_typarms (lib, tenv, original); def typeof_type (framework_type) { def tc = lib.TypeInfoOfType (framework_type); if (framework_type.IsGenericType) { def parms = List.MapFromArray (framework_type.GetGenericArguments (), fun (x) { lib.TypeOfType (tenv, x) }); MType.Class (tc, parms) } else MType.Class (tc, []) } def system_ifaces = original.GetInterfaces (); def direct_system_ifaces = Typer.GetMinimal (List.FromArray (system_ifaces), fun (t1, t2) { t2.IsAssignableFrom (t1) }); // compute direct supertypes mutable dst = []; foreach (ty in direct_system_ifaces) dst ::= typeof_type (ty); match (original.BaseType) { | null => () | t => dst ::= typeof_type (t) } // compute all supertypes mutable st = []; mutable st_unique = []; def add_bt (t : System.Type) { | null => () | _ => def mtype = typeof_type (t); unless (st_unique.Contains (mtype.tycon)) { st ::= mtype; st_unique ::= mtype.tycon; add_bt (t.BaseType) } }; add_bt (original.BaseType); foreach (x in system_ifaces) add_bt (x); // Message.Debug ($"reflecting $original, created dts $dst"); (tyvars, tenv, dst, st) } public ReflectTyparms (lib : LibraryReference, mutable tenv : Map [string, StaticTyVar], meth : System.Reflection.MethodBase) : list [StaticTyVar] * Map [string, StaticTyVar] { if (meth.IsGenericMethodDefinition) { mutable tyvars = []; def vars = meth.GetGenericArguments (); foreach (gparm in vars) { def tv = StaticTyVar (lib.Manager, gparm.Name, gparm); tyvars = tv :: tyvars; tenv = tenv.Replace (gparm.Name, tv) } tyvars = tyvars.Reverse (); def set_constraints (t, tv : StaticTyVar) { def constraints = t.GetGenericParameterConstraints (); tv.SetConstraints (t.GenericParameterAttributes, List.MapFromArray (constraints, fun (t) { lib.TypeOfType (tenv, t) })) }; NArray.Iter2 (vars, tyvars, set_constraints); (tyvars, tenv) } else ([], tenv) } // -------------- ENCODING ---------------------- FlattenTerm (t : Term) : string { def ret = System.Text.StringBuilder (); def walk (t) { | Term.App (name, args) => ignore (ret.Append (name)); ignore (ret.Append ('(')); List.Iter (args, walk); ignore (ret.Append (')')); }; walk (t); ret.ToString () } encode_tvs (t : list [TyVar]) : list [Term] { List.Map (t, encode_tv) } encode_tv (t : TyVar) : Term { encode (t.Fix ()) } encode (t : MType) : Term { | MType.Array (t, rank) => Term.App (".a", [Term.App (rank.ToString (), []), encode_tv (t)]) | MType.TyVarRef (tv) => Term.App (".v", [Term.App (tv.Name.ToString (), [])]) | MType.Void => Term.App ("System.Void", []) | MType.Fun (arg, rt) => Term.App (".f", [encode_tv (arg), encode_tv (rt)]) | MType.Tuple (args) => Term.App (".p", encode_tvs (args)) | MType.Class (ti, args) => Term.App (ti.FrameworkTypeName, encode_tvs (args)) | MType.Ref (t) => Term.App (".r", [encode_tv (t)]) | MType.Out (t) => Term.App (".o", [encode_tv (t)]) | MType.Intersection => assert (false) } /** * Used to emit Nemerle types in assembly metadata * * * The type tree is converted to prefix, term-like notation. * */ public EncodeType (t : MType) : string { FlattenTerm (encode (t)) } } } // ns