// NOTE: May be called on invalid definitions // See also: InlinedMethodCache::PrimIsInlinable public bool IsInlinable(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, InstanceState state) { if (!env.InlinedMethods.CouldBeInlinableBasedOnHeaderAlone(assemblyDef, typeDef, methodDef)) return false; if (methodDef.IsStatic && methodDef.IsConstructor) // Static constructors are invoked by type initializers return false; if (!IsImported(assemblyDef, typeDef, methodDef)) // Not an imported method return false; if (!methodDef.IsStatic && methodDef.IsConstructor) { // Instance constructors need too much surrounding logic to be worth inlining, // but constructors for 'Merged' types must be inlined return state == InstanceState.Merged; } var outer = typeDef.OuterPropertyOrEvent(methodDef.MethodSignature); if (outer != null && outer.Flavor == CST.MemberDefFlavor.Event) // Event adders/removers need too much surrounding logic to be worth inlining return false; var isInline = default(bool); if (env.AttributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, env.AttributeHelper.InlineAttributeRef, env.AttributeHelper.TheIsInlinedProperty, true, false, ref isInline)) // User has specified whether or not to inline, which overrides size-based determination return isInline; var script = default(JST.Expression); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.TheScriptProperty, true, false, ref script); // Use script size, even though actual function size may be a bit larger after adjusting for // various import flavors return script == null || script.Size <= env.ImportInlineThreshold; }
// NOTE: May be called on invalid definitions public bool IsImported(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef) { var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); var outerDef = typeDef.OuterPropertyOrEvent(methodDef.MethodSignature); if (outerDef != null) { switch (outerDef.Flavor) { case CST.MemberDefFlavor.Event: { var eventDef = (CST.EventDef)outerDef; if (eventDef.Add != null && eventDef.Remove != null) { var n = 0; if (attributeHelper.MethodHasAttribute (assemblyDef, typeDef, typeDef.ResolveMethod(eventDef.Add), attributeHelper.ImportAttributeRef, false, false)) n++; if (attributeHelper.MethodHasAttribute (assemblyDef, typeDef, typeDef.ResolveMethod(eventDef.Remove), attributeHelper.ImportAttributeRef, false, false)) n++; if (n == 1) { env.Log (new InvalidInteropMessage (ctxt, "events with adders and removers must be imported simultaneously")); throw new DefinitionException(); } } break; } case CST.MemberDefFlavor.Property: { var propDef = (CST.PropertyDef)outerDef; if (propDef.Get != null && propDef.Set != null) { var n = 0; if (attributeHelper.MethodHasAttribute (assemblyDef, typeDef, typeDef.ResolveMethod(propDef.Get), attributeHelper.ImportAttributeRef, false, false)) n++; if (attributeHelper.MethodHasAttribute (assemblyDef, typeDef, typeDef.ResolveMethod(propDef.Set), attributeHelper.ImportAttributeRef, false, false)) n++; if (n == 1) { env.Log (new InvalidInteropMessage (ctxt, "properties with getters and setters must be imported simultaneously")); throw new DefinitionException(); } } break; } case CST.MemberDefFlavor.Field: case CST.MemberDefFlavor.Method: throw new InvalidOperationException("not a property or event"); default: throw new ArgumentOutOfRangeException(); } } if (IsExtern(assemblyDef, typeDef, methodDef)) { if (attributeHelper.MethodHasAttribute (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, true, false)) { if (attributeHelper.MethodHasAttribute (assemblyDef, typeDef, methodDef, env.Global.DllImportAttributeRef, false, false)) { env.Log(new InvalidInteropMessage(ctxt, "cannot mix 'Import' and 'DllImport' attributes")); throw new DefinitionException(); } return true; } else return false; } else { if (attributeHelper.MethodHasAttribute (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, false, false)) { if (outerDef == null) { env.Log (new InvalidInteropMessage (ctxt, "cannot Import a method which already has an implementation")); throw new DefinitionException(); } // else: C# doesn't allow extern properties, so be forgiving here return true; } return false; } }
private JST.Expression RecasePropertyEvent(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression script) { var outer = typeDef.OuterPropertyOrEvent(methodDef.MethodSignature); if (outer == null) throw new ArgumentException("not a getter/setter/adder/remover method"); if (script != null) return script; var casing = default(Casing); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.TheMemberNameCasingProperty, true, false, ref casing); return new JST.Identifier(JST.Lexemes.StringToIdentifier(Recase(outer.Name, casing))).ToE(); }
public void AppendExport(JST.NameSupply nameSupply, JST.Identifier rootId, CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression instance, Seq<JST.Statement> body, AppendCallExported appendCallExported) { var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); if (methodDef.TypeArity > 0) { env.Log(new InvalidInteropMessage(ctxt, "polymorphic methods cannot be exported")); throw new DefinitionException(); } if (typeDef.Arity > 0 && (methodDef.IsConstructor || methodDef.IsStatic)) { env.Log (new InvalidInteropMessage (ctxt, "higher-kinded types cannot export static methods or instance constructors")); throw new DefinitionException(); } CheckParameterAndReturnTypesAreImportableExportable(assemblyDef, typeDef, methodDef); var script = default(JST.Expression); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheScriptProperty, true, false, ref script); var outer = typeDef.OuterPropertyOrEvent(methodDef.MethodSignature); if (!methodDef.IsStatic && methodDef.IsConstructor) { // Constructors if (methodDef.TypeArity > 0) throw new InvalidOperationException("invalid constructor"); } else if (outer != null) { var localScript = default(JST.Expression); var hasLocalScript = attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheScriptProperty, false, false, ref localScript); switch (outer.Flavor) { case CST.MemberDefFlavor.Event: { var eventDef = (CST.EventDef)outer; if (eventDef.Add != null && methodDef.Signature.Equals(eventDef.Add)) // Adder script = hasLocalScript ? GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "add", localScript) : GetterSetterAdderRemoverNameFromPropertyEvent (assemblyDef, typeDef, methodDef, "add", script); else if (eventDef.Remove != null && methodDef.Signature.Equals(eventDef.Remove)) // Remover script = hasLocalScript ? GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "remove", localScript) : GetterSetterAdderRemoverNameFromPropertyEvent (assemblyDef, typeDef, methodDef, "remove", script); else throw new InvalidOperationException(); break; } case CST.MemberDefFlavor.Property: { var propDef = (CST.PropertyDef)outer; if (propDef.Get != null && methodDef.Signature.Equals(propDef.Get)) // Getter script = hasLocalScript ? GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "get", localScript) : GetterSetterAdderRemoverNameFromPropertyEvent (assemblyDef, typeDef, methodDef, "get", script); else if (propDef.Set != null && methodDef.Signature.Equals(propDef.Set)) // Setter script = hasLocalScript ? GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "set", localScript) : GetterSetterAdderRemoverNameFromPropertyEvent (assemblyDef, typeDef, methodDef, "set", script); else throw new InvalidOperationException(); break; } case CST.MemberDefFlavor.Field: case CST.MemberDefFlavor.Method: throw new InvalidOperationException("not a property or event"); default: throw new ArgumentOutOfRangeException(); } } else { // Normal methods script = RecaseMethod(assemblyDef, typeDef, methodDef, script); } script = PrefixName(assemblyDef, typeDef, methodDef, script, true); AppendFinalExport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, instance, body, appendCallExported); }
public JST.Expression AppendImport(JST.NameSupply nameSupply, JST.Identifier rootId, CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, ISeq<JST.Statement> body, IImSeq<JST.Expression> arguments) { var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); CheckParameterAndReturnTypesAreImportableExportable(assemblyDef, typeDef, methodDef); var script = default(JST.Expression); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.TheScriptProperty, true, false, ref script); if (!methodDef.IsStatic && methodDef.IsConstructor) { // Constructor if (script == null) { var creation = default(Creation); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.TheCreationProperty, true, false, ref creation); switch (creation) { case Creation.Constructor: script = PrefixName(assemblyDef, typeDef, methodDef, null, false); break; case Creation.Object: if (arguments.Count > 0) { env.Log (new InvalidInteropMessage (ctxt, "imported constructors for object literals cannot have arguments")); throw new DefinitionException(); } script = Constants.Object.ToE(); break; case Creation.Array: script = Constants.Array.ToE(); break; default: throw new ArgumentOutOfRangeException(); } var call = AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); return new JST.NewExpression(call); } else if (script is JST.FunctionExpression) return AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); else { script = PrefixName(assemblyDef, typeDef, methodDef, script, false); var call = AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); return new JST.NewExpression(call); } } else { var outer = typeDef.OuterPropertyOrEvent(methodDef.MethodSignature); if (outer != null) { var isOnMethod = attributeHelper.MethodHasAttribute (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, false, false); var localScript = default(JST.Expression); if (isOnMethod) attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.TheScriptProperty, false, false, ref localScript); switch (outer.Flavor) { case CST.MemberDefFlavor.Property: { var propDef = (CST.PropertyDef)outer; if (propDef.Get != null && methodDef.Signature.Equals(propDef.Get)) { // Getter if (isOnMethod) { script = PrefixName (assemblyDef, typeDef, methodDef, GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "get", localScript), false); return AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); } else if (script != null && script is JST.FunctionExpression) { env.Log (new InvalidInteropMessage (ctxt, "property import script cannot be a function")); throw new DefinitionException(); } else { if (script == null && arguments.Count == 2 && !methodDef.IsStatic) return new JST.IndexExpression(arguments[0], arguments[1]); else { script = PrefixName (assemblyDef, typeDef, methodDef, RecasePropertyEvent(assemblyDef, typeDef, methodDef, script), false); if (methodDef.IsStatic && arguments.Count == 0) return script; else if (!methodDef.IsStatic && arguments.Count == 1) return JST.Expression.Dot (arguments[0], JST.Expression.ExplodePath(script)); else { env.Log (new InvalidInteropMessage (ctxt, "additional getter parameters not supported for default getters")); throw new DefinitionException(); } } } } else if (propDef.Set != null && methodDef.Signature.Equals(propDef.Set)) { // Setter if (isOnMethod) { script = PrefixName (assemblyDef, typeDef, methodDef, GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "set", localScript), false); return AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); } else if (script != null && script is JST.FunctionExpression) { env.Log (new InvalidInteropMessage (ctxt, "property import script cannot be a function")); throw new DefinitionException(); } else { if (script == null && arguments.Count == 3 && !methodDef.IsStatic) return new JST.BinaryExpression (new JST.IndexExpression(arguments[0], arguments[1]), JST.BinaryOp.Assignment, arguments[2]); else { script = PrefixName (assemblyDef, typeDef, methodDef, RecasePropertyEvent(assemblyDef, typeDef, methodDef, script), false); if (methodDef.IsStatic && arguments.Count == 1) return new JST.BinaryExpression (script, JST.BinaryOp.Assignment, arguments[0]); else if (!methodDef.IsStatic && arguments.Count == 2) return new JST.BinaryExpression (JST.Expression.Dot(arguments[0], JST.Expression.ExplodePath(script)), JST.BinaryOp.Assignment, arguments[1]); else { env.Log (new InvalidInteropMessage (ctxt, "additional setter parameters not supported for default setters")); throw new DefinitionException(); } } } } else throw new InvalidOperationException(); } case CST.MemberDefFlavor.Event: { var eventDef = (CST.EventDef)outer; // XREF1201 if (eventDef.Add != null && methodDef.Signature.Equals(eventDef.Add)) { // Adder if (isOnMethod) { script = PrefixName (assemblyDef, typeDef, methodDef, GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "add", localScript), false); return AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); } else if (script != null && script is JST.FunctionExpression) { env.Log (new InvalidInteropMessage(ctxt, "event import script cannot be a function")); throw new DefinitionException(); } else { // The delegate argument has already taken account of the combine, so // just a field assignment script = PrefixName (assemblyDef, typeDef, methodDef, RecasePropertyEvent(assemblyDef, typeDef, methodDef, script), false); if (methodDef.IsStatic && arguments.Count == 1) return new JST.BinaryExpression(script, JST.BinaryOp.Assignment, arguments[0]); else if (!methodDef.IsStatic && arguments.Count == 2) return new JST.BinaryExpression (JST.Expression.Dot(arguments[0], JST.Expression.ExplodePath(script)), JST.BinaryOp.Assignment, arguments[1]); else throw new InvalidOperationException("mismatched event adder arity"); } } else if (eventDef.Remove != null && methodDef.Signature.Equals(eventDef.Remove)) { // Remover if (isOnMethod) { script = PrefixName (assemblyDef, typeDef, methodDef, GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "remove", localScript), false); return AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); } else if (script != null && script is JST.FunctionExpression) { env.Log (new InvalidInteropMessage(ctxt, "event import script cannot be a function")); throw new DefinitionException(); } else { // The delegate argument has already taken account of the delete, so // just a field assignment script = PrefixName (assemblyDef, typeDef, methodDef, RecasePropertyEvent(assemblyDef, typeDef, methodDef, script), false); if (methodDef.IsStatic && arguments.Count == 1) return new JST.BinaryExpression(script, JST.BinaryOp.Assignment, arguments[0]); else if (!methodDef.IsStatic && arguments.Count == 2) return new JST.BinaryExpression (JST.Expression.Dot(arguments[0], JST.Expression.ExplodePath(script)), JST.BinaryOp.Assignment, arguments[1]); else throw new InvalidOperationException("mismatched event remover arity"); } } else throw new InvalidOperationException(); } case CST.MemberDefFlavor.Field: case CST.MemberDefFlavor.Method: throw new InvalidOperationException("outer is not property or event"); default: throw new ArgumentOutOfRangeException(); } } else { // Normal method script = PrefixName (assemblyDef, typeDef, methodDef, RecaseMethod(assemblyDef, typeDef, methodDef, script), false); return AppendFinalImport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, body, arguments); } } }