/// <summary> /// Stores a reference on the top of the stack to a specified variable. /// </summary> internal void StoreLocalRefAssign(CodeGenerator codeGenerator, VariablesTable.Entry variable, LocalBuilder variableName) { ILEmitter il = codeGenerator.IL; Debug.Assert(variable == null ^ variableName == null); if (variable != null) { Debug.Assert(variable.IsPhpReference); variable.Variable.EmitStore(il); } else { // temp = STACK LocalBuilder temp = il.GetTemporaryLocal(Types.PhpReference[0], true); il.Stloc(temp); // CALL Operators.SetVariableRef(<local variables table>,<name>,temp); codeGenerator.EmitLoadScriptContext(); codeGenerator.EmitLoadRTVariablesTable(); il.Ldloc(variableName); il.Ldloc(temp); il.Emit(OpCodes.Call, Methods.Operators.SetVariableRef); } }
/// <summary> /// Stores a value on the top of the stack to a specified variable. /// </summary> internal static void StoreLocalAssign(IndirectVarUse node, CodeGenerator codeGenerator, VariablesTable.Entry variable, LocalBuilder variableName) { ILEmitter il = codeGenerator.IL; Debug.Assert(variable == null ^ variableName == null); LocalBuilder temp; if (variable != null) { if (variable.IsPhpReference) { // temp = STACK temp = il.GetTemporaryLocal(Types.Object[0], true); il.Stloc(temp); // <variable>.value = temp; variable.Variable.EmitLoad(il); il.Ldloc(temp); il.Emit(OpCodes.Stfld, Fields.PhpReference_Value); } else { variable.Variable.EmitStore(il); } } else { // temp = STACK temp = il.GetTemporaryLocal(Types.Object[0], true); il.Stloc(temp); // CALL Operators.SetVariable(<local variables table>,<name>,temp); codeGenerator.EmitLoadScriptContext(); codeGenerator.EmitLoadRTVariablesTable(); il.Ldloc(variableName); il.Ldloc(temp); il.Emit(OpCodes.Call, Methods.Operators.SetVariable); } }
/// <summary> /// Loads the value represented by this object from the runtime variables table, /// stores it to a local variable and loads the address of this local. /// </summary> /// <remarks>This method is used only in non-optimized user functions and global code. /// Specified local variable is obtained from current <see cref="ILEmitter"/> by /// <see cref="ILEmitter.GetTemporaryLocal"/> and stored to <see cref="TabledLocalAddressStorage"/> /// for later use. Once the local become useless, <see cref="ILEmitter.ReturnTemporaryLocal"/> /// should be called. /// </remarks> /// <param name="codeGenerator">Currently used <see cref="CodeGenerator"/>.</param> internal virtual void LoadTabledVariableAddress(CodeGenerator codeGenerator) { // This function should be call only once on every SimpleVarUse object // TODO: ASSERTION FAILS (e.g. PhpMyAdmin, common.lib.php) // Debug.Assert(this.TabledLocalAddressStorage == null); ILEmitter il = codeGenerator.IL; // Load the value represented by this node from the runtime variables table // LOAD Operators.GetVariableUnchecked(<script context>, <local variables table>, <variable name>); codeGenerator.EmitLoadScriptContext(); codeGenerator.EmitLoadRTVariablesTable(); EmitName(codeGenerator); codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.GetVariableUnchecked); // Get local from ILEmitter this.TabledLocalAddressStorage = il.GetTemporaryLocal(Types.Object[0]); // Store the value il.Stloc(this.TabledLocalAddressStorage); // Load the address il.Ldloca(this.TabledLocalAddressStorage); }
/// <summary> /// Emits the try block and the catch blocks. /// </summary> /// <param name="codeGenerator">A code generator.</param> /// <remarks> /// <code> /// try /// { /// // guarded code // /// } /// catch(E1 $e1) /// { /// // E1 // /// } /// catch(E2 $e2) /// { /// // E2 // /// } /// </code> /// is translated as follows: /// <code> /// try /// { /// // guarded code // /// } /// catch(PhpUserException _e) /// { /// PhpObject _o = _e.UserException; /// if (_o instanceOf E1) /// { /// $e1 = _o; /// // E1 // /// } /// else if (_o instanceOf E2) /// { /// $e2 = _o; /// // E2 // /// } /// else /// { /// throw; /// } /// } /// </code> /// </remarks> internal override void Emit(CodeGenerator /*!*/ codeGenerator) { Statistics.AST.AddNode("TryStmt"); // emit try block without CLR exception block if possible if (!HasCatches && !HasFinallyStatements) { this.Statements.Emit(codeGenerator); return; } // emit CLR exception block ILEmitter il = codeGenerator.IL; codeGenerator.ExceptionBlockNestingLevel++; // TRY Label end_label = il.BeginExceptionBlock(); this.Statements.Emit(codeGenerator); // catches if (HasCatches) { // catch (PHP.Core.ScriptDiedException) // { throw; } il.BeginCatchBlock(typeof(PHP.Core.ScriptDiedException)); il.Emit(OpCodes.Rethrow); // catch (System.Exception ex) il.BeginCatchBlock(typeof(System.Exception)); // <exception_local> = (DObject) (STACK is PhpUserException) ? ((PhpUserException)STACK).UserException : ClrObject.WrapRealObject(STACK) Label clrExceptionLabel = il.DefineLabel(); Label wrapEndLabel = il.DefineLabel(); LocalBuilder exception_local = il.GetTemporaryLocal(typeof(DObject)); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Isinst, typeof(PHP.Core.PhpUserException)); // <STACK> as PhpUserException il.Emit(OpCodes.Brfalse, clrExceptionLabel); // if (<STACK> as PhpUserException != null) { il.Emit(OpCodes.Ldfld, Fields.PhpUserException_UserException); il.Emit(OpCodes.Br, wrapEndLabel); } // else il.MarkLabel(clrExceptionLabel); { il.Emit(OpCodes.Call, Methods.ClrObject_WrapRealObject); } il.MarkLabel(wrapEndLabel); il.Stloc(exception_local); // emits all PHP catch-blocks processing into a single CLI catch-block: foreach (CatchItem c in catches) { Label next_catch_label = il.DefineLabel(); // IF (exception <instanceOf> <type>); c.Emit(codeGenerator, exception_local, end_label, next_catch_label); // ELSE il.MarkLabel(next_catch_label); } il.ReturnTemporaryLocal(exception_local); // emits the "else" branch invoked if the exceptions is not catched: il.Emit(OpCodes.Rethrow); } // finally if (HasFinallyStatements) { finallyItem.Emit(codeGenerator); } // il.EndExceptionBlock(); codeGenerator.ExceptionBlockNestingLevel--; }
/// <author>Tomas Matousek</author> /// <remarks> /// Emits the following code: /// <code> /// IPhpEnumerable enumerable = ARRAY as IPhpEnumerable; /// if (enumerable==null) /// { /// PhpException.InvalidForeachArgument(); /// } /// else /// FOREACH_BEGIN: /// { /// IDictionaryEnumerator enumerator = enumerable.GetForeachEnumerator(KEYED,ALIASED,TYPE_HANDLE); /// /// goto LOOP_TEST; /// LOOP_BEGIN: /// { /// ASSIGN(value,enumerator.Value); /// ASSIGN(key,enumerator.Key); /// /// BODY; /// } /// LOOP_TEST: /// if (enumerator.MoveNext()) goto LOOP_BEGIN; /// } /// FOREACH_END: /// </code> /// </remarks> /// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal override void Emit(CodeGenerator codeGenerator) { Statistics.AST.AddNode("Loop.Foreach"); ILEmitter il = codeGenerator.IL; Label foreach_end = il.DefineLabel(); Label foreach_begin = il.DefineLabel(); Label loop_begin = il.DefineLabel(); Label loop_test = il.DefineLabel(); codeGenerator.BranchingStack.BeginLoop(loop_test, foreach_end, codeGenerator.ExceptionBlockNestingLevel); LocalBuilder enumerable = il.GetTemporaryLocal(typeof(IPhpEnumerable)); // marks foreach "header" (the first part of the IL code): codeGenerator.MarkSequencePoint( enumeree.Position.FirstLine, enumeree.Position.FirstColumn, valueVariable.Position.LastLine, valueVariable.Position.LastColumn + 1); // enumerable = array as IPhpEnumerable; enumeree.Emit(codeGenerator); il.Emit(OpCodes.Isinst, typeof(IPhpEnumerable)); il.Stloc(enumerable); // if (enumerable==null) il.Ldloc(enumerable); il.Emit(OpCodes.Brtrue, foreach_begin); { // CALL PhpException.InvalidForeachArgument(); codeGenerator.EmitPhpException(Methods.PhpException.InvalidForeachArgument); il.Emit(OpCodes.Br, foreach_end); } // FOREACH_BEGIN: il.MarkLabel(foreach_begin); { LocalBuilder enumerator = il.GetTemporaryLocal(typeof(System.Collections.IDictionaryEnumerator)); // enumerator = enumerable.GetForeachEnumerator(KEYED,ALIASED,TYPE_HANDLE); il.Ldloc(enumerable); il.LoadBool(keyVariable != null); il.LoadBool(valueVariable.Alias); codeGenerator.EmitLoadClassContext(); il.Emit(OpCodes.Callvirt, Methods.IPhpEnumerable_GetForeachEnumerator); il.Stloc(enumerator); // goto LOOP_TEST; il.Emit(OpCodes.Br, loop_test); // LOOP_BEGIN: il.MarkLabel(loop_begin); { // enumerator should do dereferencing and deep copying (if applicable): // ASSIGN(value,enumerator.Value); valueVariable.Emit(codeGenerator); il.Ldloc(enumerator); il.Emit(OpCodes.Callvirt, Properties.IDictionaryEnumerator_Value.GetGetMethod()); if (valueVariable.Alias) { il.Emit(OpCodes.Castclass, typeof(PhpReference)); } valueVariable.EmitAssign(codeGenerator); if (keyVariable != null) { // enumerator should do dereferencing and deep copying (if applicable): // ASSIGN(key,enumerator.Key); keyVariable.Emit(codeGenerator); il.Ldloc(enumerator); il.Emit(OpCodes.Callvirt, Properties.IDictionaryEnumerator_Key.GetGetMethod()); keyVariable.EmitAssign(codeGenerator); } // BODY: body.Emit(codeGenerator); } // LOOP_TEST: il.MarkLabel(loop_test); // marks foreach "header" (the second part of the code): codeGenerator.MarkSequencePoint( enumeree.Position.FirstLine, enumeree.Position.FirstColumn, valueVariable.Position.LastLine, valueVariable.Position.LastColumn + 1); // if (enumerator.MoveNext()) goto LOOP_BEGIN; il.Ldloc(enumerator); il.Emit(OpCodes.Callvirt, Methods.IEnumerator_MoveNext); il.Emit(OpCodes.Brtrue, loop_begin); // il.ReturnTemporaryLocal(enumerator); } // FOREACH_END: il.MarkLabel(foreach_end); il.ReturnTemporaryLocal(enumerable); codeGenerator.BranchingStack.EndLoop(); il.ForgetLabel(foreach_end); il.ForgetLabel(foreach_begin); il.ForgetLabel(loop_begin); il.ForgetLabel(loop_test); }
/// <summary> /// Emits local variable switch and performs a specified operation on each case. /// </summary> /// <param name="codeGenerator">The code generator.</param> /// <param name="method">The operation performed in each case.</param> internal void EmitSwitch(CodeGenerator codeGenerator, SwitchMethod method) { ILEmitter il = codeGenerator.IL; Debug.Assert(method != null); Label default_case = il.DefineLabel(); Label end_label = il.DefineLabel(); LocalBuilder ivar_local = il.GetTemporaryLocal(Types.String[0], true); LocalBuilder non_interned_local = il.DeclareLocal(Types.String[0]); VariablesTable variables = codeGenerator.CurrentVariablesTable; Label[] labels = new Label[variables.Count]; // non_interned_local = <name expression>; EmitName(codeGenerator); il.Stloc(non_interned_local); // ivar_local = String.IsInterned(non_interned_local) il.Ldloc(non_interned_local); il.Emit(OpCodes.Call, Methods.String_IsInterned); il.Stloc(ivar_local); // switch for every compile-time variable: int i = 0; foreach (VariablesTable.Entry variable in variables) { labels[i] = il.DefineLabel(); // IF (ivar_local == <i-th variable name>) GOTO labels[i]; il.Ldloc(ivar_local); il.Emit(OpCodes.Ldstr, variable.VariableName.ToString()); il.Emit(OpCodes.Beq, labels[i]); i++; } // GOTO default_case: il.Emit(OpCodes.Br, default_case); // operation on each variable: i = 0; foreach (VariablesTable.Entry variable in variables) { // labels[i]: il.MarkLabel(labels[i]); // operation: method(codeGenerator, variable, null); // GOTO end; il.Emit(OpCodes.Br, end_label); i++; } // default case - new variable created at runtime: il.MarkLabel(default_case); method(codeGenerator, null, non_interned_local); // END: il.MarkLabel(end_label); }