private void EmitNodeWriteAssign(CodeGenerator codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; // Note that for cases 1,3,4,5,6,9 EmitAssign is never called!!! // 2,7,8 if (chain.IsMember) { // 2,8 if (chain.Exists) { // 8: b[]->$a chain.EmitSetObjectField(); } else { // 2: $b->a Debug.Assert(this.isMemberOf is SimpleVarUse || this.isMemberOf is FunctionCall); if (this.isMemberOf is FunctionCall) { codeGenerator.ChainBuilder.LoadAddressOfFunctionReturnValue = true; } assignmentCallback(codeGenerator, PhpTypeCode.Object); SimpleVarUse svu = this.isMemberOf as SimpleVarUse; if (svu != null) { svu.EmitLoadAddress_StoreBack(codeGenerator); } // else do nothing } chain.End(); } else { // 7: $a //codeGenerator.EmitVariableStoreAssign(this); this.EmitStoreAssign(codeGenerator); } }
/// <summary> /// Emits IL instructions that read the value of an instance field. /// </summary> /// <param name="codeGenerator">The current <see cref="CodeGenerator"/>.</param> /// <param name="wantRef">If <B>false</B> the field value should be left on the evaluation stack, /// if <B>true</B> the <see cref="PhpReference"/> should be left on the evaluation stack.</param> /// <returns> /// Nothing is expected on the evaluation stack. A <see cref="PhpReference"/> (if <paramref name="wantRef"/> /// is <B>true</B>) or the field value itself (if <paramref name="wantRef"/> is <B>false</B>) is left on the /// evaluation stack. /// </returns> internal virtual PhpTypeCode EmitReadField(CodeGenerator codeGenerator, bool wantRef) { ILEmitter il = codeGenerator.IL; DirectVarUse direct_instance = isMemberOf as DirectVarUse; if (direct_instance != null && direct_instance.IsMemberOf == null && direct_instance.VarName.IsThisVariableName) { return(EmitReadFieldOfThis(codeGenerator, wantRef)); } if (!wantRef) { //codeGenerator.ChainBuilder.Lengthen(); //PhpTypeCode type_code = isMemberOf.Emit(codeGenerator); //Debug.Assert(type_code == PhpTypeCode.Object || type_code == PhpTypeCode.DObject); //// CALL Operators.GetProperty(STACK,<field name>,<type desc>,<quiet>); //EmitName(codeGenerator); //codeGenerator.EmitLoadClassContext(); //il.LoadBool(codeGenerator.ChainBuilder.QuietRead); //il.Emit(OpCodes.Call, Methods.Operators.GetProperty); //return PhpTypeCode.Object; string fieldName = (this is DirectVarUse) ? ((DirectVarUse)this).VarName.Value : null; Expression fieldNameExpr = (this is IndirectVarUse) ? ((IndirectVarUse)this).VarNameEx : null; bool quietRead = wantRef ? false : codeGenerator.ChainBuilder.QuietRead; return(codeGenerator.CallSitesBuilder.EmitGetProperty( codeGenerator, wantRef, isMemberOf, null, null, null, fieldName, fieldNameExpr, quietRead)); } // call GetProperty/GetObjectPropertyRef codeGenerator.ChainBuilder.Lengthen(); // loads the variable which field is gotten: PhpTypeCode var_type_code = isMemberOf.Emit(codeGenerator); if (codeGenerator.ChainBuilder.Exists) { Debug.Assert(var_type_code == PhpTypeCode.DObject); // CALL Operators.GetObjectPropertyRef(STACK,<field name>,<type desc>); EmitName(codeGenerator); codeGenerator.EmitLoadClassContext(); il.Emit(OpCodes.Call, Methods.Operators.GetObjectPropertyRef); } else { Debug.Assert(var_type_code == PhpTypeCode.ObjectAddress); // CALL Operators.GetPropertyRef(ref STACK,<field name>,<type desc>,<script context>); EmitName(codeGenerator); codeGenerator.EmitLoadClassContext(); codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Call, Methods.Operators.GetPropertyRef); // stores the value of variable back: SimpleVarUse simple_var = isMemberOf as SimpleVarUse; if (simple_var != null) { simple_var.EmitLoadAddress_StoreBack(codeGenerator); } } return(PhpTypeCode.PhpReference); }
private void EmitVariableIssetOptimized(SimpleVarUse variable) { // Template: for DirectVarUse // ISSET($x) // ldloc local // *** if local is of type PhpReference // ldfld PhpReference.Value // *** // ldnull // ceq // ldc.i4.0 // ceq DirectVarUse direct = variable as DirectVarUse; if (direct != null) { VariablesTable.Entry entry = currentVariablesTable[direct.VarName]; entry.Variable.EmitLoad(il); if (entry.IsPhpReference) { il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); } il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ceq); return; } IndirectVarUse indirect_var = (IndirectVarUse)variable; indirect_var.EmitSwitch_LoadLocal(this); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ceq); }
/// <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> /// Emits IL instructions that ensure that the specified variable is an instance of <see cref="PhpObject"/>. /// </summary> /// <param name="variable">Variable that should be examined.</param> /// <remarks> /// This method is used in operators chains. Nothing is expected on the evaluation stack. /// If the specified variable is an instance of <see cref="PhpObject"/> /// it is left on the evaluation stack. Otherwise the control is transfered to the end of /// the chain. /// </remarks> public void EmitEnsureVariableIsObject(SimpleVarUse variable) { ILEmitter il = codeGenerator.IL; DirectVarUse direct_variable = variable as DirectVarUse; if (direct_variable != null && direct_variable.VarName.IsThisVariableName) { // special treatment of $this switch (codeGenerator.LocationStack.LocationType) { case LocationTypes.GlobalCode: { // load $this from one of Main's arguments and check for null Label this_non_null = il.DefineLabel(); codeGenerator.EmitLoadSelf(); il.Emit(OpCodes.Brtrue_S, this_non_null); codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); il.Emit(OpCodes.Br, TopChain.ErrorLabel); il.MarkLabel(this_non_null, true); codeGenerator.EmitLoadSelf(); break; } case LocationTypes.FunctionDecl: { // always throws error codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); il.Emit(OpCodes.Br, TopChain.ErrorLabel); break; } case LocationTypes.MethodDecl: { CompilerLocationStack.MethodDeclContext context = codeGenerator.LocationStack.PeekMethodDecl(); if (context.Method.IsStatic) { // always throws error codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); il.Emit(OpCodes.Br, TopChain.ErrorLabel); } else { // arg0 or <proxy> in instance methods codeGenerator.EmitLoadSelf(); } break; } default: Debug.Assert(false, "Invalid location type."); break; } } else { // Template: PhpObject EnsureVariableIsObject(ref object,ScriptContext) // Load variable's address // if (variable is FunctionCall) // { // variable.Emit(this); // EmitLoadScriptContext(); // il.Emit(OpCodes.Call, Methods.Operators.EnsureVariableIsObject); // // Store the changed destVar into table of variables (do nothing in optimalized functions) // } // else // { variable.EmitLoadAddress(codeGenerator); codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Call, Methods.Operators.EnsureVariableIsObject); // Store the changed destVar into table of variables (do nothing in optimalized functions) variable.EmitLoadAddress_StoreBack(codeGenerator); // } EmitErrorCheck(false); } }
/// <summary> /// Emits IL instructions that ensure that the specified variable is an instance of <see cref="PhpArray"/>. /// </summary> /// <param name="variable">Variable's name-index to a table of identifiers.</param> /// <remarks> /// This method is used in operators chains. Nothing is expected on the evaluation stack. /// If the specified variable is an instance of <see cref="PhpArray"/> /// it is left on the evaluation stack. Otherwise the control is transfered to the end of /// the chain. /// </remarks> public void EmitEnsureVariableIsArray(SimpleVarUse variable) { // Template: PhpArray EnsureVariableIsArray(ref object) // Load variable's address //this.EmitVariableLoadAddress(variable); variable.EmitLoadAddress(codeGenerator); codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.EnsureVariableIsArray); // Store the changed destVar into table of variables (do nothing in optimalized functions) variable.EmitLoadAddress_StoreBack(codeGenerator); EmitErrorCheck(true); }
/// <summary> /// Emits IL instructions that loads the value of array's item as a <see cref="PHP.Core.PhpReference"/>. /// </summary> /// <param name="variable">A simple variable.</param> /// <param name="index"><see cref="Expression"/> determining the index.</param> public PhpTypeCode EmitGetItemRef(SimpleVarUse/*!*/ variable, Expression index) { IsArrayItem = true; IsLastMember = true; PhpTypeCode index_type_code = PhpTypeCode.Invalid; // index: if (index != null) index_type_code = codeGenerator.EmitArrayKey(this, index); // array: variable.Emit(codeGenerator); // LOAD Operators.GetItemRef([<index>], ref <variable>) codeGenerator.EmitGetItem(index_type_code, index, true); // store the changed variable into table of variables (do nothing in optimalized functions) variable.EmitLoadAddress_StoreBack(codeGenerator); IsArrayItem = false; return PhpTypeCode.PhpReference; }
/// <summary> /// Emits IL instructions that load a value of the specified property of an object. /// </summary> /// <param name="variable"><see cref="PHP.Core.AST.VariableUse"/> class determining the name of the field.</param> /// <remarks>Expects that the <see cref="PHP.Core.Reflection.DObject"/> whose property value should be obtained /// is loaded on the evaluation stack. The value of the property is left on the evaluation stack.</remarks> public PhpTypeCode EmitGetProperty(SimpleVarUse variable) { Debug.Assert(variable is DirectVarUse || variable is IndirectVarUse); var il = codeGenerator.IL; // we already have the instance value on top of the stack, // it must be stored in local variable first so we can call // call CallSite normally. // <memberOf> = <STACK:variable>: var memberOf = il.GetTemporaryLocal(Types.Object[0]); il.Stloc(memberOf); // create and call the CallSite: string fieldName = (variable is DirectVarUse) ? ((DirectVarUse)variable).VarName.Value : null; Expression fieldNameExpr = (variable is IndirectVarUse) ? ((IndirectVarUse)variable).VarNameEx : null; var result = codeGenerator.CallSitesBuilder.EmitGetProperty(codeGenerator, false, null, null, new IndexedPlace(memberOf), null, fieldName, fieldNameExpr, QuietRead); // return temporary variable: il.ReturnTemporaryLocal(memberOf); // return result; //// CALL object Operators.GetProperty(<STACK:variable>,<field name>,<type desc>); //variable.EmitName(codeGenerator); //codeGenerator.EmitLoadClassContext(); //codeGenerator.IL.LoadBool(QuietRead); //codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.GetProperty); //return PhpTypeCode.Object; }
/// <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> /// Tries to find an instance of <see cref="DProperty"/> that corresponds to an instance property given by /// <paramref name="varObject"/> and <paramref name="fieldName"/>. Currently it is possible only if /// <paramref name="varObject"/> represents <B>$this</B> and <paramref name="fieldName"/> is a compile time /// known instance property, which is surely accessible from current location. /// </summary> /// <param name="varObject">Represents the left side of <B>-></B>.</param> /// <param name="fieldName">Represents the right side of <B>-></B>.</param> /// <param name="location">Current location, valid only if the return value is <B>null</B>. Used by the caller to /// decide what kind of run time access should be emitted.</param> /// <returns>A valid non-<B>null</B> <see cref="PhpField"/> if the field was found, <B>null</B> otherwise.</returns> internal DProperty ResolveProperty(VarLikeConstructUse varObject, SimpleVarUse fieldName, out LocationTypes location) { DirectVarUse direct_var = varObject as DirectVarUse; DirectVarUse direct_field_name; if (direct_var != null && (direct_field_name = fieldName as DirectVarUse) != null && direct_var.IsMemberOf == null && direct_var.VarName.IsThisVariableName) { ILEmitter il = codeGenerator.IL; location = codeGenerator.LocationStack.LocationType; switch (location) { case LocationTypes.GlobalCode: { // load $this from one of Main's arguments and check for null Label this_non_null = il.DefineLabel(); codeGenerator.EmitLoadSelf(); il.Emit(OpCodes.Brtrue_S, this_non_null); codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); il.Emit(OpCodes.Br, TopChain.ErrorLabel); il.MarkLabel(this_non_null, true); return null; } case LocationTypes.FunctionDecl: { // always throws error codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); il.Emit(OpCodes.Br, TopChain.ErrorLabel); return null; } case LocationTypes.MethodDecl: { CompilerLocationStack.MethodDeclContext context = codeGenerator.LocationStack.PeekMethodDecl(); if (context.Method.IsStatic) { // always throws error codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); il.Emit(OpCodes.Br, TopChain.ErrorLabel); location = LocationTypes.FunctionDecl; return null; } else { DProperty property; if (context.Type.GetProperty(direct_field_name.VarName, context.Type, out property) == GetMemberResult.OK && !property.IsStatic) { return property; } } break; } } } location = LocationTypes.MethodDecl; return null; }
/// <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); }