/// <summary> /// Emits IL instructions that load the "$this" variable onto the evaluation stack. /// </summary> private PhpTypeCode EmitLoadThis(CodeGenerator codeGenerator) { ILEmitter il = codeGenerator.IL; CompilerLocationStack locationStack = codeGenerator.LocationStack; // special treatment of $this switch (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.Dup); il.Emit(OpCodes.Brtrue_S, this_non_null); il.Emit(OpCodes.Ldstr, VariableName.ThisVariableName.Value); codeGenerator.EmitPhpException(Methods.PhpException.UndefinedVariable); il.MarkLabel(this_non_null, true); return(PhpTypeCode.Object); } case LocationTypes.FunctionDecl: { // always null il.Emit(OpCodes.Ldstr, VariableName.ThisVariableName.Value); codeGenerator.EmitPhpException(Methods.PhpException.UndefinedVariable); il.Emit(OpCodes.Ldnull); return(PhpTypeCode.Object); } case LocationTypes.MethodDecl: { CompilerLocationStack.MethodDeclContext context = locationStack.PeekMethodDecl(); if (context.Method.IsStatic) { // always null in static methods il.Emit(OpCodes.Ldstr, VariableName.ThisVariableName.Value); codeGenerator.EmitPhpException(Methods.PhpException.UndefinedVariable); il.Emit(OpCodes.Ldnull); return(PhpTypeCode.Object); } else { // arg0 or <proxy> in instance methods codeGenerator.EmitLoadSelf(); return(PhpTypeCode.DObject); } } default: Debug.Fail("Invalid location type."); return(PhpTypeCode.Invalid); } }
/// <summary> /// Loads a PhpReference to "this" special variable to the evaluation stack. /// If "this" is not available, loads an empty PhpReference. /// </summary> private PhpTypeCode EmitLoadThisRef(CodeGenerator /*!*/ codeGenerator) { ILEmitter il = codeGenerator.IL; switch (codeGenerator.LocationStack.LocationType) { case LocationTypes.GlobalCode: { // load $this from one of Main's arguments: codeGenerator.EmitLoadSelf(); // NOTE: If $this is used by ref somewhere in the method each access to it is boxed to the reference. // Only calls to methods use the "this" pointer itself. Thus the rule "no duplicate pointers" is slightly // broken here yet everything should work fine. il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); break; } case LocationTypes.FunctionDecl: { // always null referencing PhpReference il.Emit(OpCodes.Newobj, Constructors.PhpReference_Void); break; } case LocationTypes.MethodDecl: { CompilerLocationStack.MethodDeclContext context = codeGenerator.LocationStack.PeekMethodDecl(); if (context.Method.IsStatic) { // always null referencing PhpReference in static methods il.Emit(OpCodes.Newobj, Constructors.PhpReference_Void); } else { // arg0 or <proxy> referencing PhpReference in instance methods codeGenerator.EmitLoadSelf(); // NOTE: If $this is used by ref somewhere in the method each access to it is boxed to the reference. // Only calls to methods use the "this" pointer itself. Thus the rule "no duplicate pointers" is slightly // broken here yet everything should work fine. il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); } break; } default: Debug.Fail("Invalid location type."); break; } // always returns a reference: return(PhpTypeCode.PhpReference); }
/// <summary> /// Emits IL instructions that prepare a field of $this for writing. /// </summary> private AssignmentCallback EmitWriteFieldOfThis(CodeGenerator /*!*/ codeGenerator, bool writeRef) { ILEmitter il = codeGenerator.IL; // $this->a 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, codeGenerator.ChainBuilder.ErrorLabel); il.MarkLabel(this_non_null, true); // prepare the stack for SetObjectProperty call codeGenerator.EmitLoadSelf(); EmitName(codeGenerator); return(new AssignmentCallback(EmitCallSetObjectField)); } case LocationTypes.FunctionDecl: { // always throws error codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); return(new AssignmentCallback(EmitPopValue)); } case LocationTypes.MethodDecl: { CompilerLocationStack.MethodDeclContext context = codeGenerator.LocationStack.PeekMethodDecl(); if (context.Method.IsStatic) { // always throws error codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); return(new AssignmentCallback(EmitPopValue)); } // attempt direct field writing (DirectVarUse only) return(EmitWriteFieldOfThisInInstanceMethod(codeGenerator, writeRef)); } } Debug.Fail("Invalid lcoation type."); return(null); }
/// <summary> /// Emits IL instructions that read the value of a field of $this instance. /// </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></returns> private PhpTypeCode EmitReadFieldOfThis(CodeGenerator /*!*/ codeGenerator, bool wantRef) { ILEmitter il = codeGenerator.IL; // $this->a 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(); Label reading_over = il.DefineLabel(); codeGenerator.EmitLoadSelf(); il.Emit(OpCodes.Brtrue_S, this_non_null); EmitThisUsedOutOfObjectThrow(codeGenerator, wantRef); il.Emit(OpCodes.Br, reading_over); il.MarkLabel(this_non_null, true); // call GetObjectProperty/GetObjectPropertyRef EmitGetFieldOfPlace(codeGenerator.SelfPlace, codeGenerator, wantRef); il.MarkLabel(reading_over, true); break; } case LocationTypes.FunctionDecl: { EmitThisUsedOutOfObjectThrow(codeGenerator, wantRef); break; } case LocationTypes.MethodDecl: { CompilerLocationStack.MethodDeclContext context = codeGenerator.LocationStack.PeekMethodDecl(); if (context.Method.IsStatic) { EmitThisUsedOutOfObjectThrow(codeGenerator, wantRef); break; } // attempt direct field reading (DirectVarUse only) return(EmitReadFieldOfThisInInstanceMethod(codeGenerator, wantRef)); } } return(wantRef ? PhpTypeCode.PhpReference : PhpTypeCode.Object); }
internal override PhpTypeCode EmitIsset(CodeGenerator codeGenerator, bool empty) { // Template: "isset(x)" x != null // isset doesn't distinguish between the NULL and uninitialized variable // a reference is dereferenced, i.e. isset tells us whether the referenced variable is set Debug.Assert(access == AccessType.Read); // Cases 1, 4, 5, 6, 9 never reached Debug.Assert(codeGenerator.ChainBuilder.IsMember == false); // Case 3 never reached Debug.Assert(codeGenerator.ChainBuilder.IsArrayItem == false); codeGenerator.ChainBuilder.QuietRead = true; // 2,7,8 if (this.isMemberOf != null) { // 2: $b->a // 8: b[]->a codeGenerator.ChainBuilder.Create(); codeGenerator.ChainBuilder.Begin(); codeGenerator.ChainBuilder.QuietRead = true; EmitReadField(codeGenerator, false); codeGenerator.ChainBuilder.End(); return(PhpTypeCode.Object); } // 7: $a // Check whether this variable is set //codeGenerator.EmitVariableIsset(this); ILEmitter il = codeGenerator.IL; if (varName.IsThisVariableName && codeGenerator.LocationStack.LocationType == LocationTypes.MethodDecl) { CompilerLocationStack.MethodDeclContext context = codeGenerator.LocationStack.PeekMethodDecl(); if (!context.Method.IsStatic) { // $this is always set in instance methods il.Emit(OpCodes.Ldarg_0); return(PhpTypeCode.Object); } } this.EmitLoad(codeGenerator); return(PhpTypeCode.Object); }
/// <summary> /// Emits IL instructions that unset an instance field. /// </summary> /// <remarks> /// Nothing is expected on the evaluation stack. Nothing is left on the evaluation stack. /// </remarks> private void EmitUnsetField(CodeGenerator /*!*/ codeGenerator) { ILEmitter il = codeGenerator.IL; DirectVarUse direct_instance = isMemberOf as DirectVarUse; if (direct_instance != null && direct_instance.VarName.IsThisVariableName) { // $this->a 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, codeGenerator.ChainBuilder.ErrorLabel); il.MarkLabel(this_non_null, true); // call UnsetProperty codeGenerator.EmitLoadSelf(); il.Emit(OpCodes.Ldstr, varName.ToString()); // TODO codeGenerator.EmitLoadClassContext(); il.EmitCall(OpCodes.Call, Methods.Operators.UnsetProperty, null); return; } case LocationTypes.FunctionDecl: { // always throws error codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); il.Emit(OpCodes.Br, codeGenerator.ChainBuilder.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, codeGenerator.ChainBuilder.ErrorLabel); } else { DProperty property; if (context.Type.GetProperty(varName, context.Type, out property) == GetMemberResult.OK && !property.IsStatic) { // ask the DProperty to emit its unsetting code property.EmitUnset(codeGenerator, IndexedPlace.ThisArg, null, false); } else { // unable to resolve the field -> call UnsetProperty codeGenerator.EmitLoadSelf(); il.Emit(OpCodes.Ldstr, varName.ToString()); codeGenerator.EmitLoadClassContext(); il.EmitCall(OpCodes.Call, Methods.Operators.UnsetProperty, null); } } } break; } } else { // call UnsetProperty isMemberOf.Emit(codeGenerator); il.Emit(OpCodes.Ldstr, varName.ToString()); codeGenerator.EmitLoadClassContext(); il.EmitCall(OpCodes.Call, Methods.Operators.UnsetProperty, null); } }