diff --git a/Clojure/Clojure/CljCompiler/Ast/CaseExpr.cs b/Clojure/Clojure/CljCompiler/Ast/CaseExpr.cs index d8575a76..17c2913c 100644 --- a/Clojure/Clojure/CljCompiler/Ast/CaseExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/CaseExpr.cs @@ -8,10 +8,7 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; using System; using System.Collections.Generic; using System.Linq; diff --git a/Clojure/Clojure/CljCompiler/Ast/ConstantExpr.cs b/Clojure/Clojure/CljCompiler/Ast/ConstantExpr.cs index 22e99295..65ef60a7 100644 --- a/Clojure/Clojure/CljCompiler/Ast/ConstantExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/ConstantExpr.cs @@ -46,6 +46,7 @@ public override bool HasClrType get { return _v.GetType().IsPublic + || _v.GetType().IsNestedPublic || typeof(Type).IsInstanceOfType(_v); // This bit of hackery is due to the fact that RuntimeType is not public. // Without this, System.Int64 would be seen as only type System.Object, not System.RuntimeType. } diff --git a/Clojure/Clojure/CljCompiler/Ast/FnExpr.cs b/Clojure/Clojure/CljCompiler/Ast/FnExpr.cs index e3643d10..f0916022 100644 --- a/Clojure/Clojure/CljCompiler/Ast/FnExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/FnExpr.cs @@ -8,15 +8,12 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; using System; using System.Collections.Generic; using System.Linq; -using System.Reflection.Emit; using System.Reflection; +using System.Reflection.Emit; namespace clojure.lang.CljCompiler.Ast { diff --git a/Clojure/Clojure/CljCompiler/Ast/FnMethod.cs b/Clojure/Clojure/CljCompiler/Ast/FnMethod.cs index 39c079e7..48253e04 100644 --- a/Clojure/Clojure/CljCompiler/Ast/FnMethod.cs +++ b/Clojure/Clojure/CljCompiler/Ast/FnMethod.cs @@ -8,15 +8,12 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; using System; using System.Collections.Generic; -using System.Text; -using System.Reflection.Emit; using System.Reflection; +using System.Reflection.Emit; +using System.Text; namespace clojure.lang.CljCompiler.Ast { diff --git a/Clojure/Clojure/CljCompiler/Ast/IfExpr.cs b/Clojure/Clojure/CljCompiler/Ast/IfExpr.cs index 97f5a429..07251910 100644 --- a/Clojure/Clojure/CljCompiler/Ast/IfExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/IfExpr.cs @@ -8,10 +8,7 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; using System; using System.Reflection.Emit; diff --git a/Clojure/Clojure/CljCompiler/Ast/IlGen.cs b/Clojure/Clojure/CljCompiler/Ast/IlGen.cs index 2ce3b601..5fef617d 100644 --- a/Clojure/Clojure/CljCompiler/Ast/IlGen.cs +++ b/Clojure/Clojure/CljCompiler/Ast/IlGen.cs @@ -8,11 +8,6 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - - using System; using System.Reflection; using System.Reflection.Emit; @@ -25,9 +20,16 @@ namespace clojure.lang.CljCompiler.Ast { public class CljILGen : ILGen { + private readonly ILGenerator _ilGenerator; + public ILGenerator ILGenerator + { + get { return _ilGenerator; } + } + public CljILGen(ILGenerator baseIlg) : base(baseIlg) { + _ilGenerator = baseIlg; } private static bool IsVolatile(FieldInfo fi) diff --git a/Clojure/Clojure/CljCompiler/Ast/InstanceFieldExpr.cs b/Clojure/Clojure/CljCompiler/Ast/InstanceFieldExpr.cs index 2bb647ec..0cf733b2 100644 --- a/Clojure/Clojure/CljCompiler/Ast/InstanceFieldExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/InstanceFieldExpr.cs @@ -8,10 +8,7 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; using System; using System.Reflection; using System.Reflection.Emit; diff --git a/Clojure/Clojure/CljCompiler/Ast/InvokeExpr.cs b/Clojure/Clojure/CljCompiler/Ast/InvokeExpr.cs index 9be11c0b..175c7775 100644 --- a/Clojure/Clojure/CljCompiler/Ast/InvokeExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/InvokeExpr.cs @@ -8,10 +8,7 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; using System; using System.Collections.Generic; using System.Reflection; @@ -170,10 +167,15 @@ public static Expr Parse(ParserContext pcon, ISeq form) int arity = RT.count(form.next()); object sigtag = SigTag(arity, v); object vtag = RT.get(RT.meta(v), RT.TagKey); - if (StaticInvokeExpr.Parse(v, RT.next(form), formTag ?? sigtag ?? vtag) is StaticInvokeExpr ret && !((Compiler.IsCompiling || Compiler.IsCompilingDefType) && GenContext.IsInternalAssembly(ret.Method.DeclaringType.Assembly))) + if (StaticInvokeExpr.Parse(v, RT.next(form), formTag ?? sigtag ?? vtag) is StaticInvokeExpr ret ) { + var isCompiling = Compiler.IsCompiling || Compiler.IsCompilingDefType; + var retAssembly = ret.Method.DeclaringType.Assembly; + var isInternal = GenContext.IsInternalAssembly(retAssembly); + if (! (isCompiling && isInternal)) + return ret; //Console.WriteLine("invoke direct: {0}", v); - return ret; + // return ret; } //Console.WriteLine("NOT direct: {0}", v); } diff --git a/Clojure/Clojure/CljCompiler/Ast/KeywordInvokeExpr.cs b/Clojure/Clojure/CljCompiler/Ast/KeywordInvokeExpr.cs index dd586d4a..ac75be4f 100644 --- a/Clojure/Clojure/CljCompiler/Ast/KeywordInvokeExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/KeywordInvokeExpr.cs @@ -8,10 +8,8 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ +using clojure.lang.CljCompiler.Context; using System; using System.Reflection.Emit; diff --git a/Clojure/Clojure/CljCompiler/Ast/LetExpr.cs b/Clojure/Clojure/CljCompiler/Ast/LetExpr.cs index 17e79888..6f80afe9 100644 --- a/Clojure/Clojure/CljCompiler/Ast/LetExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/LetExpr.cs @@ -8,10 +8,7 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; using System; using System.Collections.Generic; using System.Reflection.Emit; diff --git a/Clojure/Clojure/CljCompiler/Ast/LocalBinding.cs b/Clojure/Clojure/CljCompiler/Ast/LocalBinding.cs index f85b66b8..5c1eb361 100644 --- a/Clojure/Clojure/CljCompiler/Ast/LocalBinding.cs +++ b/Clojure/Clojure/CljCompiler/Ast/LocalBinding.cs @@ -59,6 +59,9 @@ public sealed class LocalBinding public LocalBinding(int index, Symbol sym, Symbol tag, Expr init, Type declaredType, bool isThis, bool isArg, bool isByRef) { + if (Compiler.MaybePrimitiveType(init) != null && tag != null) + throw new InvalidOperationException("Can't type hint a local with a primitive initializer"); + Index = index; _sym = sym; Tag = tag; diff --git a/Clojure/Clojure/CljCompiler/Ast/MethodExpr.cs b/Clojure/Clojure/CljCompiler/Ast/MethodExpr.cs index 977c69f7..cff8c5b7 100644 --- a/Clojure/Clojure/CljCompiler/Ast/MethodExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/MethodExpr.cs @@ -8,19 +8,15 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; +using clojure.lang.Runtime; +using clojure.lang.Runtime.Binding; using System; using System.Collections.Generic; -using System.Linq.Expressions; using System.Dynamic; +using System.Linq.Expressions; using System.Reflection; -using clojure.lang.Runtime.Binding; -using clojure.lang.Runtime; using System.Reflection.Emit; -using System.Linq; namespace clojure.lang.CljCompiler.Ast { @@ -168,7 +164,7 @@ public static void CacheDelegate(int key, Delegate d) private void EmitComplexCall(ObjExpr objx, CljILGen ilg) { - // TOD: We have gotten rid of light-compile. Simplify this. + // TODO: We have gotten rid of light-compile. Simplify this. // This is made more complex than I'd like by light-compiling. // Without light-compile, we could just: // Emit the target expression @@ -445,7 +441,7 @@ public static void EmitTypedArgs(ObjExpr objx, CljILGen ilg, ParameterInfo[] par { EmitTypedArg(objx, ilg, parms[i].ParameterType, args[i].ArgExpr); LocalBuilder loc = ilg.DeclareLocal(pi.ParameterType); -#if NET462 +#if NETFRAMEWORK loc.SetLocalSymInfo("_byRef_temp" + i); #endif ilg.Emit(OpCodes.Stloc, loc); diff --git a/Clojure/Clojure/CljCompiler/Ast/NewExpr.cs b/Clojure/Clojure/CljCompiler/Ast/NewExpr.cs index a42811d5..094baa15 100644 --- a/Clojure/Clojure/CljCompiler/Ast/NewExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/NewExpr.cs @@ -8,17 +8,14 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; +using clojure.lang.Runtime; +using clojure.lang.Runtime.Binding; using System; using System.Collections.Generic; -using System.Reflection; -using System.Linq.Expressions; using System.Dynamic; -using clojure.lang.Runtime.Binding; -using clojure.lang.Runtime; +using System.Linq.Expressions; +using System.Reflection; using System.Reflection.Emit; namespace clojure.lang.CljCompiler.Ast diff --git a/Clojure/Clojure/CljCompiler/Ast/NewInstanceExpr.cs b/Clojure/Clojure/CljCompiler/Ast/NewInstanceExpr.cs index b95dbbfc..350e9bad 100644 --- a/Clojure/Clojure/CljCompiler/Ast/NewInstanceExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/NewInstanceExpr.cs @@ -8,16 +8,12 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; +using Microsoft.Scripting.Generation; using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; -using System.Runtime.CompilerServices; -using Microsoft.Scripting.Generation; namespace clojure.lang.CljCompiler.Ast { @@ -170,7 +166,9 @@ internal static ObjExpr Build( ret._methodMap = overrideables; - + // TODO: This is a tempoaray fix until we figure out how to deal with PersistedAssemblyBuilder SAVE9 !!! + +#if NETFRAMEWORK GenContext context = Compiler.IsCompiling ? Compiler.CompilerContextVar.get() as GenContext : (ret.IsDefType @@ -178,6 +176,15 @@ internal static ObjExpr Build( : (Compiler.CompilerContextVar.get() as GenContext ?? Compiler.EvalContext)); +#else + GenContext context = Compiler.IsCompiling + ? Compiler.CompilerContextVar.get() as GenContext + : (ret.IsDefType + ? GenContext.CreateWithInternalAssembly("deftype" + RT.nextID().ToString(), true) + : (Compiler.CompilerContextVar.get() as GenContext + ?? + Compiler.EvalContext)); +#endif GenContext genC = context.WithNewDynInitHelper(ret.InternalName + "__dynInitHelper_" + RT.nextID().ToString()); @@ -391,6 +398,9 @@ private static void DefineBaseClassMethods(Type[] interfaces, TypeBuilder tb) { bool isExplicit = HasShadowedMethod(mi, impled); + var attrs = mi.Attributes; + var customs = mi.CustomAttributes; + EmitDummyMethod(tb, mi, isExplicit); if (!impled.ContainsKey(mi.Name)) @@ -440,7 +450,7 @@ private static bool ParametersMatch(MethodInfo m1, MethodInfo m2) // return t.FullName.Replace(',', '/'); //} - #endregion +#endregion #region Method reflection diff --git a/Clojure/Clojure/CljCompiler/Ast/NewInstanceMethod.cs b/Clojure/Clojure/CljCompiler/Ast/NewInstanceMethod.cs index a1b3a63e..bee221b9 100644 --- a/Clojure/Clojure/CljCompiler/Ast/NewInstanceMethod.cs +++ b/Clojure/Clojure/CljCompiler/Ast/NewInstanceMethod.cs @@ -8,10 +8,7 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; using System; using System.Collections.Generic; using System.Reflection; diff --git a/Clojure/Clojure/CljCompiler/Ast/Node.cs b/Clojure/Clojure/CljCompiler/Ast/Node.cs deleted file mode 100644 index 9612ee4e..00000000 --- a/Clojure/Clojure/CljCompiler/Ast/Node.cs +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) Rich Hickey. All rights reserved. - * The use and distribution terms for this software are covered by the - * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) - * which can be found in the file epl-v10.html at the root of this distribution. - * By using this software in any fashion, you are agreeing to be bound by - * the terms of this license. - * You must not remove this notice, or any other, from this software. - **/ - -/** - * Author: David Miller - **/ - -using Microsoft.Scripting; - -namespace clojure.lang.CljCompiler.Ast -{ - // TODO: Remove Node class - - /* - /// - /// Base class for AST expressions in the compiler. - /// - /// Stolen from IronPython. - public abstract class Node - { - #region Data - - private Microsoft.Scripting.SourceLocation _start = SourceLocation.Invalid; - private Microsoft.Scripting.SourceLocation _end = SourceLocation.Invalid; - - #endregion - - #region Location methods - - public void SetLoc(SourceLocation start, SourceLocation end) - { - _start = start; - _end = end; - } - - public void SetLoc(SourceSpan span) - { - _start = span.Start; - _end = span.End; - } - - public SourceLocation Start - { - get { return _start; } - set { _start = value; } - } - - public SourceLocation End - { - get { return _end; } - set { _end = value; } - } - - public SourceSpan Span - { - get - { - return new SourceSpan(_start, _end); - } - } - - #endregion - } - */ -} diff --git a/Clojure/Clojure/CljCompiler/Ast/ObjExpr.cs b/Clojure/Clojure/CljCompiler/Ast/ObjExpr.cs index 2da716cb..579eca48 100644 --- a/Clojure/Clojure/CljCompiler/Ast/ObjExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/ObjExpr.cs @@ -8,16 +8,13 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Reflection.Emit; using System.Reflection; -using System.Collections; +using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; @@ -65,37 +62,17 @@ public class ObjExpr : Expr public IPersistentVector ProtocolCallsites { get; protected set; } public IPersistentSet VarCallsites { get; protected set; } - - public IList KeywordLookupSiteFields { get; protected set; } - public IList ThunkFields { get; protected set; } - public IList CachedTypeFields { get; protected set; } - - internal FieldBuilder KeywordLookupSiteField(int i) - { - return KeywordLookupSiteFields[i]; - } - - internal FieldBuilder CachedTypeField(int i) - { - return CachedTypeFields[i]; - } - - internal FieldBuilder ThunkField(int i) - { - return ThunkFields[i]; - } - + internal FieldBuilder KeywordLookupSiteField(int i) => KeywordLookupSiteFields[i]; + internal FieldBuilder CachedTypeField(int i) => CachedTypeFields[i]; + internal FieldBuilder ThunkField(int i) => ThunkFields[i]; public FieldBuilder MetaField { get; protected set; } - public IList ClosedOverFields { get; protected set; } - public Dictionary ClosedOverFieldsMap { get; protected set; } - public Dictionary ClosedOverFieldsToBindingsMap { get; protected set; } public IPersistentVector HintedFields { get; protected set; } @@ -107,9 +84,8 @@ internal FieldBuilder ThunkField(int i) public bool OnceOnly { get; protected set; } public bool CanBeDirect { get; protected set; } - - protected bool IsDefType { get { return Fields != null; } } - protected virtual bool SupportsMeta { get { return !IsDefType; } } + protected bool IsDefType => Fields is not null; + protected virtual bool SupportsMeta => !IsDefType; internal bool IsVolatile(LocalBinding lb) { @@ -127,41 +103,18 @@ internal bool IsMutable(LocalBinding lb) lb.IsByRef; } - static String SiteName(int n) - { - return "__site__" + n; - } - - public static String SiteNameStatic(int n) - { - return SiteName(n) + "__"; - } - - static String ThunkName(int n) - { - return "__thunk__" + n; - } - - public static String ThunkNameStatic(int n) - { - return ThunkName(n) + "__"; - } - - internal static String CachedClassName(int n) - { - return "__cached_class__" + n; - } - - private static string ConstantName(int i) - { - return ConstPrefix + i; - } + static String SiteName(int n) => "__site__" + n; + public static String SiteNameStatic(int n) => SiteName(n) + "__"; + static String ThunkName(int n) => "__thunk__" + n; + public static String ThunkNameStatic(int n) => ThunkName(n) + "__"; + internal static String CachedClassName(int n) => "__cached_class__" + n; + private static string ConstantName(int i) => ConstPrefix + i; private Type ConstantType(int i) { object o = Constants.nth(i); Type t = o?.GetType(); - if (t != null && t.IsPublic) + if (t != null && (t.IsPublic || t.IsNestedPublic)) { // Java: can't emit derived fn types due to visibility if (typeof(LazySeq).IsAssignableFrom(t)) diff --git a/Clojure/Clojure/CljCompiler/Ast/ObjMethod.cs b/Clojure/Clojure/CljCompiler/Ast/ObjMethod.cs index 51ef5af5..84e84429 100644 --- a/Clojure/Clojure/CljCompiler/Ast/ObjMethod.cs +++ b/Clojure/Clojure/CljCompiler/Ast/ObjMethod.cs @@ -8,13 +8,10 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; using System; -using System.Reflection.Emit; using System.Reflection; +using System.Reflection.Emit; namespace clojure.lang.CljCompiler.Ast diff --git a/Clojure/Clojure/CljCompiler/Ast/StaticFieldExpr.cs b/Clojure/Clojure/CljCompiler/Ast/StaticFieldExpr.cs index b08a76e4..6fbc95ea 100644 --- a/Clojure/Clojure/CljCompiler/Ast/StaticFieldExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/StaticFieldExpr.cs @@ -8,10 +8,7 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; using System; using System.Reflection; using System.Reflection.Emit; diff --git a/Clojure/Clojure/CljCompiler/Ast/StaticInvokeExpr.cs b/Clojure/Clojure/CljCompiler/Ast/StaticInvokeExpr.cs index 729afd45..11b101db 100644 --- a/Clojure/Clojure/CljCompiler/Ast/StaticInvokeExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/StaticInvokeExpr.cs @@ -13,6 +13,7 @@ **/ using System; +using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; @@ -83,7 +84,16 @@ public static Expr Parse(Var v, ISeq args, object tag) Type target = v.get().GetType(); - MethodInfo[] allMethods = target.GetMethods(); + { + var directLinkMap = Compiler.DirectLinkingMapVar.deref() as Dictionary; + if (directLinkMap != null && directLinkMap.TryGetValue(v, out Type t)) + { + if (t != null) + target = t; + } + } + + MethodInfo[] allMethods = target.GetMethods(); bool variadic = false; int argcount = RT.count(args); MethodInfo method = null; diff --git a/Clojure/Clojure/CljCompiler/Ast/StaticMethodExpr.cs b/Clojure/Clojure/CljCompiler/Ast/StaticMethodExpr.cs index 9b572535..96f7a350 100644 --- a/Clojure/Clojure/CljCompiler/Ast/StaticMethodExpr.cs +++ b/Clojure/Clojure/CljCompiler/Ast/StaticMethodExpr.cs @@ -8,10 +8,7 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; using System; using System.Collections.Generic; using System.Reflection; diff --git a/Clojure/Clojure/CljCompiler/Compiler.cs b/Clojure/Clojure/CljCompiler/Compiler.cs index eb3a6b0e..ef29e4cb 100644 --- a/Clojure/Clojure/CljCompiler/Compiler.cs +++ b/Clojure/Clojure/CljCompiler/Compiler.cs @@ -8,23 +8,19 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Ast; +using clojure.lang.CljCompiler.Context; using System; +using System.Collections; using System.Collections.Generic; using System.ComponentModel; -using System.Text; using System.IO; -using System.Threading; -using clojure.lang.CljCompiler.Ast; using System.Reflection; using System.Reflection.Emit; -using System.Text.RegularExpressions; using System.Runtime.Serialization; -using System.Collections; -using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; namespace clojure.lang { @@ -40,16 +36,23 @@ public static class Compiler #region Duplicate types - static readonly Dictionary _duplicateTypeMap = new Dictionary(); + static readonly Dictionary _evalTypeMap = new Dictionary(); + static readonly Dictionary _compilerTypeMap = new Dictionary(); internal static void RegisterDuplicateType(Type type) { - _duplicateTypeMap[type.FullName] = type; + if (Compiler.IsCompiling) + _compilerTypeMap[type.FullName] = type; + else + _evalTypeMap[type.FullName] = type; } internal static Type FindDuplicateType(string typename) { - _duplicateTypeMap.TryGetValue(typename, out Type type); + if (Compiler.IsCompiling && _compilerTypeMap.TryGetValue(typename, out Type compiledType)) + { return compiledType; } + + _evalTypeMap.TryGetValue(typename, out Type type); return type; } @@ -197,7 +200,8 @@ internal static int ColumnVarDeref() internal static readonly Var CompilerContextVar = Var.create(null).setDynamic(); internal static readonly Var CompilerActiveVar = Var.create(false).setDynamic(); - + internal static readonly Var DirectLinkingMapVar = Var.create(null).setDynamic(); + public static Var CompilerOptionsVar; public static object GetCompilerOption(Keyword k) @@ -1691,7 +1695,8 @@ public static object Compile(GenContext context,TextReader rdr, string sourceDir RT.WarnOnReflectionVar, RT.WarnOnReflectionVar.deref(), RT.DataReadersVar, RT.DataReadersVar.deref(), CompilerContextVar, context, - CompilerActiveVar, true + CompilerActiveVar, true, + DirectLinkingMapVar, new Dictionary() )); try @@ -1776,7 +1781,17 @@ private static void Compile1(TypeBuilder tb, CljILGen ilg, ObjExpr objx, object objx.EmitConstantFieldDefs(tb); expr.Emit(RHC.Expression,objx,ilg); ilg.Emit(OpCodes.Pop); + + if ( expr is DefExpr dex && dex.Init is FnExpr fnx) + { + ((Dictionary)DirectLinkingMapVar.deref())[dex.Var] = fnx.CompiledType; + } + +#if NET9_0_OR_GREATER + DoSeparateEval(evPC, form); +#else expr.Eval(); +#endif } } finally @@ -1785,8 +1800,41 @@ private static void Compile1(TypeBuilder tb, CljILGen ilg, ObjExpr objx, object } } + private static void DoSeparateEval(ParserContext evPC, Object form) + { + // Reset the environment to avoid leaking the compile environment + + Var.pushThreadBindings(RT.mapUniqueKeys( + MethodVar, null, + LocalEnvVar, null, + LoopLocalsVar, null, + NextLocalNumVar, 0, + //RT.ReadEvalVar, true /* RT.T */, + //RT.CurrentNSVar, RT.CurrentNSVar.deref(), Do not reset this -- we need to see changes to this that might happen, e.g. with (ns ..) or (in-ns ...) calls + ConstantsVar, PersistentVector.EMPTY, + ConstantIdsVar, new IdentityHashMap(), + KeywordsVar, PersistentHashMap.EMPTY, + VarsVar, PersistentHashMap.EMPTY, + //RT.UncheckedMathVar, RT.UncheckedMathVar.deref(), + //RT.WarnOnReflectionVar, RT.WarnOnReflectionVar.deref(), + //RT.DataReadersVar, RT.DataReadersVar.deref(), + CompilerContextVar, null, + CompilerActiveVar, false, + DirectLinkingMapVar, null // we do not want to have the evaluated version try to direct link to functions in the compile assembly + )); + try + { + Compiler.eval(form); + } + finally + { + Var.popThreadBindings(); + } + } + + #endregion - + #region Loading internal static void LoadAssembly(FileInfo assyInfo, string relativePath) diff --git a/Clojure/Clojure/CljCompiler/Ast/DynInitHelper.cs b/Clojure/Clojure/CljCompiler/Context/DynInitHelper.cs similarity index 96% rename from Clojure/Clojure/CljCompiler/Ast/DynInitHelper.cs rename to Clojure/Clojure/CljCompiler/Context/DynInitHelper.cs index 03318074..c90cf8e0 100644 --- a/Clojure/Clojure/CljCompiler/Ast/DynInitHelper.cs +++ b/Clojure/Clojure/CljCompiler/Context/DynInitHelper.cs @@ -8,23 +8,20 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Ast; +using clojure.lang.Runtime.Binding; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; using System; using System.Collections.Generic; -using System.Text; -using System.Reflection.Emit; -using System.Reflection; using System.Linq.Expressions; -using Microsoft.Scripting.Generation; +using System.Reflection; +using System.Reflection.Emit; using System.Runtime.CompilerServices; -using Microsoft.Scripting.Utils; -using Microsoft.Scripting.Runtime; -using clojure.lang.Runtime.Binding; +using System.Text; -namespace clojure.lang.CljCompiler.Ast +namespace clojure.lang.CljCompiler.Context { class DynInitHelper @@ -32,9 +29,9 @@ class DynInitHelper #region Data int _id; - readonly AssemblyGen _assemblyGen; + readonly MyAssemblyGen _assemblyGen; TypeBuilder _typeBuilder; - TypeGen _typeGen; + MyTypeGen _typeGen; readonly string _typeName; @@ -63,7 +60,7 @@ public SiteInfo(FieldBuilder fb, Type st, CallSiteBinder binder, Type delegateTy #region Ctors and factories - public DynInitHelper(AssemblyGen ag, string typeName) + public DynInitHelper(MyAssemblyGen ag, string typeName) { _assemblyGen = ag; _typeName = typeName; @@ -111,13 +108,13 @@ private void MaybeInit() if (_typeBuilder == null) { _typeBuilder = _assemblyGen.DefinePublicType(_typeName, typeof(object), true); - _typeGen = new TypeGen(_assemblyGen, _typeBuilder); + _typeGen = new MyTypeGen(_assemblyGen, _typeBuilder); _siteInfos = new List(); } } - private Expression RewriteCallSite(CallSite site, TypeGen tg, Type delegateType, out SiteInfo siteInfo) + private Expression RewriteCallSite(CallSite site, MyTypeGen tg, Type delegateType, out SiteInfo siteInfo) { if (!(site.Binder is IExpressionSerializable)) { @@ -195,7 +192,7 @@ private bool ShouldRewriteDelegate(Type delegateType) { return false; } -#if NET462 +#if NETFRAMEWORK if (module.IsTransient()) { return true; diff --git a/Clojure/Clojure/CljCompiler/Ast/GenContext.cs b/Clojure/Clojure/CljCompiler/Context/GenContext.cs similarity index 66% rename from Clojure/Clojure/CljCompiler/Ast/GenContext.cs rename to Clojure/Clojure/CljCompiler/Context/GenContext.cs index 4e4f4ad6..36601ec2 100644 --- a/Clojure/Clojure/CljCompiler/Ast/GenContext.cs +++ b/Clojure/Clojure/CljCompiler/Context/GenContext.cs @@ -8,29 +8,28 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ -using System.Reflection.Emit; -using System.Reflection; -using System.Linq.Expressions; +using clojure.lang.CljCompiler.Ast; +using clojure.lang.Runtime; using Microsoft.Scripting.Generation; using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics.SymbolStore; -using clojure.lang.Runtime; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; using AstUtils = Microsoft.Scripting.Ast.Utils; -using System.Collections.Generic; -using System.Collections; -namespace clojure.lang.CljCompiler.Ast +namespace clojure.lang.CljCompiler.Context { public sealed class GenContext { #region Data - readonly AssemblyGen _assyGen; - public AssemblyGen AssemblyGen + readonly MyAssemblyGen _assyGen; + public MyAssemblyGen AssemblyGen { get { return _assyGen; } } @@ -77,28 +76,44 @@ public ISymbolDocumentWriter DocWriter #region C-tors & factory methods - private readonly static Dictionary InternalAssemblies = new Dictionary(); - - private static void AddInternalAssembly(Assembly a) + private class DefaultAssemblyComparer : IEqualityComparer { - lock (((ICollection)InternalAssemblies).SyncRoot) - { - InternalAssemblies[a] = true; - } + public bool Equals(Assembly x, Assembly y) => Object.ReferenceEquals(x, y); + public int GetHashCode(Assembly obj) => obj.GetHashCode(); } - public static bool IsInternalAssembly(Assembly a) + private readonly static ConcurrentDictionary _internalAssemblies = new ConcurrentDictionary(new DefaultAssemblyComparer()); + public static bool IsInternalAssembly(Assembly a) => a is not null && _internalAssemblies.ContainsKey(a); + + private static void AddInternalAssembly(GenContext ctx) { - lock (((ICollection)InternalAssemblies).SyncRoot) - { - return InternalAssemblies.ContainsKey(a); - } + // Sometime we are looking up via the AssemblyBuilder. + // But sometimes we are accessing via a method on a type. The defining assembly is actually a RuntimeAssembly, not the AssemblyBuilder itself. + // I'm not sure how else to find out what that RuntimeAssembly is, other than creating a type and a method and getting its assembly. + // We'll then register both the RuntimeAssembly and the AssemblyBuilder. + + var module = ctx.ModuleBuilder; + var dummyType = module.DefineType("__________________DummyType"); + var method = dummyType.DefineMethod("__________________DummyMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(void), Type.EmptyTypes); + var il = method.GetILGenerator(); + il.Emit(OpCodes.Ret); + + dummyType.CreateType(); + + var runtimeMethod = dummyType.GetMethod("__________________DummyMethod"); + var runtimeAssembly = runtimeMethod.DeclaringType.Assembly; + + _internalAssemblies[ctx.AssemblyBuilder] = true; + _internalAssemblies[runtimeAssembly] = true; } + + enum AssemblyType { Internal, External } + public static GenContext CreateWithInternalAssembly(string assyName, bool createDynInitHelper) { - GenContext ctx = CreateGenContext(assyName, assyName, ".dll", null, createDynInitHelper); - AddInternalAssembly(ctx.AssemblyBuilder); + GenContext ctx = CreateGenContext(AssemblyType.Internal, assyName, assyName, ".dll", null, createDynInitHelper); + AddInternalAssembly(ctx); return ctx; } @@ -112,7 +127,7 @@ public static GenContext CreateWithInternalAssembly(string assyName, bool create public static GenContext CreateWithExternalAssembly(string sourceName, string assyName, string extension, bool createDynInitHelper) { string path = Compiler.CompilePathVar.deref() as string; - return CreateGenContext(sourceName, assyName, extension, path ?? System.IO.Directory.GetCurrentDirectory(),createDynInitHelper); + return CreateGenContext(AssemblyType.External, sourceName, assyName, extension, path ?? System.IO.Directory.GetCurrentDirectory(),createDynInitHelper); } public static GenContext CreateWithExternalAssembly(string assyName, string extension, bool createDynInitHelper) @@ -120,7 +135,7 @@ public static GenContext CreateWithExternalAssembly(string assyName, string exte return CreateWithExternalAssembly(assyName, assyName, extension, createDynInitHelper); } - private static GenContext CreateGenContext(string sourceName, string assyName, string extension, string directory, bool createDynInitHelper) + private static GenContext CreateGenContext(AssemblyType assemblyType, string sourceName, string assyName, string extension, string directory, bool createDynInitHelper) { if (directory != null) { @@ -129,10 +144,10 @@ private static GenContext CreateGenContext(string sourceName, string assyName, s } AssemblyName aname = new AssemblyName(assyName); - return new GenContext(directory, aname, extension, createDynInitHelper, sourceName); + return new GenContext(assemblyType, directory, aname, extension, createDynInitHelper, sourceName); } - private GenContext(string directory, AssemblyName aname, string extension, bool createDynInitHelper, string sourceName) + private GenContext(AssemblyType assemblyType, string directory, AssemblyName aname, string extension, bool createDynInitHelper, string sourceName) { // TODO: Make this settable from a *debug* flag #if DEBUG @@ -141,7 +156,24 @@ private GenContext(string directory, AssemblyName aname, string extension, bool _isDebuggable = false; #endif - _assyGen = new AssemblyGen(aname, directory, extension, _isDebuggable); +#if NETFRAMEWORK || NET9_0_OR_GREATER + switch (assemblyType) + { + case AssemblyType.Internal: + _assyGen = new MyAssemblyGen(aname, _isDebuggable); + break; + case AssemblyType.External: + _assyGen = new MyAssemblyGen(aname, directory, extension, _isDebuggable); + break; + default: + throw new InvalidOperationException("Unknown AssemblyType"); + } +#else + _assyGen = new MyAssemblyGen(aname, _isDebuggable); +#endif + + + if (createDynInitHelper) _dynInitHelper = new DynInitHelper(_assyGen, GenerateName()); @@ -151,9 +183,12 @@ private GenContext(string directory, AssemblyName aname, string extension, bool Path = ComputeAssemblyPath(directory, aname.Name, extension); -#if NET462 +#if NETFRAMEWORK if (_isDebuggable) _docWriter = ModuleBuilder.DefineDocument(sourceName, ClojureContext.Default.LanguageGuid, ClojureContext.Default.VendorGuid, Guid.Empty); +#elif NET9_0_OR_GREATER + if (_isDebuggable && assemblyType == AssemblyType.External) + _docWriter = ModuleBuilder.DefineDocument(sourceName, ClojureContext.Default.LanguageGuid); #endif } @@ -208,13 +243,10 @@ internal void SaveAssembly() if ( _dynInitHelper != null ) _dynInitHelper.FinalizeType(); -#if NET462 +#if NETFRAMEWORK || NET9_0_OR_GREATER _assyGen.SaveAssembly(); #else Console.WriteLine("AOT-compilation not available"); - //var assembly = AssemblyBuilder; - //var generator = new Lokad.ILPack.AssemblyGenerator(); - //generator.GenerateAssembly(assembly,Path); #endif } @@ -243,22 +275,22 @@ public Expression MaybeAddDebugInfo(Expression expr, IPersistentMap spanMap) return expr; } - public static void EmitDebugInfo(ILGen ilg, IPersistentMap spanMap) + public static void EmitDebugInfo(CljILGen ilg, IPersistentMap spanMap) { if (Compiler.CompilerContextVar.deref() is GenContext context) context.MaybeEmitDebugInfo(ilg, spanMap); } - public void MaybeEmitDebugInfo(ILGen ilg, IPersistentMap spanMap) + public void MaybeEmitDebugInfo(CljILGen ilg, IPersistentMap spanMap) { -#if NET462 +#if NETFRAMEWORK || NET9_0_OR_GREATER if ( _docWriter != null && spanMap != null ) { if (Compiler.GetLocations(spanMap, out int startLine, out int startCol, out int finishLine, out int finishCol)) { try { - ilg.MarkSequencePoint(_docWriter, startLine, startCol, finishLine, finishCol); + ilg.ILGenerator.MarkSequencePoint(_docWriter, startLine, startCol, finishLine, finishCol); } catch (NotSupportedException) { @@ -277,7 +309,7 @@ public static void SetLocalName(LocalBuilder lb, string name) public void MaybSetLocalName(LocalBuilder lb, string name) { -#if NET462 +#if NETFRAMEWORK if (_isDebuggable) lb.SetLocalSymInfo(name); #endif diff --git a/Clojure/Clojure/CljCompiler/Context/MyAssemblyGen.cs b/Clojure/Clojure/CljCompiler/Context/MyAssemblyGen.cs new file mode 100644 index 00000000..6012872f --- /dev/null +++ b/Clojure/Clojure/CljCompiler/Context/MyAssemblyGen.cs @@ -0,0 +1,317 @@ +using Microsoft.Scripting.Utils; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Security; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + + +#if ! NETFRAMEWORK +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; +using System.Diagnostics.SymbolStore; +using System.Collections.Immutable; +#endif + +// This class is based on the original Microsoft code for Microsoft.Scripting.Gneeration.AssemblyGen. +// Lots of code copied here. +// +// // Licensed to the .NET Foundation under one or more agreements. +// // The .NET Foundation licenses this file to you under the Apache 2.0 License. +// // See the LICENSE file in the project root for more information. +// + +// Even before rolling my own version here, I had made my own versions of some its methods, such as MakeDelegateType. +// The main adaptation here is to allow for creating a PersistedAssemblyBuilder for when we are in creating a persisted assembly in the context of .NET 9 or later. +// To help make the code clearer to the outside and easier to implement, I created two constructors, one for persisted assemblies and one for non-persisted assemblies. +// The constructor for persisted assemblies is only visible in .NET Framework and .NET 9 or later. + +namespace clojure.lang.CljCompiler.Context; + +public sealed class MyAssemblyGen +{ + private readonly AssemblyBuilder _myAssembly; + private readonly ModuleBuilder _myModule; + private readonly bool _isDebuggable; + private readonly bool _isPersistable; + + private int _index; + + +#if NETFRAMEWORK || NET9_0_OR_GREATER + private readonly string _outFileName; // can be null iff not saveable + private readonly string _outDir; // null means the current directory +#endif + +#if NET9_0_OR_GREATER + MethodBuilder _entryPointMethodBuilder; // non-null means we have an entry point + ISymbolDocumentWriter _docWriter = null; // non-null means we are writing debug info +#endif + + internal AssemblyBuilder AssemblyBuilder => _myAssembly; + internal ModuleBuilder ModuleBuilder => _myModule; + internal bool IsDebuggable => _isDebuggable; + + // This is the constructor for the non-persisted assembly. + public MyAssemblyGen(AssemblyName name, bool isDebuggable) + { + ContractUtils.RequiresNotNull(name, nameof(name)); + + _myAssembly = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); + _myModule = _myAssembly.DefineDynamicModule(name.Name, isDebuggable); + _isDebuggable = isDebuggable; + _isPersistable = false; + +#if NETFRAMEWORK || NET9_0_OR_GREATER + _outFileName = null; + _outDir = null; +#endif + + if (isDebuggable) + { + SetDebuggableAttributes(); + } + } + +#if NETFRAMEWORK || NET9_0_OR_GREATER + + // This is the constructor for the persisted assembly. + public MyAssemblyGen(AssemblyName name, string outDir, string outFileExtension, bool isDebuggable, IDictionary attrs = null) + { + ContractUtils.RequiresNotNull(name, nameof(name)); + + if (outFileExtension == null) + { + outFileExtension = ".dll"; + } + + if (outDir != null) + { + try + { + outDir = Path.GetFullPath(outDir); + } + catch (Exception) + { + throw new InvalidOperationException("Invalid directory name"); + } + try + { + Path.Combine(outDir, name.Name + outFileExtension); + } + catch (ArgumentException) + { + throw new InvalidOperationException("Invalid assembly name or extension"); + } + + _outFileName = name.Name + outFileExtension; + _outDir = outDir; + } + + + // mark the assembly transparent so that it works in partial trust: + var attributes = new List { + new CustomAttributeBuilder(typeof(SecurityTransparentAttribute).GetConstructor(ReflectionUtils.EmptyTypes), ArrayUtils.EmptyObjects) }; + + if (attrs != null) + { + foreach (var attr in attrs) + { + if (!(attr.Value is string a) || string.IsNullOrWhiteSpace(a)) + { + continue; + } + + ConstructorInfo ctor = null; + switch (attr.Key) + { + case "assemblyFileVersion": + ctor = typeof(AssemblyFileVersionAttribute).GetConstructor(new[] { typeof(string) }); + break; + case "copyright": + ctor = typeof(AssemblyCopyrightAttribute).GetConstructor(new[] { typeof(string) }); + break; + case "productName": + ctor = typeof(AssemblyProductAttribute).GetConstructor(new[] { typeof(string) }); + break; + case "productVersion": + ctor = typeof(AssemblyInformationalVersionAttribute).GetConstructor(new[] { typeof(string) }); + break; + } + + if (ctor != null) + { + attributes.Add(new CustomAttributeBuilder(ctor, new object[] { a })); + } + } + } + +#if NETFRAMEWORK + _myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave, outDir, false, attributes); + _myModule = _myAssembly.DefineDynamicModule(name.Name, _outFileName, isDebuggable); + _myAssembly.DefineVersionInfoResource(); +#elif NET9_0_OR_GREATER + PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(name,typeof(object).Assembly, attributes); + _myAssembly = ab; + _myModule = ab.DefineDynamicModule(name.Name, isDebuggable); +#endif + _isPersistable = true; + _isDebuggable = isDebuggable; + + if (isDebuggable) { + SetDebuggableAttributes(); + } + + + } +#endif + + internal void SetDebuggableAttributes() + { + DebuggableAttribute.DebuggingModes attrs = + DebuggableAttribute.DebuggingModes.Default | + DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | + DebuggableAttribute.DebuggingModes.DisableOptimizations; + + Type[] argTypes = new Type[] { typeof(DebuggableAttribute.DebuggingModes) }; + Object[] argValues = new Object[] { attrs }; + + var debuggableCtor = typeof(DebuggableAttribute).GetConstructor(argTypes); + + _myAssembly.SetCustomAttribute(new CustomAttributeBuilder(debuggableCtor, argValues)); + _myModule.SetCustomAttribute(new CustomAttributeBuilder(debuggableCtor, argValues)); + } + + + public string SaveAssembly() + { + if (!_isPersistable) + { + throw new InvalidOperationException("Assembly is not persistable"); + } + +#if NETFRAMEWORK + _myAssembly.Save(_outFileName, PortableExecutableKinds.ILOnly, ImageFileMachine.I386); + return Path.Combine(_outDir, _outFileName); +#elif NET9_0_OR_GREATER + if ( _entryPointMethodBuilder is not null || _docWriter is not null) + SavePersistedAssemblyHard(); + else + ((PersistedAssemblyBuilder)_myAssembly).Save(_outFileName); + return Path.Combine(_outDir, _outFileName); +#else + return null; +#endif + + } + +#if NET9_0_OR_GREATER + private void SavePersistedAssemblyHard() + { + PersistedAssemblyBuilder ab = (PersistedAssemblyBuilder)_myAssembly; + MetadataBuilder metadataBuilder = ab.GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData, out MetadataBuilder pdbBuilder); + + MethodDefinitionHandle entryPointHandle = + _entryPointMethodBuilder is null + ? default(MethodDefinitionHandle) + : MetadataTokens.MethodDefinitionHandle(_entryPointMethodBuilder.MetadataToken); + DebugDirectoryBuilder debugDirectoryBuilder = GeneratePdb(pdbBuilder, metadataBuilder.GetRowCounts(), entryPointHandle); + + ManagedPEBuilder peBuilder = new( + header: PEHeaderBuilder.CreateExecutableHeader(), + metadataRootBuilder: new MetadataRootBuilder(metadataBuilder), + ilStream: ilStream, + mappedFieldData: fieldData, + debugDirectoryBuilder: debugDirectoryBuilder, + entryPoint: entryPointHandle); + + BlobBuilder peBlob = new(); + peBuilder.Serialize(peBlob); + + // Create the executable: + using FileStream fileStream = new(_outFileName, FileMode.Create, FileAccess.Write); + peBlob.WriteContentTo(fileStream); + } + + static DebugDirectoryBuilder GeneratePdb(MetadataBuilder pdbBuilder, ImmutableArray rowCounts, MethodDefinitionHandle entryPointHandle) + { + BlobBuilder portablePdbBlob = new BlobBuilder(); + PortablePdbBuilder portablePdbBuilder = new PortablePdbBuilder(pdbBuilder, rowCounts, entryPointHandle); + BlobContentId pdbContentId = portablePdbBuilder.Serialize(portablePdbBlob); + // In case saving PDB to a file + using FileStream fileStream = new FileStream("MyAssemblyEmbeddedSource.pdb", FileMode.Create, FileAccess.Write); + portablePdbBlob.WriteContentTo(fileStream); + + DebugDirectoryBuilder debugDirectoryBuilder = new DebugDirectoryBuilder(); + debugDirectoryBuilder.AddCodeViewEntry("MyAssemblyEmbeddedSource.pdb", pdbContentId, portablePdbBuilder.FormatVersion); + // In case embedded in PE: + // debugDirectoryBuilder.AddEmbeddedPortablePdbEntry(portablePdbBlob, portablePdbBuilder.FormatVersion); + return debugDirectoryBuilder; + } +#endif + + +#if NETFRAMEWORK + internal void SetEntryPoint(MethodInfo mi, PEFileKinds kind) + { + _myAssembly.SetEntryPoint(mi, kind); + } +#elif NET9_0_OR_GREATER + internal void SetEntryPoint(MethodBuilder mb) + { + _entryPointMethodBuilder = mb; + } +#endif + + + public TypeBuilder DefinePublicType(string name, Type parent, bool preserveName) + { + return DefineType(name, parent, TypeAttributes.Public, preserveName); + } + + internal TypeBuilder DefineType(string name, Type parent, TypeAttributes attr, bool preserveName) + { + ContractUtils.RequiresNotNull(name, nameof(name)); + ContractUtils.RequiresNotNull(parent, nameof(parent)); + + StringBuilder sb = new StringBuilder(name); + if (!preserveName) + { + int index = Interlocked.Increment(ref _index); + sb.Append("$"); + sb.Append(index); + } + + // There is a bug in Reflection.Emit that leads to + // Unhandled Exception: System.Runtime.InteropServices.COMException (0x80131130): Record not found on lookup. + // if there is any of the characters []*&+,\ in the type name and a method defined on the type is called. + sb.Replace('+', '_').Replace('[', '_').Replace(']', '_').Replace('*', '_').Replace('&', '_').Replace(',', '_').Replace('\\', '_'); + + name = sb.ToString(); + + return _myModule.DefineType(name, attr, parent); + } + + + private const MethodAttributes CtorAttributes = MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public; + private const MethodImplAttributes ImplAttributes = MethodImplAttributes.Runtime | MethodImplAttributes.Managed; + private const MethodAttributes InvokeAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; + private const TypeAttributes DelegateAttributes = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass; + private static readonly Type[] _DelegateCtorSignature = new Type[] { typeof(object), typeof(IntPtr) }; + + public Type MakeDelegateType(string name, Type[] parameters, Type returnType) + { + TypeBuilder builder = DefineType(name, typeof(MulticastDelegate), DelegateAttributes, false); + builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _DelegateCtorSignature).SetImplementationFlags(ImplAttributes); + builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes); + return builder.CreateTypeInfo(); + } + +} diff --git a/Clojure/Clojure/CljCompiler/Context/MyTypeGen.cs b/Clojure/Clojure/CljCompiler/Context/MyTypeGen.cs new file mode 100644 index 00000000..da6ca877 --- /dev/null +++ b/Clojure/Clojure/CljCompiler/Context/MyTypeGen.cs @@ -0,0 +1,93 @@ +// I had to copy Microsoft.Scripting.Generation.TypeGen.cs to this project and modify it to use MyAssemblyGen instead of Microsoft.Scripting.Generation.AssemblyGen. +// The latter is sealed, so I couldn't just derive. Sigh. + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +//#if FEATURE_REFEMIT + +using System; +using System.Reflection; +using System.Reflection.Emit; +using clojure.lang.CljCompiler.Context; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Utils; + +namespace clojure.lang.CljCompiler.Context; + +public sealed class MyTypeGen { + private ILGen _initGen; // The IL generator for the .cctor() + + /// + /// Gets the Compiler associated with the Type Initializer (cctor) creating it if necessary. + /// + public ILGen TypeInitializer { + get { + if (_initGen == null) { + _initGen = new ILGen(TypeBuilder.DefineTypeInitializer().GetILGenerator()); + } + return _initGen; + } + } + + internal MyAssemblyGen AssemblyGen { get; } + + public TypeBuilder TypeBuilder { get; } + + public MyTypeGen(MyAssemblyGen myAssembly, TypeBuilder myType) { + Assert.NotNull(myAssembly, myType); + + AssemblyGen = myAssembly; + TypeBuilder = myType; + } + + public override string ToString() { + return TypeBuilder.ToString(); + } + + public Type FinishType() { + _initGen?.Emit(OpCodes.Ret); + Type ret = TypeBuilder.CreateTypeInfo(); + return ret; + } + + public FieldBuilder AddStaticField(Type fieldType, string name) { + return TypeBuilder.DefineField(name, fieldType, FieldAttributes.Public | FieldAttributes.Static); + } + + public FieldBuilder AddStaticField(Type fieldType, FieldAttributes attributes, string name) { + return TypeBuilder.DefineField(name, fieldType, attributes | FieldAttributes.Static); + } + + public ILGen DefineExplicitInterfaceImplementation(MethodInfo baseMethod) { + ContractUtils.RequiresNotNull(baseMethod, nameof(baseMethod)); + + MethodAttributes attrs = baseMethod.Attributes & ~(MethodAttributes.Abstract | MethodAttributes.Public); + attrs |= MethodAttributes.NewSlot | MethodAttributes.Final; + + Type[] baseSignature = baseMethod.GetParameters().Map(p => p.ParameterType); + MethodBuilder mb = TypeBuilder.DefineMethod( + baseMethod.DeclaringType.Name + "." + baseMethod.Name, + attrs, + baseMethod.ReturnType, + baseSignature); + + TypeBuilder.DefineMethodOverride(mb, baseMethod); + return new ILGen(mb.GetILGenerator()); + } + + private const MethodAttributes MethodAttributesToEraseInOveride = MethodAttributes.Abstract | MethodAttributes.ReservedMask; + + // TODO: Use ReflectionUtils.DefineMethodOverride? + public ILGen DefineMethodOverride(MethodInfo baseMethod) { + MethodAttributes finalAttrs = baseMethod.Attributes & ~MethodAttributesToEraseInOveride; + Type[] baseSignature = baseMethod.GetParameters().Map(p => p.ParameterType); + MethodBuilder mb = TypeBuilder.DefineMethod(baseMethod.Name, finalAttrs, baseMethod.ReturnType, baseSignature); + + TypeBuilder.DefineMethodOverride(mb, baseMethod); + return new ILGen(mb.GetILGenerator()); + } +} + +//#endif diff --git a/Clojure/Clojure/CljCompiler/GenClass.cs b/Clojure/Clojure/CljCompiler/GenClass.cs index 8f50e847..e9dccd4e 100644 --- a/Clojure/Clojure/CljCompiler/GenClass.cs +++ b/Clojure/Clojure/CljCompiler/GenClass.cs @@ -8,21 +8,18 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler; +using clojure.lang.CljCompiler.Ast; +using clojure.lang.CljCompiler.Context; +using Microsoft.Scripting.Generation; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; -using clojure.lang.CljCompiler; -using clojure.lang.CljCompiler.Ast; -using Microsoft.Scripting.Generation; namespace clojure.lang -{ +{ public static class GenClass { #region Data @@ -415,7 +412,7 @@ static void EmitMain(GenContext context, TypeBuilder proxyTB, string mainName, F gen.MarkLabel(endLabel); gen.Emit(OpCodes.Ret); -#if NET462 +#if NETFRAMEWORK //context.AssyBldr.SetEntryPoint(cb); context.AssemblyBuilder.SetEntryPoint(cb); #endif diff --git a/Clojure/Clojure/CljCompiler/GenDelegate.cs b/Clojure/Clojure/CljCompiler/GenDelegate.cs index 25b65b44..4f7584f0 100644 --- a/Clojure/Clojure/CljCompiler/GenDelegate.cs +++ b/Clojure/Clojure/CljCompiler/GenDelegate.cs @@ -8,15 +8,11 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; using System; using System.Collections.Generic; -using System.Reflection; using System.Linq.Expressions; -using clojure.lang.CljCompiler.Ast; +using System.Reflection; namespace clojure.lang diff --git a/Clojure/Clojure/CljCompiler/GenInterface.cs b/Clojure/Clojure/CljCompiler/GenInterface.cs index 490dc4fb..3332c515 100644 --- a/Clojure/Clojure/CljCompiler/GenInterface.cs +++ b/Clojure/Clojure/CljCompiler/GenInterface.cs @@ -8,15 +8,12 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ +using clojure.lang.CljCompiler.Context; using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; -using clojure.lang.CljCompiler.Ast; namespace clojure.lang { @@ -40,8 +37,13 @@ public static Type GenerateInterface(string iName, IPersistentMap attributes, Se } else // TODO: In CLR4, should create a collectible type? - context = GenContext.CreateWithExternalAssembly(iName+"_"+RT.nextID(), ".dll", false); + // TODO: This is temporry until we get things sorted out: SAVE9 !!! +#if NETFRAMEWORK + context = GenContext.CreateWithExternalAssembly(iName+"_"+RT.nextID(), ".dll", false); +#else + context = GenContext.CreateWithInternalAssembly(iName + "_" + RT.nextID(), false); +#endif for (ISeq s = RT.seq(extends); s != null; s = s.next()) { object f = s.first(); @@ -73,12 +75,12 @@ public static Type GenerateInterface(string iName, IPersistentMap attributes, Se //if ( Compiler.IsCompiling ) // context.SaveAssembly(); - Compiler.RegisterDuplicateType(t); + Compiler.RegisterDuplicateType(t); return t; } - #endregion +#endregion #region Fun with attributes diff --git a/Clojure/Clojure/CljCompiler/GenProxy.cs b/Clojure/Clojure/CljCompiler/GenProxy.cs index f85e2da4..5dc27955 100644 --- a/Clojure/Clojure/CljCompiler/GenProxy.cs +++ b/Clojure/Clojure/CljCompiler/GenProxy.cs @@ -8,18 +8,14 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler; +using clojure.lang.CljCompiler.Ast; +using clojure.lang.CljCompiler.Context; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; -using clojure.lang.CljCompiler; -using clojure.lang.CljCompiler.Ast; -using Microsoft.Scripting.Generation; using System.Runtime.Serialization; namespace clojure.lang @@ -122,6 +118,10 @@ Type Generate(Type superclass, ISeq interfaces, IPersistentMap attributes, strin Type t = proxyTB.CreateType(); //if (Compiler.IsCompiling) // SaveProxyContext(); + + if (Compiler.IsCompiling) + Compiler.RegisterDuplicateType(t); + return t; } diff --git a/Clojure/Clojure/Lib/RT.cs b/Clojure/Clojure/Lib/RT.cs index b4107df8..2909137b 100644 --- a/Clojure/Clojure/Lib/RT.cs +++ b/Clojure/Clojure/Lib/RT.cs @@ -8,18 +8,16 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Context; +using clojure.lang.Runtime; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Utils; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Net; -using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -27,9 +25,6 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; -using clojure.lang.CljCompiler.Ast; -using clojure.lang.Runtime; -using Microsoft.Scripting.Hosting; using RTProperties = clojure.runtime.Properties; @@ -65,7 +60,7 @@ static IEnumerable GetAllTypesInNamespace(string nspace) .SelectMany(t => getTypes(t)) .Where(t => (t.IsClass || t.IsInterface || t.IsValueType) && t.Namespace == nspace && - t.IsPublic && + (t.IsPublic || t.IsNestedPublic) && !t.IsGenericTypeDefinition && !t.Name.StartsWith("_") && !t.Name.StartsWith("<")); @@ -454,7 +449,18 @@ static RT() try { +#if NETFRAMEWORK + Console.WriteLine("Framework!"); +#elif NET9_0_OR_GREATER + Console.WriteLine("Net 9! (or later)"); +#else + Console.WriteLine("must be 6 or 8"); +#endif + + RT.StartStopwatch(); load("clojure/core"); + RT.StopStopwatch(); + Console.WriteLine("Clojure core loaded in " + _stopwatch.ElapsedMilliseconds + " milliseconds."); } finally { @@ -547,7 +553,7 @@ public static void PostBootstrapInit() } } - #endregion +#endregion #region Id generation @@ -2767,12 +2773,16 @@ public static Type classForName(string p) // e.g. System.Environment in assemblies System.Private.CoreLib and System.Runtime.Exceptions. // It is private in the former and public in the latter. // Unfortunately, Type.GetType was finding the former. - if (t != null && t.IsPublic) + if (t != null && (t.IsPublic || t.IsNestedPublic)) + { return t; + } t = Compiler.FindDuplicateType(p); if (t != null) + { return t; + } AppDomain domain = AppDomain.CurrentDomain; Assembly[] assys = domain.GetAssemblies(); @@ -2783,42 +2793,41 @@ public static Type classForName(string p) foreach (Assembly assy in assys) { Type t1 = assy.GetType(p, false); - if (t1 != null && t1.IsPublic) + if (t1 != null && (t1.IsPublic || t1.IsNestedPublic)) + { return t1; + } } // slow path, will succeed for display names (returned by Type.Name) // e.g. "Transform" foreach (Assembly assy1 in assys) { - Type t1 = assy1.GetType(p, false); + Type t1 = null; if (IsRunningOnMono) { // I do not know why Assembly.GetType fails to find types in our assemblies in Mono - if (t1 == null) + if (!assy1.IsDynamic) { - if (!assy1.IsDynamic) + try { - try - { - foreach (Type tt in assy1.GetTypes()) + foreach (Type tt in assy1.GetTypes()) + { + if (tt.Name.Equals(p)) { - if (tt.Name.Equals(p)) - { - t1 = tt; - break; - } + t1 = tt; + break; } } - catch (System.Reflection.ReflectionTypeLoadException) - { - } + } + catch (System.Reflection.ReflectionTypeLoadException) + { } } } - + if (t1 != null && !candidateTypes.Contains(t1)) candidateTypes.Add(t1); } @@ -2831,7 +2840,13 @@ public static Type classForName(string p) t = null; if (t == null && p.IndexOfAny(_triggerTypeChars) != -1) + { t = ClrTypeSpec.GetTypeFromName(p); + if (t != null) + { + return t; + } + } return t; } @@ -3260,7 +3275,7 @@ public static string StringReverse(string input) #endregion - # region Loading/compiling + #region Loading/compiling public static void load(String relativePath) diff --git a/Clojure/Clojure/Runtime/Binding/ClojureInvokeMemberBinder.cs b/Clojure/Clojure/Runtime/Binding/ClojureInvokeMemberBinder.cs index f54e4113..32b6c0b1 100644 --- a/Clojure/Clojure/Runtime/Binding/ClojureInvokeMemberBinder.cs +++ b/Clojure/Clojure/Runtime/Binding/ClojureInvokeMemberBinder.cs @@ -8,21 +8,19 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ +using clojure.lang.CljCompiler.Ast; +using clojure.lang.CljCompiler.Context; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Actions.Calls; +using Microsoft.Scripting.Runtime; using System; using System.Collections.Generic; +using System.Dynamic; using System.Linq; using System.Linq.Expressions; -using System.Dynamic; -using Microsoft.Scripting.Runtime; -using Microsoft.Scripting.Actions; -using Microsoft.Scripting.Actions.Calls; using System.Reflection; using System.Reflection.Emit; -using clojure.lang.CljCompiler.Ast; namespace clojure.lang.Runtime.Binding { diff --git a/Clojure/Clojure/Runtime/Reflector.cs b/Clojure/Clojure/Runtime/Reflector.cs index 824409e4..55cebbda 100644 --- a/Clojure/Clojure/Runtime/Reflector.cs +++ b/Clojure/Clojure/Runtime/Reflector.cs @@ -8,24 +8,21 @@ * You must not remove this notice, or any other, from this software. **/ -/** - * Author: David Miller - **/ - +using clojure.lang.CljCompiler.Ast; +using clojure.lang.CljCompiler.Context; +using clojure.lang.Runtime; +using clojure.lang.Runtime.Binding; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Actions.Calls; +using Microsoft.Scripting.Runtime; using System; using System.Collections.Generic; +using System.Dynamic; using System.Linq; -using System.Reflection; using System.Linq.Expressions; -using Microsoft.Scripting.Actions; -using System.Dynamic; -using Microsoft.Scripting.Actions.Calls; -using Microsoft.Scripting.Runtime; -using clojure.lang.CljCompiler.Ast; -using System.Text; -using clojure.lang.Runtime.Binding; -using clojure.lang.Runtime; +using System.Reflection; using System.Runtime.CompilerServices; +using System.Text; namespace clojure.lang { diff --git a/Clojure/Csharp.Tests/LibTests/FutureTests.cs b/Clojure/Csharp.Tests/LibTests/FutureTests.cs index e83cbb2a..baf65c3e 100644 --- a/Clojure/Csharp.Tests/LibTests/FutureTests.cs +++ b/Clojure/Csharp.Tests/LibTests/FutureTests.cs @@ -74,7 +74,7 @@ public void PropagatesExceptions() } } -#if NET462 +#if NETFRAMEWORK // Thread.Abort not supported in .Net Core [Test] @@ -112,7 +112,7 @@ public void CancelFailsAfterSuccessfulCompletion() Expect(f.isCancelled()).To.Equal(false); } -#if NET462 +#if NETFRAMEWORK // Thread.Abort not supported in .Net Core [Test] [ExpectedException(typeof(FutureAbortedException))] diff --git a/Clojure/Csharp.Tests/LibTests/GenProxyTests.cs b/Clojure/Csharp.Tests/LibTests/GenProxyTests.cs index 2f71a6a0..dd2ca94f 100644 --- a/Clojure/Csharp.Tests/LibTests/GenProxyTests.cs +++ b/Clojure/Csharp.Tests/LibTests/GenProxyTests.cs @@ -821,7 +821,7 @@ public class Impl3 : Impl2 { } -#if NET462 +#if NETFRAMEWORK [Test] public void FindTypeInAssembly() { @@ -922,7 +922,7 @@ public interface I1 int m2(int x); } -#if NET462 +#if NETFRAMEWORK [Test] public void CanCreateConcreteImplementationOverAbstractProperty() {