public override DynamicMetaObject /*!*/ Bind(DynamicMetaObject /*!*/ target, DynamicMetaObject /*!*/[] /*!*/ args) { Debug.Assert(args.Length > 1); var exprs = new AstExpressions(); exprs.Add(AstUtils.LightDynamic(_getMember, typeof(object), target.Expression)); exprs.Add(args.ToExpressions()); return(new DynamicMetaObject(AstUtils.LightDynamic(_setIndex, typeof(object), exprs), BindingRestrictions.Empty)); }
private Expression /*!*/ MarshalArgument(MetaObjectBuilder /*!*/ metaBuilder, DynamicMetaObject /*!*/ arg, ArgType parameterType) { object value = arg.Value; if (value == null) { metaBuilder.AddRestriction(Ast.Equal(arg.Expression, AstUtils.Constant(null))); } else { metaBuilder.AddTypeRestriction(value.GetType(), arg.Expression); } switch (parameterType) { case ArgType.Buffer: if (value == null) { return(AstUtils.Constant(null, typeof(byte[]))); } if (value is int && (int)value == 0) { metaBuilder.AddRestriction(Ast.Equal(AstUtils.Convert(arg.Expression, typeof(int)), AstUtils.Constant(0))); return(AstUtils.Constant(null, typeof(byte[]))); } if (value.GetType() == typeof(MutableString)) { return(Methods.GetMutableStringBytes.OpCall( AstUtils.Convert(arg.Expression, typeof(MutableString)) )); } return(Methods.GetMutableStringBytes.OpCall( AstUtils.LightDynamic(ConvertToStrAction.Make(_context), typeof(MutableString), arg.Expression) )); case ArgType.Int32: if (value is int) { return(AstUtils.Convert(arg.Expression, typeof(int))); } return(Ast.Convert( Ast.Call( AstUtils.LightDynamic(ConvertToIntAction.Make(_context), typeof(IntegerValue), arg.Expression), Methods.IntegerValue_ToUInt32Unchecked ), typeof(int) )); } throw Assert.Unreachable; }
internal override MSA.Expression TransformDefinedCondition(AstGenerator /*!*/ gen) { // MRI doesn't evaluate the arguments MSA.Expression result = AstUtils.LightDynamic( RubyCallAction.Make(gen.Context, _methodName, RubyCallSignature.IsDefined(_target == null)), typeof(bool), gen.CurrentScopeVariable, (_target != null) ? AstUtils.Box(_target.TransformRead(gen)) : gen.CurrentSelfVariable ); return((_target != null) ? gen.TryCatchAny(result, AstFactory.False) : result); }
public override DynamicMetaObject /*!*/ FallbackInvoke(DynamicMetaObject /*!*/ target, DynamicMetaObject /*!*/[] /*!*/ args, DynamicMetaObject errorSuggestion) { AstExpressions exprs = new AstExpressions(); exprs.Add(target.Expression); exprs.Add(args.ToExpressions()); return(new DynamicMetaObject( AstUtils.LightDynamic(_context.MetaBinderFactory.InteropReturn(CallInfo), typeof(object), exprs), target.Restrictions.Merge(BindingRestrictions.Combine(args)) )); }
private Expression /*!*/ ToA(CallArguments /*!*/ args, Expression /*!*/ targetClassNameConstant) { return // TODO(opt): We could optimize this a bit by merging the to_a call with the conversion. // We could also check if to_a is implemented on Kernel. If so it only wraps the item into an array // that the subsequent splatting operation unwraps. However this only applies when unpslatting non-splattable objects // that don't implement to_ary - probably a rare case. (Methods.ToArrayValidator.OpCall( targetClassNameConstant, AstUtils.LightDynamic( RubyCallAction.Make(args.RubyContext, "to_a", RubyCallSignature.WithImplicitSelf(0)), args.TargetExpression ) )); }
// Only used if method_missing() is called directly on the main singleton. internal override void BuildCallNoFlow(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, string /*!*/ name) { var globalScope = args.TargetClass.GlobalScope; // TODO: this just calls super for now, so it doesn't look up the scope: metaBuilder.Result = AstUtils.LightDynamic( new RubyCallAction(globalScope.Context, Symbols.MethodMissing, new RubyCallSignature( args.Signature.ArgumentCount, args.Signature.Flags | RubyCallFlags.HasImplicitSelf | RubyCallFlags.IsSuperCall ) ), typeof(object), args.GetCallSiteArguments(args.TargetExpression) ); }
private MSA.Expression /*!*/ TransformWrite(AstGenerator /*!*/ gen, MSA.Expression /*!*/ transformedRight, bool isSimpleRhs) { var writes = new AstBlock(); MSA.Expression rightList = gen.CurrentScope.DefineHiddenVariable("#rhs", typeof(IList)); MSA.Expression result; if (isSimpleRhs) { // 1.9 returns the RHS, not an unsplatted array, if there is just a single RHS: result = gen.CurrentScope.DefineHiddenVariable("#pr", transformedRight.Type); writes.Add(Ast.Assign(result, transformedRight)); transformedRight = AstUtils.LightDynamic(ImplicitSplatAction.Make(gen.Context), typeof(IList), result); } else { result = rightList; } writes.Add(Ast.Assign(rightList, transformedRight)); for (int i = 0; i < _unsplattedValueIndex; i++) { writes.Add(_leftValues[i].TransformWrite(gen, Methods.GetArrayItem.OpCall(rightList, AstUtils.Constant(i)))); } if (HasUnsplattedValue) { MSA.Expression explicitCount = AstUtils.Constant(_leftValues.Length - 1); // remaining RHS values: MSA.Expression array = Methods.GetArrayRange.OpCall(rightList, AstUtils.Constant(_unsplattedValueIndex), explicitCount); writes.Add(_leftValues[_unsplattedValueIndex].TransformWrite(gen, array)); for (int i = _unsplattedValueIndex + 1; i < _leftValues.Length; i++) { writes.Add(_leftValues[i].TransformWrite(gen, Methods.GetTrailingArrayItem.OpCall(rightList, AstUtils.Constant(_leftValues.Length - i), explicitCount))); } } writes.Add(result); return(writes); }
public override DynamicMetaObject /*!*/ Bind(DynamicMetaObject /*!*/ target, DynamicMetaObject /*!*/[] /*!*/ args) { Debug.Assert(args.Length == 1); if (_setMemberUnmangled == null) { // no unmangled name, just do the set member binding return(_setMember.Bind(target, args)); } // // Consider this case: // x = {"Foo" -> 1}. // x.foo += 1 // Without name mangling this would result to x being {"Foo" -> 1, "foo" -> 2} while the expected result is {"Foo" -> 2}. // // Hence if the object doesn't contain the member but contains an unmangled member we set the unmangled one: // return(new DynamicMetaObject( Expression.Condition( Expression.AndAlso( Expression.Equal( AstUtils.LightDynamic(_tryGetMember, typeof(object), target.Expression), Expression.Constant(OperationFailed.Value) ), Expression.NotEqual( AstUtils.LightDynamic(_tryGetMemberUnmangled, typeof(object), target.Expression), Expression.Constant(OperationFailed.Value) ) ), AstUtils.LightDynamic(_setMemberUnmangled, typeof(object), target.Expression, args[0].Expression), AstUtils.LightDynamic(_setMember, typeof(object), target.Expression, args[0].Expression) ), target.Restrict(CompilerHelpers.GetType(target.Value)).Restrictions )); }
protected override Expression /*!*/ MakeValidatorCall(CallArguments /*!*/ args, Expression /*!*/ targetClassNameConstant, Expression /*!*/ result) { return(AstUtils.LightDynamic(ConvertToStrAction.Make(args.RubyContext), AstUtils.Box(result))); }
internal static void BuildConversion(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, Type /*!*/ resultType, params ProtocolConversionAction /*!*/[] /*!*/ conversions) { Assert.NotNull(metaBuilder, args, conversions); Debug.Assert(args.SimpleArgumentCount == 0 && !args.Signature.HasBlock && !args.Signature.HasSplattedArgument && !args.Signature.HasRhsArgument); Debug.Assert(!args.Signature.HasScope); // implicit conversions should only depend on the static type: foreach (var conversion in conversions) { if (conversion.TryImplicitConversion(metaBuilder, args)) { metaBuilder.AddObjectTypeRestriction(args.Target, args.TargetExpression); if (!metaBuilder.Error) { metaBuilder.Result = ConvertResult(metaBuilder.Result, resultType); } return; } } RubyClass targetClass = args.RubyContext.GetImmediateClassOf(args.Target); Expression targetClassNameConstant = AstUtils.Constant(targetClass.GetNonSingletonClass().Name, typeof(string)); MethodResolutionResult respondToMethod, methodMissing = MethodResolutionResult.NotFound; ProtocolConversionAction selectedConversion = null; RubyMemberInfo conversionMethod = null; using (targetClass.Context.ClassHierarchyLocker()) { // check for type version: metaBuilder.AddTargetTypeTest(args.Target, targetClass, args.TargetExpression, args.MetaContext, ArrayUtils.Insert(Symbols.RespondTo, Symbols.MethodMissing, ArrayUtils.ConvertAll(conversions, (c) => c.ToMethodName)) ); // we can optimize if Kernel#respond_to? method is not overridden: respondToMethod = targetClass.ResolveMethodForSiteNoLock(Symbols.RespondTo, VisibilityContext.AllVisible); if (respondToMethod.Found && respondToMethod.Info.DeclaringModule == targetClass.Context.KernelModule && respondToMethod.Info is RubyLibraryMethodInfo) // TODO: better override detection { respondToMethod = MethodResolutionResult.NotFound; // get the first applicable conversion: foreach (var conversion in conversions) { selectedConversion = conversion; conversionMethod = targetClass.ResolveMethodForSiteNoLock(conversion.ToMethodName, VisibilityContext.AllVisible).Info; if (conversionMethod != null) { break; } else { // find method_missing - we need to add "to_xxx" methods to the missing methods table: if (!methodMissing.Found) { methodMissing = targetClass.ResolveMethodNoLock(Symbols.MethodMissing, VisibilityContext.AllVisible); } methodMissing.InvalidateSitesOnMissingMethodAddition(conversion.ToMethodName, targetClass.Context); } } } } if (!respondToMethod.Found) { if (conversionMethod == null) { // error: selectedConversion.SetError(metaBuilder, args, targetClassNameConstant, resultType); return; } else { // invoke target.to_xxx() and validate it; returns an instance of TTargetType: conversionMethod.BuildCall(metaBuilder, args, selectedConversion.ToMethodName); if (!metaBuilder.Error) { metaBuilder.Result = ConvertResult( selectedConversion.MakeValidatorCall(args, targetClassNameConstant, metaBuilder.Result), resultType ); } return; } } // slow path: invoke respond_to?, to_xxx and result validation: for (int i = conversions.Length - 1; i >= 0; i--) { string toMethodName = conversions[i].ToMethodName; var conversionCallSite = AstUtils.LightDynamic( RubyCallAction.Make(args.RubyContext, toMethodName, RubyCallSignature.WithImplicitSelf(0)), args.TargetExpression ); metaBuilder.Result = Ast.Condition( // If // respond_to?() Methods.IsTrue.OpCall( AstUtils.LightDynamic( RubyCallAction.Make(args.RubyContext, Symbols.RespondTo, RubyCallSignature.WithImplicitSelf(1)), args.TargetExpression, Ast.Constant(args.RubyContext.CreateSymbol(toMethodName, RubyEncoding.Binary)) ) ), // Then // to_xxx(): ConvertResult( conversions[i].MakeValidatorCall(args, targetClassNameConstant, conversionCallSite), resultType ), // Else (i < conversions.Length - 1) ? metaBuilder.Result : conversions[i].MakeErrorExpression(args, targetClassNameConstant, resultType) ); } }
private MSA.Expression /*!*/ TransformWrite(AstGenerator /*!*/ gen, AstExpressions /*!*/ rightValues, MSA.Expression splattedValue) { // We need to distinguish various special cases here. // Each of the bool variables defined below is true iff the corresponding special form of LHS/RHS occurs. // These flags drive the DLR AST being produced by this method. // For parallel assignment specification, see "Ruby Language.docx/Runtime/Parallel Assignment". // L(0,-) not applicable Debug.Assert(!(_leftValues.Count == 0 && _unsplattedValue == null)); // L(1,-)? bool leftOneNone = _leftValues.Count == 1 && _unsplattedValue == null; // L(0,*)? bool leftNoneSplat = _leftValues.Count == 0 && _unsplattedValue != null; // R(0,*)? bool rightNoneSplat = rightValues.Count == 0 && splattedValue != null; // R(1,-)? bool rightOneNone = rightValues.Count == 1 && splattedValue == null; // R(1,*)? bool rightOneSplat = rightValues.Count == 1 && splattedValue != null; // R(0,-) not applicable Debug.Assert(!(rightValues.Count == 0 && splattedValue == null)); MSA.Expression resultExpression; if (leftOneNone) { // L(1,-): // recurse right away (X) = RHS is equivalent to X = RHS: CompoundLeftValue compound = _leftValues[0] as CompoundLeftValue; if (compound != null) { return(compound.TransformWrite(gen, rightValues, splattedValue)); } if (rightOneSplat) { // R(1,*) resultExpression = Methods.SplatPair.OpCall( AstUtils.Box(rightValues[0]), AstUtils.LightDynamic(SplatAction.Make(gen.Context), typeof(IList), splattedValue) ); } else { // case 1: R(1,-) // case 2: R(0,*) // case 3: otherwise resultExpression = Arguments.TransformRead(gen, rightValues, splattedValue, true /* Splat */); } return(_leftValues[0].TransformWrite(gen, resultExpression)); } bool optimizeReads = true; if (rightOneNone && !leftNoneSplat) { // R(1,-) && !L(0,*) resultExpression = Methods.Unsplat.OpCall( AstUtils.LightDynamic(ConvertToArraySplatAction.Make(gen.Context), rightValues[0]) ); optimizeReads = false; } else { // case 1: R(0,*) = L // case 2: otherwise resultExpression = Arguments.TransformRead(gen, rightValues, splattedValue, false /* Unsplat */); optimizeReads = !rightNoneSplat; } var writes = new AstBlock(); MSA.Expression result = gen.CurrentScope.DefineHiddenVariable("#rhs", typeof(IList)); writes.Add(Ast.Assign(result, resultExpression)); MethodInfo itemGetter = Methods.IList_get_Item; for (int i = 0; i < _leftValues.Count; i++) { MSA.Expression rvalue; if (optimizeReads) { if (i < rightValues.Count) { // unchecked get item: rvalue = Ast.Call(result, itemGetter, AstUtils.Constant(i)); } else if (splattedValue != null) { // checked get item: rvalue = Methods.GetArrayItem.OpCall(result, AstUtils.Constant(i)); } else { // missing item: rvalue = AstUtils.Constant(null); } } else { rvalue = Methods.GetArrayItem.OpCall(result, AstUtils.Constant(i)); } writes.Add(_leftValues[i].TransformWrite(gen, rvalue)); } // unsplatting the rest of rhs values into an array: if (_unsplattedValue != null) { // copies the rest of resulting array to the *LHS; // the resulting array contains splatted *RHS - no need for additional appending: MSA.Expression array = Methods.GetArraySuffix.OpCall(result, AstUtils.Constant(_leftValues.Count)); // assign the array (possibly empty) to *LHS: writes.Add(_unsplattedValue.TransformWrite(gen, array)); } writes.Add(result); return(writes); }
internal override void BuildMethodMissingCallNoFlow(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, string /*!*/ name) { var globalScope = args.TargetClass.GlobalScope; var context = globalScope.Context; if (name.LastCharacter() == '=') { var normalizedArgs = RubyOverloadResolver.NormalizeArguments(metaBuilder, args, 1, 1); if (!metaBuilder.Error) { var scopeVar = metaBuilder.GetTemporary(typeof(Scope), "#scope"); metaBuilder.AddInitialization( Ast.Assign(scopeVar, Methods.GetGlobalScopeFromScope.OpCall(AstUtils.Convert(args.MetaScope.Expression, typeof(RubyScope)))) ); var interopSetter = context.MetaBinderFactory.InteropSetMember(name.Substring(0, name.Length - 1)); metaBuilder.SetMetaResult( interopSetter.Bind( new DynamicMetaObject( scopeVar, BindingRestrictions.Empty, globalScope.Scope ), new[] { normalizedArgs[0] } ), true ); } } else { RubyOverloadResolver.NormalizeArguments(metaBuilder, args, 0, 0); Expression errorExpr = metaBuilder.Error ? Ast.Throw(metaBuilder.Result, typeof(object)) : null; var scopeVar = metaBuilder.GetTemporary(typeof(Scope), "#scope"); var scopeLookupResultVar = metaBuilder.GetTemporary(typeof(object), "#result"); metaBuilder.AddInitialization( Ast.Assign(scopeVar, Methods.GetGlobalScopeFromScope.OpCall(AstUtils.Convert(args.MetaScope.Expression, typeof(RubyScope)))) ); Expression scopeLookupResultExpr = errorExpr ?? scopeLookupResultVar; Expression fallbackExp; if (name == "scope") { fallbackExp = errorExpr ?? args.TargetExpression; } else { // super(methodName, ...args...) - ignore argument error: args.InsertMethodName(name); fallbackExp = AstUtils.LightDynamic( context.MetaBinderFactory.Call(Symbols.MethodMissing, new RubyCallSignature( args.Signature.ArgumentCount + 1, args.Signature.Flags | RubyCallFlags.HasImplicitSelf | RubyCallFlags.IsSuperCall ) ), typeof(object), args.GetCallSiteArguments(args.TargetExpression) ); } var scopeLookup = Ast.NotEqual( Ast.Assign(scopeLookupResultVar, AstUtils.LightDynamic(context.MetaBinderFactory.InteropTryGetMemberExact(name), typeof(object), scopeVar)), Expression.Constant(OperationFailed.Value) ); string unmanagled = RubyUtils.TryUnmangleMethodName(name); if (unmanagled != null) { scopeLookup = Ast.OrElse( scopeLookup, Ast.NotEqual( Ast.Assign(scopeLookupResultVar, AstUtils.LightDynamic(context.MetaBinderFactory.InteropTryGetMemberExact(unmanagled), typeof(object), scopeVar)), Expression.Constant(OperationFailed.Value) ) ); } metaBuilder.Result = Ast.Condition( scopeLookup, scopeLookupResultExpr, fallbackExp ); } }
internal static MSA.Expression /*!*/ MakeConversion(AstGenerator /*!*/ gen, Expression /*!*/ expression) { return(AstUtils.LightDynamic(ConvertToSAction.Make(gen.Context), typeof(MutableString), expression.TransformRead(gen))); }
internal MSA.Expression <T> /*!*/ Transform <T>(AstGenerator /*!*/ gen) { Debug.Assert(gen != null); ScopeBuilder scope = DefineLocals(); MSA.ParameterExpression[] parameters; MSA.ParameterExpression selfVariable; MSA.ParameterExpression runtimeScopeVariable; MSA.ParameterExpression blockParameter; if (gen.CompilerOptions.FactoryKind == TopScopeFactoryKind.None || gen.CompilerOptions.FactoryKind == TopScopeFactoryKind.ModuleEval) { parameters = new MSA.ParameterExpression[4]; runtimeScopeVariable = parameters[0] = Ast.Parameter(typeof(RubyScope), "#scope"); selfVariable = parameters[1] = Ast.Parameter(typeof(object), "#self"); parameters[2] = Ast.Parameter(typeof(RubyModule), "#module"); blockParameter = parameters[3] = Ast.Parameter(typeof(Proc), "#block"); } else { parameters = new MSA.ParameterExpression[2]; runtimeScopeVariable = parameters[0] = Ast.Parameter(typeof(RubyScope), "#scope"); selfVariable = parameters[1] = Ast.Parameter(typeof(object), "#self"); blockParameter = null; } gen.EnterSourceUnit( scope, selfVariable, runtimeScopeVariable, blockParameter, gen.CompilerOptions.TopLevelMethodName, // method name for blocks null // parameters for super calls ); MSA.Expression body; if (_statements.Count > 0) { if (gen.PrintInteractiveResult) { var resultVariable = scope.DefineHiddenVariable("#result", typeof(object)); var epilogue = Methods.PrintInteractiveResult.OpCall(runtimeScopeVariable, AstUtils.LightDynamic(ConvertToSAction.Make(gen.Context), typeof(MutableString), CallSiteBuilder.InvokeMethod(gen.Context, "inspect", RubyCallSignature.WithScope(0), gen.CurrentScopeVariable, resultVariable ) ) ); body = gen.TransformStatements(null, _statements, epilogue, ResultOperation.Store(resultVariable)); } else { body = gen.TransformStatements(_statements, ResultOperation.Return); } // TODO: var exceptionVariable = Ast.Parameter(typeof(Exception), "#exception"); body = AstUtils.Try( body ).Filter(exceptionVariable, Methods.TraceTopLevelCodeFrame.OpCall(runtimeScopeVariable, exceptionVariable), Ast.Empty() ); } else { body = AstUtils.Constant(null); } // scope initialization: MSA.Expression prologue; switch (gen.CompilerOptions.FactoryKind) { case TopScopeFactoryKind.None: case TopScopeFactoryKind.ModuleEval: prologue = Methods.InitializeScopeNoLocals.OpCall(runtimeScopeVariable, EnterInterpretedFrameExpression.Instance); break; case TopScopeFactoryKind.Hosted: case TopScopeFactoryKind.File: case TopScopeFactoryKind.WrappedFile: prologue = Methods.InitializeScope.OpCall( runtimeScopeVariable, scope.MakeLocalsStorage(), scope.GetVariableNamesExpression(), EnterInterpretedFrameExpression.Instance ); break; case TopScopeFactoryKind.Main: prologue = Methods.InitializeScope.OpCall( runtimeScopeVariable, scope.MakeLocalsStorage(), scope.GetVariableNamesExpression(), EnterInterpretedFrameExpression.Instance ); if (_dataOffset >= 0) { prologue = Ast.Block( prologue, Methods.SetDataConstant.OpCall( runtimeScopeVariable, gen.SourcePathConstant, AstUtils.Constant(_dataOffset) ) ); } break; default: throw Assert.Unreachable; } // BEGIN blocks: if (gen.FileInitializers != null) { var b = new AstBlock(); b.Add(prologue); b.Add(gen.FileInitializers); b.Add(body); body = b; } body = gen.AddReturnTarget(scope.CreateScope(body)); gen.LeaveSourceUnit(); return(Ast.Lambda <T>(body, GetEncodedName(gen), parameters)); }
internal override MSA.Expression /*!*/ Transform(AstGenerator /*!*/ gen) { Assert.NotNull(gen); return(AstUtils.LightDynamic(ConvertToProcAction.Make(gen.Context), typeof(Proc), _expression.TransformRead(gen))); }