/// <summary> /// Emits IL instructions that add an object field access to the current <see cref="PhpRuntimeChain"/>. /// </summary> /// <param name="varUse">AST node representing the field access.</param> /// <remarks> /// A reference to <see cref="PhpRuntimeChain"/> is expected and left on the evaluation stack. /// </remarks> public void EmitRTChainAddField(SimpleVarUse varUse) { codeGenerator.IL.Emit(OpCodes.Dup); varUse.EmitName(codeGenerator); codeGenerator.IL.EmitCall(OpCodes.Call, Methods.PhpRuntimeChain.AddField, null); }
/// <summary> /// Emits IL instructions that ensure that the specified property of an object is /// of the <see cref="PhpArray"/> type. /// </summary> /// <param name="varObject">Represents the instance whose property should be examined.</param> /// <param name="fieldName">A <see cref="SimpleVarUse"/> that evaluates to the property name.</param> /// <param name="ensureArray">Whether to ensure that static property is an array (or an object).</param> /// <remarks>Nothing is expected on the evaluation stack. If the property is of <see cref="PhpArray"/> type /// it is left on the evaluation stack. Otherwise the control is transfered to the end of chain.</remarks> public PhpTypeCode EmitEnsureProperty(VarLikeConstructUse/*!*/ varObject, SimpleVarUse/*!*/ fieldName, bool ensureArray) { // Template: PhpArray EnsurePropertyIsArray(DObject,field,DTypeDesc) Debug.Assert(varObject != null && fieldName != null); Debug.Assert(fieldName is DirectVarUse || fieldName is IndirectVarUse); LocationTypes location; DProperty property = ResolveProperty(varObject, fieldName, out location); ILEmitter il = codeGenerator.IL; PhpField php_field = property as PhpField; if (php_field != null) // we can emit code that manipulates the property directly { // HACK HACK EmitEnsurePhpFieldDirect(php_field, fieldName, ensureArray); } else { switch (location) { case LocationTypes.GlobalCode: { // call EnsurePropertyIsArray codeGenerator.EmitLoadSelf(); fieldName.EmitName(codeGenerator); codeGenerator.EmitLoadClassContext(); if (ensureArray) { il.Emit(OpCodes.Call, Methods.Operators.EnsurePropertyIsArray); } else { codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Call, Methods.Operators.EnsurePropertyIsObject); } break; } case LocationTypes.MethodDecl: { if (ensureArray) this.Lengthen(); // for hop over -> FunctionCall func = varObject as FunctionCall; if (func == null) { varObject.Emit(codeGenerator); } else { this.LoadAddressOfFunctionReturnValue = true; func.Emit(codeGenerator); RecastValueReturnedByFunctionCall(); } fieldName.EmitName(codeGenerator); codeGenerator.EmitLoadClassContext(); if (ensureArray) { il.Emit(OpCodes.Call, Methods.Operators.EnsurePropertyIsArray); } else { codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Call, Methods.Operators.EnsurePropertyIsObject); } EmitErrorCheck(ensureArray); break; } // if the location was FunctionDecl, appropriate code was already generated by GetDProperty } } return (ensureArray ? PhpTypeCode.PhpArray : PhpTypeCode.DObject); }
/// <summary> /// I do not like PHP-specific access code emission here. TODO: Move to PhpField /// </summary> /// <param name="field"></param> /// <param name="fieldName"></param> /// <param name="ensureArray"></param> private void EmitEnsurePhpFieldDirect(PhpField/*!*/ field, SimpleVarUse/*!*/ fieldName, bool ensureArray) { ILEmitter il = codeGenerator.IL; // check whether the field is set il.Ldarg(FunctionBuilder.ArgThis); il.Emit(OpCodes.Ldfld, field.RealField); Label direct_ensure = il.DefineLabel(); Label ensuring_over = il.DefineLabel(); // test whether it is set il.Emit(OpCodes.Callvirt, Properties.PhpReference_IsSet.GetGetMethod()); il.Emit(OpCodes.Brtrue, direct_ensure); // the field has been unset -> must call operator that handles __get/__set if (ensureArray) this.Lengthen(); // TODO: ??? codeGenerator.EmitLoadSelf(); fieldName.EmitName(codeGenerator); codeGenerator.EmitLoadClassContext(); if (ensureArray) { il.Emit(OpCodes.Call, Methods.Operators.EnsurePropertyIsArray); } else { codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Call, Methods.Operators.EnsurePropertyIsObject); } il.Emit(OpCodes.Br, ensuring_over); // read the field again and call EnsureVariableIsArray il.MarkLabel(direct_ensure, true); il.Ldarg(FunctionBuilder.ArgThis); il.Emit(OpCodes.Ldfld, field.RealField); il.Emit(OpCodes.Ldflda, Fields.PhpReference_Value); if (ensureArray) { il.Emit(OpCodes.Call, Methods.Operators.EnsureVariableIsArray); } else { codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Call, Methods.Operators.EnsureVariableIsObject); } il.MarkLabel(ensuring_over, true); EmitErrorCheck(ensureArray); }