/// <summary> /// Emits IL instructions that write the value of a field of $this instance when we know that we /// are in an instance method and hence there's a chance of actually resolving the field being written. /// </summary> /// <param name="codeGenerator">The current <see cref="CodeGenerator"/>.</param> /// <param name="writeRef">If <B>true</B> the value being written is a <see cref="PhpReference"/>; if /// <B>false</B> the value being written is an <see cref="Object"/>.</param> /// <returns></returns> internal virtual AssignmentCallback EmitWriteFieldOfThisInInstanceMethod(CodeGenerator/*!*/ codeGenerator, bool writeRef) { // prepare for SetObjectProperty call codeGenerator.EmitLoadSelf(); EmitName(codeGenerator); return new AssignmentCallback(EmitCallSetObjectField); }
/// <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; }
/// <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> /// 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 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> /// 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); } }
/// <summary> /// Emits dynamic inclusion. /// </summary> private PhpTypeCode EmitDynamicInclusion(CodeGenerator/*!*/ codeGenerator) { // do not generate dynamic auto inclusions: if (InclusionTypesEnum.IsAutoInclusion(inclusionType)) return PhpTypeCode.Void; ILEmitter il = codeGenerator.IL; // CALL context.DynamicInclude(<file name>,<relative includer source path>,variables,self,includer); codeGenerator.EmitLoadScriptContext(); codeGenerator.EmitConversion(fileNameEx, PhpTypeCode.String); il.Emit(OpCodes.Ldstr, codeGenerator.SourceUnit.SourceFile.RelativePath.ToString()); codeGenerator.EmitLoadRTVariablesTable(); codeGenerator.EmitLoadSelf(); codeGenerator.EmitLoadClassContext(); il.LoadLiteral(inclusionType); il.Emit(OpCodes.Call, Methods.ScriptContext.DynamicInclude); return PhpTypeCode.Object; }
/// <summary> /// Emits a static inclusion. /// </summary> private PhpTypeCode EmitStaticInclusion(CodeGenerator/*!*/ codeGenerator) { ILEmitter il = codeGenerator.IL; Label endif_label = il.DefineLabel(); Label else_label = il.DefineLabel(); MethodInfo method; // if the expression should be emitted: if (characteristic == Characteristic.StaticArgEvaluated) { if (!(fileNameEx is StringLiteral || fileNameEx is BinaryStringLiteral)) { // emits expression evaluation and ignores the result: fileNameEx.Emit(codeGenerator); il.Emit(OpCodes.Pop); } } if (characteristic == Characteristic.StaticAutoInclusion) { // calls the Main routine only if this script is the main one: il.Ldarg(ScriptBuilder.ArgIsMain); } else { RelativePath relativePath = new RelativePath(inclusion.Includee.RelativeSourcePath); // normalize the relative path // CALL context.StaticInclude(<relative included script source path>,<this script type>,<inclusion type>); codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Ldc_I4, (int)relativePath.Level); il.Emit(OpCodes.Ldstr, relativePath.Path); il.Emit(OpCodes.Ldtoken, inclusion.Includee.ScriptClassType); il.LoadLiteral(inclusionType); il.Emit(OpCodes.Call, Methods.ScriptContext.StaticInclude); } // IF (STACK) il.Emit(OpCodes.Brfalse, else_label); if (true) { // emits a call to the main helper of the included script: method = inclusion.Includee.MainHelper; // CALL <Main>(context, variables, self, includer, false): codeGenerator.EmitLoadScriptContext(); codeGenerator.EmitLoadRTVariablesTable(); codeGenerator.EmitLoadSelf(); codeGenerator.EmitLoadClassContext(); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Call, method); il.Emit(OpCodes.Br, endif_label); } // ELSE il.MarkLabel(else_label); if (true) { // LOAD <PhpScript.SkippedIncludeReturnValue>; il.LoadLiteral(ScriptModule.SkippedIncludeReturnValue); il.Emit(OpCodes.Box, ScriptModule.SkippedIncludeReturnValue.GetType()); } il.MarkLabel(endif_label); // END IF return PhpTypeCode.Object; }