/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Microsoft Public License. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Microsoft Public License, please send an email to
* ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Microsoft Public License.
*
* You must not remove this notice, or any other, from this software.
*
*
* ***************************************************************************/
using System;
using System.Linq.Expressions;
using System.Dynamic;
using System.Collections.Generic;
using Microsoft.Scripting.Utils;
using Ast = System.Linq.Expressions.Expression;
using System.Diagnostics;
using IronRuby.Compiler.Generation;
using IronRuby.Compiler;
namespace IronRuby.Runtime.Calls {
public enum RubyCallFlags {
None = 0,
HasScope = 1,
HasSplattedArgument = 2,
// an additional argument following splat arguments (e.g. target[args, *splat]=rhs)
HasRhsArgument = 4,
HasBlock = 8,
// Used for private visibility check. By default method call sites have explicit self, so private methods are not visible.
HasImplicitSelf = 16,
// Tries to call the method, if not successful returns a RubyOps.MethodNotFound singleton.
IsTryCall = 32,
}
///
/// RubyScope/RubyContext, (self), (argument){ArgumentCount}, (splatted-argument)?, (block)?
///
public struct RubyCallSignature : IEquatable {
private const int FlagsCount = 6;
private const int MaxArgumentCount = (int)(UInt32.MaxValue >> FlagsCount);
private const RubyCallFlags FlagsMask = (RubyCallFlags)(1 << FlagsCount) - 1;
private readonly uint _countAndFlags;
public bool HasImplicitSelf { get { return ((RubyCallFlags)_countAndFlags & RubyCallFlags.HasImplicitSelf) != 0; } }
public bool HasScope { get { return ((RubyCallFlags)_countAndFlags & RubyCallFlags.HasScope) != 0; } }
public bool HasBlock { get { return ((RubyCallFlags)_countAndFlags & RubyCallFlags.HasBlock) != 0; } }
public bool HasSplattedArgument { get { return ((RubyCallFlags)_countAndFlags & RubyCallFlags.HasSplattedArgument) != 0; } }
public bool HasRhsArgument { get { return ((RubyCallFlags)_countAndFlags & RubyCallFlags.HasRhsArgument) != 0; } }
public bool IsTryCall { get { return ((RubyCallFlags)_countAndFlags & RubyCallFlags.IsTryCall) != 0; } }
public int ArgumentCount { get { return (int)_countAndFlags >> FlagsCount; } }
internal RubyCallFlags Flags { get { return (RubyCallFlags)_countAndFlags & FlagsMask; } }
// total call-site arguments w/o RubyContext/RubyScope
public int TotalArgumentCount {
get {
return 1 + // instance (self)
ArgumentCount +
(HasSplattedArgument ? 1 : 0) +
(HasBlock ? 1 : 0) +
(HasRhsArgument ? 1 : 0);
}
}
public RubyCallSignature(int argumentCount, RubyCallFlags flags) {
Debug.Assert(argumentCount >= 0 && argumentCount < MaxArgumentCount);
Debug.Assert(((int)flags >> FlagsCount) == 0);
_countAndFlags = ((uint)argumentCount << FlagsCount) | (uint)flags;
}
public RubyCallSignature(bool isTryCall, bool hasScope, bool hasImplicitSelf, int argumentCount, bool hasSplattedArgument, bool hasBlock, bool hasRhsArgument) {
Debug.Assert(argumentCount >= 0 && argumentCount < MaxArgumentCount);
var flags = RubyCallFlags.None;
if (isTryCall) flags |= RubyCallFlags.IsTryCall;
if (hasImplicitSelf) flags |= RubyCallFlags.HasImplicitSelf;
if (hasScope) flags |= RubyCallFlags.HasScope;
if (hasSplattedArgument) flags |= RubyCallFlags.HasSplattedArgument;
if (hasBlock) flags |= RubyCallFlags.HasBlock;
if (hasRhsArgument) flags |= RubyCallFlags.HasRhsArgument;
_countAndFlags = ((uint)argumentCount << FlagsCount) | (uint)flags;
}
[Emitted, Obsolete("Do not use from code"), CLSCompliant(false)]
public RubyCallSignature(uint countAndFlags) {
_countAndFlags = countAndFlags;
}
internal static bool TryCreate(IList/*!*/ action, out RubyCallSignature callSignature) {
callSignature = Simple(action.Count);
return HasNamedArgument(action);
}
internal static bool HasNamedArgument(IList/*!*/ arguments) {
for (int i = 0; i < arguments.Count; i++) {
if (arguments[i].ArgumentType == ArgumentKind.Named) {
return true;
}
}
return false;
}
public static RubyCallSignature WithImplicitSelf(int argumentCount) {
return new RubyCallSignature(argumentCount, RubyCallFlags.HasImplicitSelf);
}
public static RubyCallSignature Simple(int argumentCount) {
return new RubyCallSignature(argumentCount, RubyCallFlags.None);
}
public static RubyCallSignature WithBlock(int argumentCount) {
return new RubyCallSignature(argumentCount, RubyCallFlags.HasBlock);
}
public static RubyCallSignature WithSplat(int argumentCount) {
return new RubyCallSignature(argumentCount, RubyCallFlags.HasSplattedArgument);
}
public static RubyCallSignature WithSplatAndBlock(int argumentCount) {
return new RubyCallSignature(argumentCount, RubyCallFlags.HasBlock | RubyCallFlags.HasSplattedArgument);
}
public static RubyCallSignature WithScope(int argumentCount) {
return new RubyCallSignature(argumentCount, RubyCallFlags.HasScope);
}
public static RubyCallSignature WithScopeAndBlock(int argumentCount) {
return new RubyCallSignature(argumentCount, RubyCallFlags.HasScope | RubyCallFlags.HasBlock);
}
public static RubyCallSignature WithScopeAndSplat(int argumentCount) {
return new RubyCallSignature(argumentCount, RubyCallFlags.HasScope | RubyCallFlags.HasSplattedArgument);
}
public static RubyCallSignature WithScopeAndSplatAndBlock(int argumentCount) {
return new RubyCallSignature(argumentCount, RubyCallFlags.HasScope | RubyCallFlags.HasBlock | RubyCallFlags.HasSplattedArgument);
}
internal Expression/*!*/ CreateExpression() {
return Ast.New(Methods.RubyCallSignatureCtor, Ast.Constant(_countAndFlags));
}
public bool Equals(RubyCallSignature other) {
return _countAndFlags == other._countAndFlags;
}
public override string/*!*/ ToString() {
return "(" +
(HasImplicitSelf ? "." : "") +
(HasScope ? "S," : "C,") +
ArgumentCount.ToString() +
(HasSplattedArgument ? "*" : "") +
(HasBlock ? "&" : "") +
(HasRhsArgument ? "=" : "") +
")";
}
}
}