bool TryConstructRuntimeChainElement(BoundExpression expr, out RuntimeChainElement runtimeChainElement) { Debug.Assert(expr != null); runtimeChainElement = null; if (expr.IsConstant()) { return(false); } // 1/ $$->{field} // should be a not resolved field reference (dynamic), otherwise it's unnecessary if (expr is BoundFieldRef fieldref && fieldref.IsInstanceField && ((Microsoft.CodeAnalysis.Operations.IFieldReferenceOperation)fieldref).Field == null) { runtimeChainElement = new RuntimeChainElement(_cg.CoreTypes.RuntimeChain_Property_T, fieldref.Instance) { Fields = new List <KeyValuePair <string, BoundOperation> >(1) { new KeyValuePair <string, BoundOperation>("Name", fieldref.FieldName), } }; } // 2/ $$[Key], $$[] if (expr is BoundArrayItemEx arritem) { if (arritem.Index != null) { runtimeChainElement = new RuntimeChainElement(_cg.CoreTypes.RuntimeChain_ArrayItem_T, arritem.Array) { Fields = new List <KeyValuePair <string, BoundOperation> >(1) { new KeyValuePair <string, BoundOperation>("Key", arritem.Index), } }; } else { runtimeChainElement = new RuntimeChainElement(_cg.CoreTypes.RuntimeChain_ArrayNewItem_T, arritem.Array) { }; } } // TODO: 3/ StaticProperty, ClassConstant // return(runtimeChainElement != null); }
/// <summary>Emits argument to be passed to callsite.</summary> void EmitArg(BoundArgument a) { var expr = a.Value; if (a.IsUnpacking) { EmitUnpackingParam(_cg.Emit(expr)); } else { RuntimeChainElement runtimeChain = null; // construct the runtime chain if possible: while (TryConstructRuntimeChainElement(expr, out var element)) { element.Next = runtimeChain ?? new RuntimeChainElement(_cg.CoreTypes.RuntimeChain_ChainEnd); if (element.Type.Arity == 1) { // construct Element<TNext> // TNext:typeof(element.Next) element.Type = element.Type.Construct(element.Next.Type); } runtimeChain = element; // if (element.Parent == null) { break; } expr = element.Parent; } // emit the root of the chain: TypeSymbol t = null; bool byref = false; if (!expr.IsConstant() && expr is BoundReferenceExpression varref) { // try to read the value by ref so it can be changed if necessary var place = varref.Place(); if (place != null) { if (place.HasAddress && place.Type == _cg.CoreTypes.PhpValue) { place.EmitLoadAddress(_cg.Builder); t = place.Type; byref = true; } } else if (varref.BindPlace(_cg) is LocalVariableReference loc && !loc.IsOptimized) { t = loc.LoadIndirectLocal(_cg); // IndirectLocal wrapper } } if (t == null) { // load by value t = _cg.Emit(expr); } if (t.SpecialType == SpecialType.System_Void) { Debug.WriteLine("Unexpected: argument evaluates to 'void'."); // NOTE: this should be handled in diagnostics t = _cg.Emit_PhpValue_Null(); } // AddArg(t, byref: byref); // emit the chain eventually: // after the root of the chain, order matters! if (runtimeChain != null) { AddArg(runtimeChain.EmitRuntimeChain(_cg), byref: false); } } }