public BoundPrimitiveTypeRef(PhpTypeCode type) { _type = type; // IsNullable = type == PhpTypeCode.Null || type == PhpTypeCode.Mixed; }
public MethodInfo EmitLambda(string name, AST.DirectVarUse variable, AST.Expression /*!*/ expression, PhpTypeCode returnType) { MethodBuilder result = linqContextBuilder.DefineMethod(name, MethodAttributes.PrivateScope | MethodAttributes.SpecialName, PhpTypeCodeEnum.ToType(returnType), new Type[] { typeof(object) }); ILEmitter il = new ILEmitter(result); EnterLambdaDeclaration(il); if (variable != null) { // <variable> = COPY(<argument>); variable.Emit(cg); il.Emit(OpCodes.Ldarg_1); cg.EmitVariableCopy(CopyReason.Assigned, null); variable.EmitAssign(cg); } cg.EmitConversion(expression, returnType); il.Emit(OpCodes.Ret); LeaveLambdaDeclaration(); return(result); }
public MethodInfo EmitLambda(string name, AST.DirectVarUse variable, AST.Expression/*!*/ expression, PhpTypeCode returnType) { MethodBuilder result = linqContextBuilder.DefineMethod(name, MethodAttributes.PrivateScope | MethodAttributes.SpecialName, PhpTypeCodeEnum.ToType(returnType), new Type[] { typeof(object) }); ILEmitter il = new ILEmitter(result); EnterLambdaDeclaration(il); if (variable != null) { // <variable> = COPY(<argument>); variable.Emit(cg); il.Emit(OpCodes.Ldarg_1); cg.EmitVariableCopy(CopyReason.Assigned, null); variable.EmitAssign(cg); } cg.EmitConversion(expression, returnType); il.Emit(OpCodes.Ret); LeaveLambdaDeclaration(); return result; }
/// <summary> /// Emits code for loading the variable's value as a <see cref="PhpReference"/>. This function is called only /// by first AST node in chain. /// </summary> /// <param name="codeGenerator"></param> private PhpTypeCode EmitNodeReadRef(CodeGenerator codeGenerator) { // Cases 1, 4, 5, 6, 9 never reached Debug.Assert(codeGenerator.ChainBuilder.IsMember == false); // Case 3 never reached Debug.Assert(codeGenerator.ChainBuilder.IsArrayItem == false); // 2, 7, 8 if (this.isMemberOf != null) { // 2: $b->a // 8: b[]->a codeGenerator.ChainBuilder.Create(); codeGenerator.ChainBuilder.Begin(); if (this.isMemberOf is FunctionCall) { codeGenerator.ChainBuilder.LoadAddressOfFunctionReturnValue = true; } PhpTypeCode type_code = EmitReadField(codeGenerator, true); Debug.Assert(type_code == PhpTypeCode.PhpReference); codeGenerator.ChainBuilder.EndRef(); return(PhpTypeCode.PhpReference); } // 7: $a EmitLoadRef(codeGenerator); return(PhpTypeCode.PhpReference); }
/// <summary> /// Retrieves <see cref="Type"/> from a specified <see cref="PhpTypeCode"/>. /// </summary> public static Type ToType(PhpTypeCode code) { switch (code) { case PhpTypeCode.String: return(Types.String[0]); case PhpTypeCode.Integer: return(Types.Int[0]); case PhpTypeCode.LongInteger: return(Types.LongInt[0]); case PhpTypeCode.Boolean: return(Types.Bool[0]); case PhpTypeCode.Double: return(Types.Double[0]); case PhpTypeCode.Object: return(Types.Object[0]); case PhpTypeCode.PhpReference: return(Types.PhpReference[0]); case PhpTypeCode.PhpArray: return(Types.PhpArray[0]); case PhpTypeCode.DObject: return(Types.DObject[0]); case PhpTypeCode.PhpResource: return(typeof(PhpResource)); case PhpTypeCode.PhpBytes: return(typeof(PhpBytes)); case PhpTypeCode.PhpString: return(typeof(PhpString)); case PhpTypeCode.Void: return(Types.Void); case PhpTypeCode.LinqSource: return(Types.IEnumerableOfObject); default: return(null); } }
private PhpTypeCode EmitNodeReadRef(IndirectVarUse node, CodeGenerator codeGenerator) { // Cases 1, 4, 5, 6, 9 never reached Debug.Assert(codeGenerator.ChainBuilder.IsMember == false); // Case 3 never reached Debug.Assert(codeGenerator.ChainBuilder.IsArrayItem == false); // 2, 7, 8 if (node.IsMemberOf != null) { // 2: $b->a // 8: b[]->a codeGenerator.ChainBuilder.Create(); codeGenerator.ChainBuilder.Begin(); if (node.IsMemberOf is FunctionCall) { codeGenerator.ChainBuilder.LoadAddressOfFunctionReturnValue = true; } PhpTypeCode result = EmitReadField(node, codeGenerator, true); codeGenerator.ChainBuilder.EndRef(); Debug.Assert(result == PhpTypeCode.PhpReference); } else { // 7: $a //codeGenerator.EmitVariableLoadRef(this); EmitLoadRef(node, codeGenerator); } return(PhpTypeCode.PhpReference); }
private PhpTypeCode EmitNodeReadUnknown(CodeGenerator codeGenerator) { if (codeGenerator.ChainBuilder.IsMember) { // 1,4,5,6,9 if (this.isMemberOf != null) { // 1: ...->$a->... codeGenerator.ChainBuilder.Lengthen(); // for hop over -> PhpTypeCode res = isMemberOf.Emit(codeGenerator); if (res != PhpTypeCode.PhpRuntimeChain) { codeGenerator.EmitBoxing(res); codeGenerator.ChainBuilder.EmitCreateRTChain(); } codeGenerator.ChainBuilder.EmitRTChainAddField(this); return(PhpTypeCode.PhpRuntimeChain); } if (codeGenerator.ChainBuilder.IsArrayItem && !codeGenerator.ChainBuilder.IsLastMember) { // 6: $b->${"a"}[3] codeGenerator.ChainBuilder.EmitRTChainAddField(this); return(PhpTypeCode.PhpRuntimeChain); } // 4: ${"a"}[][] // 5: $$a->b->c->... // 9: $$a->b this.EmitLoadRef(codeGenerator); codeGenerator.ChainBuilder.EmitCreateRTChain(); return(PhpTypeCode.PhpRuntimeChain); } // 2,3,7,8 if (this.isMemberOf != null) { // 2: $b->$a // 8: b[]->$a codeGenerator.ChainBuilder.Create(); codeGenerator.ChainBuilder.Begin(); codeGenerator.ChainBuilder.Lengthen(); // for hop over -> PhpTypeCode res = isMemberOf.Emit(codeGenerator); if (res != PhpTypeCode.PhpRuntimeChain) { codeGenerator.EmitBoxing(res); codeGenerator.ChainBuilder.EmitCreateRTChain(); } codeGenerator.ChainBuilder.EmitRTChainAddField(this); codeGenerator.ChainBuilder.End(); return(PhpTypeCode.PhpRuntimeChain); } // 3: ${"a"}[3] // 7: $$a this.EmitLoadRef(codeGenerator); return(PhpTypeCode.PhpReference); }
/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal PhpTypeCode Emit(CodeGenerator /*!*/ codeGenerator) { codeGenerator.ChainBuilder.Create(); PhpTypeCode result = expression.Emit(codeGenerator); codeGenerator.ChainBuilder.End(); return(result); }
/// <summary> /// <c>True</c> iff given <paramref name="code"/> represents value that can be copied (is IPhpCloneable and implements some logic in Copy method). /// </summary> /// <param name="code"><see cref="PhpTypeCode"/>.</param> /// <returns>Wheter given <paramref name="code"/> represents value that can be copied.</returns> internal static bool IsDeeplyCopied(PhpTypeCode code) { return (code != PhpTypeCode.Void && code != PhpTypeCode.String && code != PhpTypeCode.Boolean && code != PhpTypeCode.Double && code != PhpTypeCode.Integer && code != PhpTypeCode.LongInteger && code != PhpTypeCode.PhpResource); }
/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal override PhpTypeCode Emit(CodeGenerator /*!*/ codeGenerator) { Statistics.AST.AddNode("IndirectVarUse"); PhpTypeCode result = PhpTypeCode.Invalid; switch (codeGenerator.SelectAccess(access)) { // This case occurs everytime we want to get current variable value // All we do is push the value onto the IL stack case AccessType.Read: // Push value onto a IL stack result = EmitNodeRead(codeGenerator); break; // This case occurs when the varible is written ($a = $b, then $a has Write mark) // We only prepare the stack for storing, the work will be done later, // by EmitAssign() case AccessType.Write: result = EmitNodeWrite(codeGenerator); break; case AccessType.None: EmitNodeRead(codeGenerator); codeGenerator.IL.Emit(OpCodes.Pop); result = PhpTypeCode.Void; break; case AccessType.ReadRef: // if the selector is set to the ReadRef, the chain is emitted as if it was written // (chained nodes are marked as ReadAndWrite): if (codeGenerator.AccessSelector == AccessType.ReadRef) { codeGenerator.AccessSelector = AccessType.Write; } result = EmitNodeReadRef(codeGenerator); Debug.Assert(result == PhpTypeCode.PhpReference); break; case AccessType.ReadUnknown: result = EmitNodeReadUnknown(codeGenerator); break; case AccessType.WriteRef: EmitNodeWriteRef(codeGenerator); result = PhpTypeCode.PhpReference; break; default: result = PhpTypeCode.Invalid; Debug.Fail(); break; } return(result); }
/// <summary> /// Emits code that loads a specified parameter on the evaluation stack. /// </summary> /// <param name="paramInfo">The parameter to load.</param> /// <param name="requiredTypeCode">Specifies whether <see cref="PhpReference"/> /// (<see cref="PhpTypeCode.PhpReference"/>), <see cref="object"/> (<see cref="PhpTypeCode.Object"/>), /// or the most fitting of these two should be loaded.</param> public void EmitLoadClrParameter(ParameterInfo /*!*/ paramInfo, PhpTypeCode requiredTypeCode) { if (paramInfo.IsOut) { il.Emit(OpCodes.Ldnull); } else { il.Ldarg(paramInfo.Position + paramOffset); // dereference ref param Type param_type = paramInfo.ParameterType; if (param_type.IsByRef) { param_type = param_type.GetElementType(); il.Ldind(param_type); } // convert the parameter to PHP type PhpTypeCode type_code = ClrOverloadBuilder.EmitConvertToPhp( il, param_type /*, * scriptContextPlace*/); il.EmitBoxing(type_code); } // check whether we have to create a PhpReference if (requiredTypeCode == PhpTypeCode.Object || (requiredTypeCode == PhpTypeCode.Unknown && !paramInfo.ParameterType.IsByRef)) { return; } if (paramInfo.ParameterType.IsByRef) { LocalBuilder ref_local = il.DeclareLocal(Types.PhpReference[0]); // remember the PhpReference in a local il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); il.Emit(OpCodes.Dup); il.Stloc(ref_local); referenceLocals[paramInfo.Position] = ref_local; } else { // no reference store-back is necessary il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); } }
public override PhpTypeCode Emit(ArrayEx node, CodeGenerator codeGenerator) { Debug.Assert(access == AccessType.Read || access == AccessType.None); ILEmitter il = codeGenerator.IL; // count integer and string keys: int int_count = 0; int string_count = 0; DetermineCapacities(node, out int_count, out string_count); // array = new PhpArray(<int_count>, <string_count>); il.Emit(OpCodes.Ldc_I4, int_count); il.Emit(OpCodes.Ldc_I4, string_count); il.Emit(OpCodes.Newobj, Constructors.PhpArray.Int32_Int32); if (codeGenerator.Context.Config.Compiler.Debug) { il.Emit(OpCodes.Nop); il.Emit(OpCodes.Nop); il.Emit(OpCodes.Nop); } foreach (var item in node.Items) { var itemcompiler = item.NodeCompiler <ItemCompiler>(); // CALL array.SetArrayItemRef(z, p); // CALL array.SetArrayItem(x, PhpVariable.Copy(y, CopyReason.Assigned)); // CALL array.SetArrayItem(PhpVariable.Copy(x, CopyReason.Assigned)) // CALL array.AddToEnd(x) il.Emit(OpCodes.Dup); PhpTypeCode index_type_code = itemcompiler.EmitIndex(item, codeGenerator); itemcompiler.EmitValue(item, codeGenerator); codeGenerator.EmitSetArrayItem(index_type_code, item.Index, item is RefItem, true); } switch (this.access) { case AccessType.Read: // keep array on the stack return(PhpTypeCode.PhpArray); case AccessType.None: // pop array from the stack il.Emit(OpCodes.Pop); return(PhpTypeCode.Void); } throw new InvalidOperationException(); //return PhpTypeCode.Invalid; }
public override PhpTypeCode Emit(EvalEx node, CodeGenerator codeGenerator) { // not emitted in release mode: Debug.Assert(access == AccessType.None || access == AccessType.Read || access == AccessType.ReadRef); Debug.Assert(codeGenerator.RTVariablesTablePlace != null, "Function should have variables table."); Statistics.AST.AddNode("EvalEx"); PhpTypeCode result = codeGenerator.EmitEval(EvalKinds.ExplicitEval, node.Code, node.Span, null, null); // handles return value according to the access type: codeGenerator.EmitReturnValueHandling(node, false, ref result); return(result); }
/// <summary> /// Emits code to prepare an evaluation stack for storing a reference into a variable. /// Supports operators chaining. Store is finished by calling <see cref="EmitAssign"/>. /// </summary> /// <param name="codeGenerator"></param> private PhpTypeCode EmitNodeWriteRef(CodeGenerator codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; // Case 3: a_[x]_[x] never reached Debug.Assert(chain.IsArrayItem == false); // Case 4,5 never reached // 4: a[x]->... // 5: ...->a[]->... Debug.Assert(chain.IsMember == false); // 1, 2, 6, 7 if (this.isMemberOf != null) { // 6, 7: ...->a[x]_[x]_ chain.Create(); chain.Begin(); // Store isMemberOf for lazy emit chain.SetObjectForLazyEmit(this); chain.IsArrayItem = true; chain.IsLastMember = false; chain.Lengthen(); // for own [] array.Emit(codeGenerator); indexTypeCode = codeGenerator.EmitArrayKey(chain, index); // Note that EmitAssign will finish the work (SetArrayItem or PhpArray.Add) } else { // 1, 2 Debug.Assert(this.isMemberOf == null); if (array is ItemUse || array is DirectStFldUse || array is IndirectStFldUse /* ??? */) { // 2: a[]_[]_ chain.Create(); chain.Begin(); chain.IsArrayItem = true; chain.IsLastMember = true; array.Emit(codeGenerator); indexTypeCode = codeGenerator.EmitArrayKey(chain, index); // Note that further work will be done by EmitAssign (SetArrayItem or PhpArray.Add) } // 1: a_[x]_ // Do nothing now, let the work be done in EmitAssign() // Note further work will be done by EmitAssign (either SetItem or SetItemRef); } return(PhpTypeCode.Unknown); }
/// <summary> /// Emit <see cref="PhpVariable.Copy"/> if needed. It means <see cref="Expression.Access"/> has to be <see cref="AccessType.Read"/> and <paramref name="returnType"/> has to be copiable. /// </summary> /// <param name="il">The <see cref="ILEmitter"/>.</param> /// <param name="returnType"><see cref="PhpTypeCode"/> of function call return value.</param> protected void EmitReturnValueCopy(ILEmitter/*!*/il, PhpTypeCode returnType) { Debug.Assert(il != null); // copy only if we are reading the return value && // only if return type is copiable: if (access != AccessType.None && // reading, not literals: PhpTypeCodeEnum.IsDeeplyCopied(returnType) && returnType != PhpTypeCode.PhpReference) // PhpSmartReference can be an issue if method returns an object field (but this is handled by binders) { il.LdcI4((int)CopyReason.ReturnedByCopy); il.Emit(OpCodes.Call, Methods.PhpVariable.Copy); } }
/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal override PhpTypeCode Emit(CodeGenerator /*!*/ codeGenerator) { Statistics.AST.AddNode("ItemUse"); PhpTypeCode result = PhpTypeCode.Invalid; switch (codeGenerator.SelectAccess(access)) { case AccessType.None: result = EmitNodeRead(codeGenerator, Operators.GetItemKinds.Get); codeGenerator.IL.Emit(OpCodes.Pop); break; case AccessType.Read: result = EmitNodeRead(codeGenerator, Operators.GetItemKinds.Get); break; case AccessType.Write: // prepares for write: result = EmitNodeWrite(codeGenerator); break; case AccessType.ReadRef: // if the selector is set to the ReadRef, the chain is emitted as if it was written // (chained nodes are marked as ReadAndWrite): if (codeGenerator.AccessSelector == AccessType.ReadRef) { codeGenerator.AccessSelector = AccessType.Write; } result = EmitNodeReadRef(codeGenerator); break; case AccessType.ReadUnknown: result = EmitNodeReadUnknown(codeGenerator); break; case AccessType.WriteRef: // prepares for write: result = EmitNodeWriteRef(codeGenerator); break; default: Debug.Fail(); break; } return(result); }
public void EmitConvertReturnValue(Type /*!*/ returnType, PhpTypeCode expectedTypeCode) { if (returnType == Types.Void) { il.Emit(OpCodes.Pop); } else { if (expectedTypeCode == PhpTypeCode.PhpReference) { // dereference il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); expectedTypeCode = PhpTypeCode.Object; } ClrOverloadBuilder.EmitConvertToClr( il, expectedTypeCode, returnType); } }
/// <summary> /// Emits load of an argument of a concatenation. /// </summary> private static PhpTypeCode EmitConcatExpressionLoad(CodeGenerator /*!*/ codeGenerator, Expression /*!*/ expression) { // tries to evaluate the expression: if (expression.HasValue()) { var value = expression.GetValue(); if (value is PhpBytes) { codeGenerator.IL.LoadLiteral(value); return(PhpTypeCode.PhpBytes); } else { // evaluated expression is converted to a string if necessary: codeGenerator.IL.Emit(OpCodes.Ldstr, Convert.ObjectToString(value)); return(PhpTypeCode.String); } } else { // emits non-evaluable expression: PhpTypeCode result = expression.Emit(codeGenerator); // the result should be converted to string: (so we know the type for the further analysis) if (result != PhpTypeCode.String && // string already result != PhpTypeCode.Object && // object can contain PhpBytes, should be converted just when we know we need string result != PhpTypeCode.PhpBytes // keep PhpBytes ) { codeGenerator.EmitBoxing(result); // in case of value-type codeGenerator.IL.Emit(OpCodes.Call, Methods.Convert.ObjectToString); result = PhpTypeCode.String; } return(result); } }
/// <summary> /// Emits code for loading the variable's value onto the evaluation stack. Supports operators chaining. /// </summary> /// <param name="codeGenerator"></param> private PhpTypeCode EmitNodeRead(CodeGenerator codeGenerator) { if (codeGenerator.ChainBuilder.IsMember) { // 1,4,5,6,9 if (this.isMemberOf != null) { // 1: ...->a->... codeGenerator.ChainBuilder.Lengthen(); return(EmitReadField(codeGenerator, false)); } if (codeGenerator.ChainBuilder.IsArrayItem && !codeGenerator.ChainBuilder.IsLastMember) { // 6: $b->a[3] return(codeGenerator.ChainBuilder.EmitGetProperty(this)); } // 4: a[][] // 5: $a->b->c->... // 9: $a->b return(this.EmitLoad(codeGenerator)); } // 2,3,7,8 if (this.isMemberOf != null) { // 2: $b->a // 8: b[]->a codeGenerator.ChainBuilder.Create(); codeGenerator.ChainBuilder.Begin(); PhpTypeCode result = EmitReadField(codeGenerator, false); codeGenerator.ChainBuilder.End(); return(result); } // 3: a[3] // 7: $a return(this.EmitLoad(codeGenerator)); }
internal void EmitSetItem(PhpTypeCode keyTypeCode, Expression keyExpr, bool reference) { MethodInfo method; switch (keyTypeCode) { case PhpTypeCode.Integer: method = (reference) ? Methods.Operators.SetItemRef.Int32 : Methods.Operators.SetItem.Int32; break; case PhpTypeCode.String: if (reference) { method = Methods.Operators.SetItemRef.String; } else { if (EmitExactStringKeyHash(keyTypeCode, keyExpr)) method = Methods.Operators.SetItemExact; else method = Methods.Operators.SetItem.String; } break; case PhpTypeCode.Object: method = (reference) ? Methods.Operators.SetItemRef.Object : Methods.Operators.SetItem.Object; break; case PhpTypeCode.Invalid: method = Methods.Operators.SetItem.Keyless; break; default: throw new ArgumentException(); } il.Emit(OpCodes.Call, method); }
internal void EmitSetArrayItem(PhpTypeCode keyTypeCode, Expression keyExpr, bool reference, bool ctor) { MethodInfo method; switch (keyTypeCode) { case PhpTypeCode.Integer: method = (reference) ? Methods.PhpArray.SetArrayItemRef_Int32 : Methods.PhpArray.SetArrayItem_Int32; break; case PhpTypeCode.String: if (reference) { method = Methods.PhpArray.SetArrayItemRef_String; } else { if (EmitExactStringKeyHash(keyTypeCode, keyExpr)) method = Methods.PhpArray.SetArrayItemExact_String; else method = Methods.PhpArray.SetArrayItem_String; } break; case PhpTypeCode.Object: method = reference ? Methods.PhpArray.SetArrayItemRef_Object : Methods.PhpArray.SetArrayItem_Object; break; case PhpTypeCode.Invalid: method = ctor ? Methods.PhpArray.AddToEnd_Object : Methods.PhpArray.SetArrayItem; break; default: throw new ArgumentException(); } il.Emit(method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, method); }
private bool EmitExactStringKeyHash(PhpTypeCode keyTypeCode, Expression keyExpr) { if (keyExpr != null && keyTypeCode == PhpTypeCode.String && keyExpr.HasValue()) { string skey = (string)keyExpr.GetValue(); IntStringKey array_key = Convert.StringToArrayKey(skey); if (array_key.IsString && skey == array_key.String) // skey was not converted to int { il.LdcI4(array_key.GetHashCode()); // == array_key.Integer == IntStringKey.StringKeyToArrayIndex(skey) // previously: skey.GetHashCode() return true; } } return false; }
/// <summary> /// Emits code that loads a specified parameter on the evaluation stack. /// </summary> /// <param name="paramInfo">The parameter to load.</param> /// <param name="requiredTypeCode">Specifies whether <see cref="PhpReference"/> /// (<see cref="PhpTypeCode.PhpReference"/>), <see cref="object"/> (<see cref="PhpTypeCode.Object"/>), /// or the most fitting of these two should be loaded.</param> public void EmitLoadClrParameter(ParameterInfo/*!*/ paramInfo, PhpTypeCode requiredTypeCode) { if (paramInfo.IsOut) il.Emit(OpCodes.Ldnull); else { il.Ldarg(paramInfo.Position + paramOffset); // dereference ref param Type param_type = paramInfo.ParameterType; if (param_type.IsByRef) { param_type = param_type.GetElementType(); il.Ldind(param_type); } // convert the parameter to PHP type PhpTypeCode type_code = ClrOverloadBuilder.EmitConvertToPhp( il, param_type/*, scriptContextPlace*/); il.EmitBoxing(type_code); } // check whether we have to create a PhpReference if (requiredTypeCode == PhpTypeCode.Object || (requiredTypeCode == PhpTypeCode.Unknown && !paramInfo.ParameterType.IsByRef)) return; if (paramInfo.ParameterType.IsByRef) { LocalBuilder ref_local = il.DeclareLocal(Types.PhpReference[0]); // remember the PhpReference in a local il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); il.Emit(OpCodes.Dup); il.Stloc(ref_local); referenceLocals[paramInfo.Position] = ref_local; } else { // no reference store-back is necessary il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); } }
/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal override PhpTypeCode Emit(CodeGenerator /*!*/ codeGenerator) { Statistics.AST.AddNode("FieldUse.Static"); ChainBuilder chain = codeGenerator.ChainBuilder; PhpTypeCode result = PhpTypeCode.Invalid; switch (codeGenerator.SelectAccess(access)) { case AccessType.Read: result = EmitRead(codeGenerator, false); if (chain.IsMember) { chain.Lengthen(); } break; case AccessType.ReadUnknown: result = EmitRead(codeGenerator, true); if (chain.IsMember) { chain.Lengthen(); } break; case AccessType.ReadRef: if (chain.IsMember) { chain.Lengthen(); result = EmitRead(codeGenerator, false); } else { result = EmitRead(codeGenerator, true); } break; case AccessType.Write: if (chain.IsMember) { result = EmitEnsure(codeGenerator, chain); chain.Lengthen(); } else { assignmentCallback = EmitWrite(codeGenerator, false); result = PhpTypeCode.Unknown; } break; case AccessType.WriteRef: if (chain.IsMember) { result = EmitEnsure(codeGenerator, chain); chain.Lengthen(); } else { assignmentCallback = EmitWrite(codeGenerator, true); result = PhpTypeCode.Unknown; } break; case AccessType.None: result = PhpTypeCode.Void; break; } return(result); }
private static void EmitPopValue(CodeGenerator/*!*/ codeGenerator, PhpTypeCode stackTypeCode) { // just pop the value that was meant to be written codeGenerator.IL.Emit(OpCodes.Pop); }
internal void EmitSetConversion(ILEmitter/*!*/ il, PhpTypeCode sourceTypeCode, Type/*!*/ targetType) { LocalBuilder strictness = il.GetTemporaryLocal(typeof(PHP.Core.ConvertToClr.ConversionStrictness)); if (!ClrOverloadBuilder.EmitConvertToClr(il, sourceTypeCode, targetType, strictness)) { Label label_ok = il.DefineLabel(); il.Ldloc(strictness); il.LdcI4((int)PHP.Core.ConvertToClr.ConversionStrictness.Failed); il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Brfalse, label_ok); il.Emit(OpCodes.Ldstr, Property.DeclaringType.FullName); il.Emit(OpCodes.Ldstr, Property.FullName); il.Emit(OpCodes.Call, Methods.PhpException.PropertyTypeMismatch); il.MarkLabel(label_ok, true); } il.ReturnTemporaryLocal(strictness); }
public void EmitLoad(ILEmitter /*!*/ il) { Debug.Assert(ReferenceEquals(il, codeGenerator.IL)); typeCode = expression.Emit(codeGenerator); }
/// <summary> /// Emits concatenation of a pair of expressions. /// </summary> /// <param name="codeGenerator">A code generator.</param> /// <param name="x">The first expression.</param> /// <param name="y">The second expression.</param> /// <returns>The resulting type code.</returns> internal static PhpTypeCode EmitConcat(CodeGenerator /*!*/ codeGenerator, Expression /*!*/ x, Expression /*!*/ y) { PhpTypeCode type_code_x = EmitConcatExpressionLoad(codeGenerator, x); PhpTypeCode type_code_y = EmitConcatExpressionLoad(codeGenerator, y); if (type_code_x == PhpTypeCode.String) { if (type_code_y == PhpTypeCode.String) { // string.string: codeGenerator.IL.Emit(OpCodes.Call, Methods.String_Concat_String_String); return(PhpTypeCode.String); } else if (type_code_y == PhpTypeCode.PhpBytes) { // check the return type: Debug.Assert(Methods.PhpBytes.Concat_Object_PhpBytes.ReturnType == typeof(PhpBytes)); // string.PhpBytes: codeGenerator.IL.Emit(OpCodes.Call, Methods.PhpBytes.Concat_Object_PhpBytes); return(PhpTypeCode.PhpBytes); } else { // string.object: codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.Concat.String_Object); return(PhpTypeCode.Object); } } else if (type_code_x == PhpTypeCode.PhpBytes) { if (type_code_y == PhpTypeCode.PhpBytes) { // check the return type: Debug.Assert(Methods.PhpBytes.Concat_PhpBytes_PhpBytes.ReturnType == typeof(PHP.Core.PhpBytes)); // PhpBytes.PhpBytes codeGenerator.IL.Emit(OpCodes.Call, Methods.PhpBytes.Concat_PhpBytes_PhpBytes); } else { // check the return type: Debug.Assert(Methods.PhpBytes.Concat_PhpBytes_Object.ReturnType == typeof(PHP.Core.PhpBytes)); // PhpBytes.object: codeGenerator.IL.Emit(OpCodes.Call, Methods.PhpBytes.Concat_PhpBytes_Object); } return(PhpTypeCode.PhpBytes); } else { if (type_code_y == PhpTypeCode.String) { // check the return type: //Debug.Assert(Methods.Operators.Concat.Object_String.ReturnType == typeof(object)); // object.string: codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.Concat.Object_String); return(PhpTypeCode.Object); } else if (type_code_y == PhpTypeCode.PhpBytes) { // check the return type: Debug.Assert(Methods.PhpBytes.Concat_Object_PhpBytes.ReturnType == typeof(PhpBytes)); // object.PhpBytes: codeGenerator.IL.Emit(OpCodes.Call, Methods.PhpBytes.Concat_Object_PhpBytes); return(PhpTypeCode.PhpBytes); } else { // object.object: codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.Concat.Object_Object); return(PhpTypeCode.Object); } } }
/// <summary> /// Converts a PHP value to the given CLR type (the caller passes a <paramref name="strictnessLocal"/> that will /// receive one of the <see cref="PHP.Core.ConvertToClr.ConversionStrictness"/> enumeration values that /// describe the conversion result (the Failed value indicates that conversion was not successful). /// </summary> /// <returns><B>True</B> if it the conversion will surely succeed.</returns> internal static bool EmitConvertToClr(ILEmitter/*!*/ il, PhpTypeCode typeCode, Type/*!*/ formalType, LocalBuilder/*!*/ strictnessLocal) { Debug.Assert(strictnessLocal.LocalType == typeof(ConversionStrictness)); // preprocess the value according to the PHP type code switch (typeCode) { case PhpTypeCode.PhpReference: { // dereference il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); typeCode = PhpTypeCode.Object; break; } case PhpTypeCode.ObjectAddress: { // dereference il.Emit(OpCodes.Ldind_Ref); typeCode = PhpTypeCode.Object; break; } case PhpTypeCode.LinqSource: case PhpTypeCode.PhpRuntimeChain: { Debug.Fail(); return true; } } // special treatment for generic parameters if (formalType.IsGenericParameter) { EmitConvertToClrGeneric(il, formalType, strictnessLocal); return false; } // convert CLR type return EmitConvertObjectToClr(il, typeCode, formalType, strictnessLocal); }
private void EmitConversionToAmbiguousVararg(int argIndex, Type/*!*/ formalParamType, LocalBuilder/*!*/ tmpStrictness, PhpTypeCode argLocalTypeCode, LocalBuilder/*!*/ argLocal, LocalBuilder[] formals) { Debug.Assert(formalParamType.IsArray); Type element_type = formalParamType.GetElementType(); Label success_label = il.DefineLabel(); // IF (overloadStrictness == ImplExactMatch) GOTO <success_label> il.Ldloc(tmpStrictness); il.LdcI4((int)ConversionStrictness.ImplExactMatch); il.Emit(OpCodes.Beq, success_label); // formal = new ELEMENT_TYPE[1] { CONVERT(stack, out strictness) }; EmitCreateParamsArray(element_type, formals[argIndex], 1); il.Ldloc(formals[argIndex]); il.LdcI4(0); // reload the argument il.Ldloc(argLocal); bool ct_ok = EmitConvertToClr(il, argLocalTypeCode, element_type, tmpStrictness); il.Stelem(element_type); il.MarkLabel(success_label, true); }
/// <summary> /// Emits IL instructions that sets the value to an <see cref="PHP.Core.PhpArray"/> item. /// </summary> /// <remarks>This method is used to set the item of an array having the index determined (not <B>null</B>). /// This method is called only in simple cases when operators chained are <b>not</b> involved. /// See short example of PHP code below. /// Expects that the reference to the object whose item should be set, the index and the value /// are loaded on the evaluation stack. Nothing is left on the evaluation stack. /// stack. /// </remarks> /// <example>$a[3] = "foo"; $a[] = "foo";</example> /// <example>$x[y] =& p; $x[] =& p;</example> public void EmitSetItem(PhpTypeCode keyTypeCode, Expression index, bool reference) { codeGenerator.EmitSetItem(keyTypeCode, index, reference); }
/// <summary> /// Emits assignment. /// </summary> /// <remarks> /// Pattern: a op= b /// /// PREPARE a (prepared) /// LOAD a (prepared,a) /// LOAD b (prepared,a,b) /// OP (prepared,result) /// *DUP (prepared,result,result) /// *STORE tmp (prepared,result) must be this stack here! /// STORE a () /// *LOAD tmp (result) /// /// * only if the resulting value needs to be propagated to the right /// /// Note: There is a possible improvement: some store operations (SetVariable) may return the value set /// which would replace DUP and second temp op. /// </remarks> public override PhpTypeCode Emit(AssignEx node, CodeGenerator codeGenerator) { Debug.Assert(access == AccessType.Read || access == AccessType.None || access == AccessType.ReadRef || access == AccessType.ReadUnknown); Statistics.AST.AddNode("Assign.Value"); AccessType old_selector = codeGenerator.AccessSelector; codeGenerator.ChainBuilder.Create(); PhpTypeCode result; if (node.Operation == Operations.AssignValue) { // // Access Type = ReadRef/ReadUnknown // --------------------------------- // // f(&$x) { } // // f($a = $b); // f($a = $b =& $c); // // Destination variable $a is prepared for reference write. // A new reference is created and its value set to a deep copy of the result of RHS ($b, $b =& $c). // RHS has Read access => it has been dereferenced. // // PREPARE a: codeGenerator.AccessSelector = AccessType.Write; node.lvalue.Emit(codeGenerator); codeGenerator.AccessSelector = AccessType.None; PhpTypeCode src_type_code = EmitSourceValRead((ValueAssignEx)node, codeGenerator); // RHS should have Read access => should be dereferenced Debug.Assert(src_type_code != PhpTypeCode.PhpReference); // LOAD BOX b codeGenerator.EmitBoxing(src_type_code); // makes a copy if necessary: if (PhpTypeCodeEnum.IsDeeplyCopied(src_type_code)) { codeGenerator.EmitVariableCopy(CopyReason.Assigned, ((ValueAssignEx)node).rvalue); } } else { // PREPARE a: codeGenerator.AccessSelector = AccessType.Write; node.LValue.Emit(codeGenerator); codeGenerator.AccessSelector = AccessType.None; // LOAD b,a (rvalue must be processed first, than +-*/ with lvalue, since lvalu can be changed by rvalue expression) //must be the second operand// EmitDestVarRead(codeGenerator); PhpTypeCode right_type = EmitSourceValRead((ValueAssignEx)node, codeGenerator); var rvalue_tmp = codeGenerator.IL.GetTemporaryLocal(PhpTypeCodeEnum.ToType(right_type), false); codeGenerator.IL.Emit(OpCodes.Stloc, rvalue_tmp); EmitDestVarRead(node, codeGenerator); codeGenerator.IL.Emit(OpCodes.Ldloc, rvalue_tmp); codeGenerator.IL.ReturnTemporaryLocal(rvalue_tmp); switch (node.Operation) { #region Arithmetic case Operations.AssignAdd: { switch (right_type) { case PhpTypeCode.Integer: result = codeGenerator.EmitMethodCall(Methods.Operators.Add.Object_Int32); break; case PhpTypeCode.Double: result = codeGenerator.EmitMethodCall(Methods.Operators.Add.Object_Double); break; default: codeGenerator.EmitBoxing(right_type); result = codeGenerator.EmitMethodCall(Methods.Operators.Add.Object_Object); break; } break; } case Operations.AssignSub: { switch (right_type) { case PhpTypeCode.Integer: result = codeGenerator.EmitMethodCall(Methods.Operators.Subtract.Object_Int); break; default: codeGenerator.EmitBoxing(right_type); result = codeGenerator.EmitMethodCall(Methods.Operators.Subtract.Object_Object); break; } break; } case Operations.AssignDiv: { switch (right_type) { case PhpTypeCode.Integer: result = codeGenerator.EmitMethodCall(Methods.Operators.Divide.Object_Int32); break; case PhpTypeCode.Double: result = codeGenerator.EmitMethodCall(Methods.Operators.Divide.Object_Double); break; default: codeGenerator.EmitBoxing(right_type); result = codeGenerator.EmitMethodCall(Methods.Operators.Divide.Object_Object); break; } break; } case Operations.AssignMul: { switch (right_type) { case PhpTypeCode.Integer: result = codeGenerator.EmitMethodCall(Methods.Operators.Multiply.Object_Int32); break; case PhpTypeCode.Double: result = codeGenerator.EmitMethodCall(Methods.Operators.Multiply.Object_Double); break; default: codeGenerator.EmitBoxing(right_type); result = codeGenerator.EmitMethodCall(Methods.Operators.Multiply.Object_Object); break; } break; } case Operations.AssignPow: codeGenerator.EmitBoxing(right_type); result = codeGenerator.EmitMethodCall(Methods.Operators.Pow.Object_Object); break; case Operations.AssignMod: if (right_type == PhpTypeCode.Integer) { result = codeGenerator.EmitMethodCall(Methods.Operators.Remainder.Object_Int32); } else { codeGenerator.EmitBoxing(right_type); result = codeGenerator.EmitMethodCall(Methods.Operators.Remainder.Object_Object); } break; #endregion #region Bitwise case Operations.AssignAnd: codeGenerator.EmitBoxing(right_type); codeGenerator.IL.Emit(OpCodes.Ldc_I4, (int)Operators.BitOp.And); result = codeGenerator.EmitMethodCall(Methods.Operators.BitOperation); break; case Operations.AssignOr: codeGenerator.EmitBoxing(right_type); codeGenerator.IL.Emit(OpCodes.Ldc_I4, (int)Operators.BitOp.Or); result = codeGenerator.EmitMethodCall(Methods.Operators.BitOperation); break; case Operations.AssignXor: codeGenerator.EmitBoxing(right_type); codeGenerator.IL.Emit(OpCodes.Ldc_I4, (int)Operators.BitOp.Xor); result = codeGenerator.EmitMethodCall(Methods.Operators.BitOperation); break; case Operations.AssignShiftLeft: codeGenerator.EmitBoxing(right_type); result = codeGenerator.EmitMethodCall(Methods.Operators.ShiftLeft); break; case Operations.AssignShiftRight: codeGenerator.EmitBoxing(right_type); result = codeGenerator.EmitMethodCall(Methods.Operators.ShiftRight); break; #endregion #region String case Operations.AssignAppend: { if (right_type == PhpTypeCode.String) { result = codeGenerator.EmitMethodCall(Methods.Operators.Append.Object_String); } else if (right_type == PhpTypeCode.PhpBytes) { result = codeGenerator.EmitMethodCall(Methods.PhpBytes.Append_Object_PhpBytes); } else { codeGenerator.EmitBoxing(right_type); result = codeGenerator.EmitMethodCall(Methods.Operators.Append.Object_Object); } break; } case Operations.AssignPrepend: { if (right_type == PhpTypeCode.String) { result = codeGenerator.EmitMethodCall(Methods.Operators.Prepend.Object_String); } else { codeGenerator.EmitBoxing(right_type); result = codeGenerator.EmitMethodCall(Methods.Operators.Prepend.Object_Object); } break; } #endregion default: throw new InvalidOperationException(); } codeGenerator.IL.EmitBoxing(result); } switch (access) { case AccessType.Read: { // DUP codeGenerator.IL.Emit(OpCodes.Dup); // STORE tmp codeGenerator.IL.Stloc(codeGenerator.IL.GetAssignmentLocal()); // STORE prepared, result codeGenerator.AccessSelector = AccessType.Write; result = VariableUseHelper.EmitAssign(node.LValue, codeGenerator); codeGenerator.AccessSelector = AccessType.None; Debug.Assert(result == PhpTypeCode.Void); // LOAD result codeGenerator.IL.Ldloc(codeGenerator.IL.GetAssignmentLocal()); result = PhpTypeCode.Object; break; } case AccessType.ReadRef: case AccessType.ReadUnknown: // STORE prepared,result codeGenerator.AccessSelector = AccessType.Write; result = VariableUseHelper.EmitAssign(node.LValue, codeGenerator); codeGenerator.AccessSelector = AccessType.None; Debug.Assert(result == PhpTypeCode.Void); // loads a reference on the LHS variable: codeGenerator.AccessSelector = access; codeGenerator.ChainBuilder.Create(); result = node.LValue.Emit(codeGenerator); codeGenerator.ChainBuilder.EndRef(); codeGenerator.AccessSelector = AccessType.None; break; case AccessType.None: // STORE a: codeGenerator.AccessSelector = AccessType.Write; result = VariableUseHelper.EmitAssign(node.LValue, codeGenerator); codeGenerator.AccessSelector = AccessType.None; Debug.Assert(result == PhpTypeCode.Void); break; default: Debug.Fail("Invalid access type."); result = PhpTypeCode.Invalid; break; } codeGenerator.ChainBuilder.End(); codeGenerator.AccessSelector = old_selector; return(result); }
private PhpNumber(long value) : this() { _typeCode = PhpTypeCode.Long; _long = value; }
public PrimitiveTypeRef(PhpTypeCode code) { _code = code; }
private PhpNumber(double value) : this() { _typeCode = PhpTypeCode.Double; _double = value; }
/// <summary> /// Emits code to prepare an evaluation stack for storing a value into a variable. /// Supports operators chaining. Store is finished by calling <see cref="EmitAssign"/>. /// </summary> /// <param name="codeGenerator"></param> private PhpTypeCode EmitNodeWrite(CodeGenerator codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; if (chain.IsArrayItem) { // 3: a_[x]_[v] Debug.Assert(this.isMemberOf == null); return chain.EmitEnsureItem(array, index, true); } // 1, 2, 4, 5, 6, 7 if (chain.IsMember) { // 4, 5 if (this.isMemberOf != null) { // 5: ...->a[]->... // Store isMemberOf for lazy emit chain.SetObjectForLazyEmit(this); chain.IsArrayItem = true; chain.IsLastMember = false; } else { // 4: a_[x]_->c->..., a[x]_[x]_->c->... chain.IsArrayItem = true; chain.IsLastMember = true; } PhpTypeCode result = chain.EmitEnsureItem(array, index, false); chain.IsArrayItem = false; return result; } // 1, 2, 6, 7 if (this.isMemberOf != null) { // 6, 7: ...->a[x]_[x]_ chain.Create(); chain.Begin(); // Store isMemberOf for lazy emit chain.SetObjectForLazyEmit(this); chain.IsArrayItem = true; chain.IsLastMember = false; chain.Lengthen(); // for own [] array.Emit(codeGenerator); indexTypeCode = codeGenerator.EmitArrayKey(chain, index); // Note that EmitAssign will finish the work (SetArrayItem or PhpArray.Add) return PhpTypeCode.Unknown; } // 1, 2 Debug.Assert(this.isMemberOf == null); if (array is ItemUse || array is DirectStFldUse || array is IndirectStFldUse /* ??? */) { // 2: a[]_[]_ chain.Create(); chain.Begin(); chain.IsArrayItem = true; chain.IsLastMember = true; array.Emit(codeGenerator); indexTypeCode = codeGenerator.EmitArrayKey(chain, index); // Note that further work will be done by EmitAssign (SetArrayItem or PhpArray.Add) return PhpTypeCode.Unknown; } // 1: a_[x]_ // Do nothing now, let the work be done in EmitAssign() return PhpTypeCode.Unknown; }
public void EmitLoad(ILEmitter/*!*/ il) { Debug.Assert(ReferenceEquals(il, codeGenerator.IL)); typeCode = expression.Emit(codeGenerator); }
private static void EmitCallSetObjectField(CodeGenerator/*!*/ codeGenerator, PhpTypeCode stackTypeCode) { // CALL Operators.SetObjectProperty(<STACK:instance>,<STACK:field name>,<STACK:field value>, <type desc>) codeGenerator.EmitLoadClassContext(); codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.SetObjectProperty); //always when function with void return argument is called it's necesarry to add nop instruction due to debugger if (codeGenerator.Context.Config.Compiler.Debug) { codeGenerator.IL.Emit(OpCodes.Nop); } }
/// <summary> /// Finishes the write operation starte by <see cref="Emit"/>. /// </summary> internal override PhpTypeCode EmitAssign(CodeGenerator/*!*/ codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; PhpTypeCode result; switch (access) { case AccessType.WriteAndReadRef: case AccessType.WriteAndReadUnknown: case AccessType.ReadAndWrite: case AccessType.ReadAndWriteAndReadRef: case AccessType.ReadAndWriteAndReadUnknown: case AccessType.Write: case AccessType.WriteRef: { bool reference = access == AccessType.WriteRef; // Note that some work was done in Emit() ! // In cases 3, 4, 5 EmitAssign is not called if (isMemberOf != null || (isMemberOf == null && (array is DirectStFldUse || array is IndirectStFldUse || array is ItemUse))) { // 2, 6, 7 chain.EmitSetArrayItem(indexTypeCode, index, reference); chain.End(); } else { // Note: The value which should be stored is already loaded on the evaluation stack. // Push the destination array and index and call the operator // 1: a_[x]_ Debug.Assert(array is SimpleVarUse); chain.IsArrayItem = true; chain.IsLastMember = true; indexTypeCode = codeGenerator.EmitArrayKey(chain, index); array.Emit(codeGenerator); chain.EmitSetItem(indexTypeCode, index, reference); // Store the changed variable into table of variables (do nothing in optimalized functions) ((SimpleVarUse)array).EmitLoadAddress_StoreBack(codeGenerator); } result = PhpTypeCode.Void; break; } case AccessType.None: // do nothing result = PhpTypeCode.Void; break; case AccessType.Read: // do nothing result = PhpTypeCode.Object; break; case AccessType.ReadRef: // Do nothing result = PhpTypeCode.PhpReference; break; default: Debug.Fail(); result = PhpTypeCode.Invalid; break; } return result; }
/// <summary> /// Emits code to prepare an evaluation stack for storing a value into a variable. /// Supports operators chaining. Store is finished by calling <see cref="EmitAssign"/>. /// </summary> /// <param name="codeGenerator"></param> private PhpTypeCode EmitNodeWrite(CodeGenerator codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; if (chain.IsArrayItem) { // 3: a_[x]_[v] Debug.Assert(this.isMemberOf == null); return(chain.EmitEnsureItem(array, index, true)); } // 1, 2, 4, 5, 6, 7 if (chain.IsMember) { // 4, 5 if (this.isMemberOf != null) { // 5: ...->a[]->... // Store isMemberOf for lazy emit chain.SetObjectForLazyEmit(this); chain.IsArrayItem = true; chain.IsLastMember = false; } else { // 4: a_[x]_->c->..., a[x]_[x]_->c->... chain.IsArrayItem = true; chain.IsLastMember = true; } PhpTypeCode result = chain.EmitEnsureItem(array, index, false); chain.IsArrayItem = false; return(result); } // 1, 2, 6, 7 if (this.isMemberOf != null) { // 6, 7: ...->a[x]_[x]_ chain.Create(); chain.Begin(); // Store isMemberOf for lazy emit chain.SetObjectForLazyEmit(this); chain.IsArrayItem = true; chain.IsLastMember = false; chain.Lengthen(); // for own [] array.Emit(codeGenerator); indexTypeCode = codeGenerator.EmitArrayKey(chain, index); // Note that EmitAssign will finish the work (SetArrayItem or PhpArray.Add) return(PhpTypeCode.Unknown); } // 1, 2 Debug.Assert(this.isMemberOf == null); if (array is ItemUse || array is DirectStFldUse || array is IndirectStFldUse /* ??? */) { // 2: a[]_[]_ chain.Create(); chain.Begin(); chain.IsArrayItem = true; chain.IsLastMember = true; array.Emit(codeGenerator); indexTypeCode = codeGenerator.EmitArrayKey(chain, index); // Note that further work will be done by EmitAssign (SetArrayItem or PhpArray.Add) return(PhpTypeCode.Unknown); } // 1: a_[x]_ // Do nothing now, let the work be done in EmitAssign() return(PhpTypeCode.Unknown); }
/// <summary> /// Emits code to prepare an evaluation stack for storing a reference into a variable. /// Supports operators chaining. Store is finished by calling <see cref="EmitAssign"/>. /// </summary> /// <param name="codeGenerator"></param> private PhpTypeCode EmitNodeWriteRef(CodeGenerator codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; // Case 3: a_[x]_[x] never reached Debug.Assert(chain.IsArrayItem == false); // Case 4,5 never reached // 4: a[x]->... // 5: ...->a[]->... Debug.Assert(chain.IsMember == false); // 1, 2, 6, 7 if (this.isMemberOf != null) { // 6, 7: ...->a[x]_[x]_ chain.Create(); chain.Begin(); // Store isMemberOf for lazy emit chain.SetObjectForLazyEmit(this); chain.IsArrayItem = true; chain.IsLastMember = false; chain.Lengthen(); // for own [] array.Emit(codeGenerator); indexTypeCode = codeGenerator.EmitArrayKey(chain, index); // Note that EmitAssign will finish the work (SetArrayItem or PhpArray.Add) } else { // 1, 2 Debug.Assert(this.isMemberOf == null); if (array is ItemUse || array is DirectStFldUse || array is IndirectStFldUse /* ??? */) { // 2: a[]_[]_ chain.Create(); chain.Begin(); chain.IsArrayItem = true; chain.IsLastMember = true; array.Emit(codeGenerator); indexTypeCode = codeGenerator.EmitArrayKey(chain, index); // Note that further work will be done by EmitAssign (SetArrayItem or PhpArray.Add) } // 1: a_[x]_ // Do nothing now, let the work be done in EmitAssign() // Note further work will be done by EmitAssign (either SetItem or SetItemRef); } return PhpTypeCode.Unknown; }
// Obsolete: // <summary> // A <see cref="Stack{T}"/> class that stores temporarily used variables. See Remarks for more information. // </summary> // <remarks>This stack stores locals that are used to obtain address of a variable stored in // a runtime variables table while calling methods from <see cref="PHP.Core.Operators"/> having // a <c>ref</c> argument. Those variables are not so short-live to be obtained by <see cref="GetTemporaryLocal"/>, // but can be reused within a defining scope under certain circumstances. // When <see cref="GetAddressStorageLocal"/> method is called, the temporary // local is either popped from the cache or a new local is defined if the cache is empty. // If the variable become useless, <see cref="ReturnAddressStorageLocal"/> method should // be called to push the variable back to cache. Once the variable is returned to cache it must not have // been used unless it is obtained again by <see cref="GetAddressStorageLocal"/> method. // The cache is created when the first local is returned. // </remarks> #endregion #region PHP Specific internal void EmitBoxing(PhpTypeCode type) { switch (type) { case PhpTypeCode.Integer: il.Emit(OpCodes.Box, typeof(Int32)); break; case PhpTypeCode.LongInteger: il.Emit(OpCodes.Box, typeof(Int64)); break; case PhpTypeCode.Double: il.Emit(OpCodes.Box, typeof(Double)); break; case PhpTypeCode.Boolean: il.Emit(OpCodes.Box, typeof(Boolean)); break; case PhpTypeCode.Void: il.Emit(OpCodes.Ldnull); break; } }
///// <summary> ///// Emits IL instructions for calling the best overload of <see cref="PHP.Core.ScriptContext.Echo"/> method. ///// </summary> ///// <param name="typecode"><see cref="PHP.Core.PhpTypeCode"/> of the parameter.</param> ///// <remarks>GetUserEntryPoint parameters are expected on the evaluation stack. Nothing is left on the evaluation stack.</remarks> //private void EmitEchoCall(PhpTypeCode typecode) //{ // switch (typecode) // { // case PhpTypeCode.Object: // il.Emit(OpCodes.Call, Methods.ScriptContext.Echo.Object); // break; // case PhpTypeCode.String: // il.Emit(OpCodes.Call, Methods.ScriptContext.Echo.String); // break; // case PhpTypeCode.PhpBytes: // il.Emit(OpCodes.Call, Methods.ScriptContext.Echo.PhpBytes); // break; // case PhpTypeCode.Integer: // il.Emit(OpCodes.Call, Methods.ScriptContext.Echo.Int); // break; // case PhpTypeCode.LongInteger: // il.Emit(OpCodes.Call, Methods.ScriptContext.Echo.LongInt); // break; // case PhpTypeCode.Double: // il.Emit(OpCodes.Call, Methods.ScriptContext.Echo.Double); // break; // case PhpTypeCode.Boolean: // il.Emit(OpCodes.Call, Methods.ScriptContext.Echo.Bool); // break; // default: // il.Emit(OpCodes.Call, Methods.ScriptContext.Echo.Object); // break; // } //} /// <summary> /// Emits IL instructions for calling the best overload of <see cref="PHP.Core.ScriptContext.Echo"/> static method. /// </summary> /// <param name="typecode"><see cref="PHP.Core.PhpTypeCode"/> of the parameter.</param> /// <remarks>Nothing is left on the evaluation stack. Emitted method call expects two parameters on the evaluation stack: (value, ScriptContext).</remarks> private void EmitEchoStaticCall(PhpTypeCode typecode) { switch (typecode) { case PhpTypeCode.Object: il.Emit(OpCodes.Call, Methods.ScriptContext.EchoStatic.Object); break; case PhpTypeCode.String: il.Emit(OpCodes.Call, Methods.ScriptContext.EchoStatic.String); break; case PhpTypeCode.PhpBytes: il.Emit(OpCodes.Call, Methods.ScriptContext.EchoStatic.PhpBytes); break; case PhpTypeCode.Integer: il.Emit(OpCodes.Call, Methods.ScriptContext.EchoStatic.Int); break; case PhpTypeCode.LongInteger: il.Emit(OpCodes.Call, Methods.ScriptContext.EchoStatic.LongInt); break; case PhpTypeCode.Double: il.Emit(OpCodes.Call, Methods.ScriptContext.EchoStatic.Double); break; case PhpTypeCode.Boolean: il.Emit(OpCodes.Call, Methods.ScriptContext.EchoStatic.Bool); break; default: il.Emit(OpCodes.Call, Methods.ScriptContext.EchoStatic.Object); break; } }
public void EmitConvertReturnValue(Type/*!*/ returnType, PhpTypeCode expectedTypeCode) { if (returnType == Types.Void) il.Emit(OpCodes.Pop); else { if (expectedTypeCode == PhpTypeCode.PhpReference) { // dereference il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); expectedTypeCode = PhpTypeCode.Object; } ClrOverloadBuilder.EmitConvertToClr( il, expectedTypeCode, returnType); } }
/// <summary> /// Converts a PHP value to the given CLR type (the caller is not interested in the success of the conversion). /// </summary> public static void EmitConvertToClr(ILEmitter/*!*/ il, PhpTypeCode typeCode, Type/*!*/ formalType) { EmitConvertToClr(il, typeCode, formalType, il.GetTemporaryLocal(typeof(ConversionStrictness), true)); }
/// <summary> /// Emits IL instructions to process the <B>echo</B> and <B>print</B> commands. /// </summary> /// <param name="expressions">List of expressions to be echoed. They will be evaluated first. The list cannot be null and it must contain at least one element.</param> public void EmitEcho(Expression[]/*!*/expressions) { Debug.Assert(expressions != null); Debug.Assert(expressions.Length > 0); // known types of resulting values PhpTypeCode[] types = new PhpTypeCode[expressions.Length]; // construct the array with values // to preserve the proper order of evaluation and output il.LdcI4(expressions.Length); il.Emit(OpCodes.Newarr, typeof(object)); for (int i = 0; i < expressions.Length; ++i) { // array[<i>] = <expressions[i]>; il.Emit(OpCodes.Dup); il.LdcI4(i); EmitBoxing(types[i] = expressions[i].Emit(this)); il.Emit(OpCodes.Stelem_Ref); } // echo the values for (int i = 0; i < expressions.Length; ++i) { il.Emit(OpCodes.Dup); // array il.LdcI4(i); // <i> il.Emit(OpCodes.Ldelem_Ref); // object array[<i>] il.EmitUnboxingForArg(types[i]); // UnBox value type, if value-type was boxed here, prepared for method call argument // convert object to string or PhpBytes to hold the right type on the stack (valid IL) if (types[i] == PhpTypeCode.PhpBytes) il.Emit(OpCodes.Castclass, Types.PhpBytes[0]); else if (types[i] == PhpTypeCode.String) il.Emit(OpCodes.Castclass, Types.String[0]); EmitLoadScriptContext(); // CALL ScriptContext.Echo(<obj>, <ScriptContext>) EmitEchoStaticCall(types[i]); } il.Emit(OpCodes.Pop);// remove the array from the stack }
/// <summary> /// Converts object to CLR type /// </summary> private static bool EmitConvertObjectToClr(ILEmitter il, PhpTypeCode typeCode, Type formalType, LocalBuilder strictnessLocal) { MethodInfo convert_method = null; switch (Type.GetTypeCode(formalType)) { case TypeCode.Boolean: if (typeCode != PhpTypeCode.Boolean) convert_method = Methods.ConvertToClr.TryObjectToBoolean; break; case TypeCode.Int32: if (typeCode != PhpTypeCode.Integer) convert_method = Methods.ConvertToClr.TryObjectToInt32; break; case TypeCode.Int64: if (typeCode != PhpTypeCode.LongInteger) convert_method = Methods.ConvertToClr.TryObjectToInt64; break; case TypeCode.Double: if (typeCode != PhpTypeCode.Double) convert_method = Methods.ConvertToClr.TryObjectToDouble; break; case TypeCode.String: if (typeCode != PhpTypeCode.String) convert_method = Methods.ConvertToClr.TryObjectToString; break; case TypeCode.SByte: convert_method = Methods.ConvertToClr.TryObjectToInt8; break; case TypeCode.Int16: convert_method = Methods.ConvertToClr.TryObjectToInt16; break; case TypeCode.Byte: convert_method = Methods.ConvertToClr.TryObjectToUInt8; break; case TypeCode.UInt16: convert_method = Methods.ConvertToClr.TryObjectToUInt16; break; case TypeCode.UInt32: convert_method = Methods.ConvertToClr.TryObjectToUInt32; break; case TypeCode.UInt64: convert_method = Methods.ConvertToClr.TryObjectToUInt64; break; case TypeCode.Single: convert_method = Methods.ConvertToClr.TryObjectToSingle; break; case TypeCode.Decimal: convert_method = Methods.ConvertToClr.TryObjectToDecimal; break; case TypeCode.Char: convert_method = Methods.ConvertToClr.TryObjectToChar; break; case TypeCode.DateTime: convert_method = Methods.ConvertToClr.TryObjectToDateTime; break; case TypeCode.DBNull: convert_method = Methods.ConvertToClr.TryObjectToDBNull; break; case TypeCode.Object: { if (formalType.IsValueType) { if (formalType.IsGenericType && NullableType == formalType.GetGenericTypeDefinition()) { // This is an ugly corner case (using generic TryObjectToStruct wouldn't work, because // for nullables .IsValueType returns true, but it doesn't match "T : struct" constraint)! // We have to try converting object to Nullable<T> first and then to T // (which requires a new call to 'EmitConvertObjectToClr') Type nullableArg = formalType.GetGenericArguments()[0]; Type nullableType = NullableType.MakeGenericType(nullableArg); LocalBuilder tmpVar = il.DeclareLocal(typeof(object)); // This succeeds only for exact match il.Emit(OpCodes.Call, Methods.ConvertToClr.UnwrapNullable); il.Emit(OpCodes.Dup); il.Stloc(tmpVar); // <stack_0> = tmpVar = UnwrapNullable(...) // if (<stack_0> != null) Label lblNull = il.DefineLabel(), lblDone = il.DefineLabel(); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Beq, lblNull); // { // Convert tmpVar to T and wrap it into Nullable<T> il.Ldloc(tmpVar); bool ret = EmitConvertObjectToClr(il, typeCode, nullableArg, strictnessLocal); // TODO: use reflection cache? il.Emit(OpCodes.Newobj, nullableType.GetConstructors()[0]); il.Emit(OpCodes.Br, lblDone); // } else /* == null */ { il.MarkLabel(lblNull); // return (T?)null; LocalBuilder tmpNull = il.DeclareLocal(nullableType); il.Ldloca(tmpNull); il.Emit(OpCodes.Initobj, nullableType); il.Ldloc(tmpNull); // } il.MarkLabel(lblDone); return ret; } else convert_method = Methods.ConvertToClr.TryObjectToStruct.MakeGenericMethod(formalType); } else { if (formalType.IsArray) convert_method = Methods.ConvertToClr.TryObjectToArray.MakeGenericMethod(formalType.GetElementType()); else if (typeof(Delegate).IsAssignableFrom(formalType)) convert_method = Methods.ConvertToClr.TryObjectToDelegate.MakeGenericMethod(formalType); else convert_method = Methods.ConvertToClr.TryObjectToClass.MakeGenericMethod(formalType); } break; } default: Debug.Fail(); return true; } if (convert_method != null) { il.Ldloca(strictnessLocal); il.Emit(OpCodes.Call, convert_method); return false; } else return true; }
internal void EmitName(string fullName, Expression nameExpr, bool createChain, PhpTypeCode dstType) { Debug.Assert(fullName != null ^ nameExpr != null); if (fullName != null) { il.Emit(OpCodes.Ldstr, fullName); } else { if (createChain) ChainBuilder.Create(); EmitConversion(nameExpr, dstType); if (createChain) ChainBuilder.End(); } }
public ExpressionPlace(CodeGenerator /*!*/ codeGenerator, Expression /*!*/ expression) { this.codeGenerator = codeGenerator; this.expression = expression; this.typeCode = PhpTypeCode.Invalid; }
/// <summary> /// Emits instructions to conform a required access type. /// </summary> /// <param name="callExpression">Expression emitting the call.</param> /// <param name="loadAddress">Whether to load an address of the return value.</param> /// <param name="result">The type code of a top item of the evaluation stack.</param> public void EmitReturnValueHandling(Expression/*!*/ callExpression, bool loadAddress, ref PhpTypeCode result) { Debug.Assert(callExpression != null); if (loadAddress) { if (result == PhpTypeCode.PhpReference) { // LOADADDR STACK.Value; il.Emit(OpCodes.Ldflda, Fields.PhpReference_Value); } else { if (result == PhpTypeCode.Void) il.Emit(OpCodes.Ldnull); // local = STACK; // LOADADDR local; LocalBuilder local = il.GetTemporaryLocal(PhpTypeCodeEnum.ToType(result), true); il.Stloc(local); il.Ldloca(local); } result = PhpTypeCode.ObjectAddress; return; } switch (callExpression.GetAccess()) { case AccessType.None: // return value is discarded: if (result != PhpTypeCode.Void) { il.Emit(OpCodes.Pop); result = PhpTypeCode.Void; } break; case AccessType.ReadUnknown: case AccessType.ReadRef: if (result != PhpTypeCode.PhpReference) { // return value is "boxed" to PhpReference: if (result != PhpTypeCode.Void) { EmitBoxing(result); EmitVariableCopy(CopyReason.ReturnedByCopy, callExpression); il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); } else { il.Emit(OpCodes.Newobj, Constructors.PhpReference_Void); } result = PhpTypeCode.PhpReference; } break; case AccessType.Read: if (result == PhpTypeCode.PhpReference) { // return value is dereferenced: il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); result = PhpTypeCode.Object; } else if (result == PhpTypeCode.Void) { // null value is loaded as a result: il.Emit(OpCodes.Ldnull); result = PhpTypeCode.Object; } break; default: Debug.Fail(null); break; } }
public ExpressionPlace(CodeGenerator/*!*/ codeGenerator, Expression/*!*/ expression) { this.codeGenerator = codeGenerator; this.expression = expression; this.typeCode = PhpTypeCode.Invalid; }
/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal override PhpTypeCode Emit(CodeGenerator codeGenerator) { Statistics.AST.AddNode("IncDecEx"); Debug.Assert(access == AccessType.Read || access == AccessType.None); AccessType old_selector = codeGenerator.AccessSelector; PhpTypeCode returned_typecode = PhpTypeCode.Void; codeGenerator.AccessSelector = AccessType.Write; codeGenerator.ChainBuilder.Create(); variable.Emit(codeGenerator); codeGenerator.AccessSelector = AccessType.Read; codeGenerator.ChainBuilder.Create(); variable.Emit(codeGenerator); codeGenerator.ChainBuilder.End(); LocalBuilder old_value = null; if (access == AccessType.Read && post) { old_value = codeGenerator.IL.DeclareLocal(Types.Object[0]); // Save variable's value for later use codeGenerator.IL.Emit(OpCodes.Dup); codeGenerator.IL.Stloc(old_value); } if (this.inc) { // Increment codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.Increment); } else { // Decrement codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.Decrement); } codeGenerator.AccessSelector = AccessType.Write; if (access == AccessType.Read) { if (post) { variable.EmitAssign(codeGenerator); // Load original value (as was before operation) codeGenerator.IL.Ldloc(old_value); } else { old_value = codeGenerator.IL.DeclareLocal(Types.Object[0]); // pre-incrementation // Load variable's value after operation codeGenerator.IL.Emit(OpCodes.Dup); codeGenerator.IL.Stloc(old_value); variable.EmitAssign(codeGenerator); codeGenerator.IL.Ldloc(old_value); } returned_typecode = PhpTypeCode.Object; } else { variable.EmitAssign(codeGenerator); } codeGenerator.AccessSelector = old_selector; codeGenerator.ChainBuilder.End(); return(returned_typecode); }
/// <summary> /// Emits IL instructions that convert the top of evaluation stack to a specified type. /// </summary> /// <remarks> /// Emits a call to one of <see cref="PHP.Core.Convert"/> methods to do the conversion. /// The method result is left on the evaluation stack. /// </remarks> internal void EmitConversion(Expression/*!*/ expression, PhpTypeCode dst) { // expression is evaluable: if (expression.HasValue()) { switch (dst) { case PhpTypeCode.String: il.Emit(OpCodes.Ldstr, PHP.Core.Convert.ObjectToString(expression.GetValue())); break; case PhpTypeCode.Boolean: il.LdcI4(PHP.Core.Convert.ObjectToBoolean(expression.GetValue()) ? 1 : 0); break; case PhpTypeCode.Integer: il.LdcI4(PHP.Core.Convert.ObjectToInteger(expression.GetValue())); break; case PhpTypeCode.Double: il.Emit(OpCodes.Ldc_R8, PHP.Core.Convert.ObjectToDouble(expression.GetValue())); break; case PhpTypeCode.Object: il.LoadLiteral(expression.GetValue()); break; default: Debug.Fail("Conversion not implemented."); break; } } else { // emits the expression: PhpTypeCode src = expression.Emit(this); // emits no conversion if types are the same: if (src == dst) return; // emits boxing if needed (conversion methods takes object): EmitBoxing(src); switch (dst) { case PhpTypeCode.String: il.Emit(OpCodes.Call, Methods.Convert.ObjectToString); break; case PhpTypeCode.Boolean: il.Emit(OpCodes.Call, Methods.Convert.ObjectToBoolean); break; case PhpTypeCode.Integer: il.Emit(OpCodes.Call, Methods.Convert.ObjectToBoolean); break; case PhpTypeCode.Double: il.Emit(OpCodes.Call, Methods.Convert.ObjectToDouble); break; case PhpTypeCode.Object: // nop // break; default: Debug.Fail("Conversion is not implemented."); break; } } }
private static void EmitCallSetObjectField(CodeGenerator /*!*/ codeGenerator, PhpTypeCode stackTypeCode) { // CALL Operators.SetObjectProperty(<STACK:instance>,<STACK:field name>,<STACK:field value>, <type desc>) codeGenerator.EmitLoadClassContext(); codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.SetObjectProperty); //always when function with void return argument is called it's necesarry to add nop instruction due to debugger if (codeGenerator.Context.Config.Compiler.Debug) { codeGenerator.IL.Emit(OpCodes.Nop); } }
/// <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); }
internal void EmitReferenceDereference(ref PhpTypeCode typeCode, bool wantRef) { if (wantRef) { // make reference: if (typeCode != PhpTypeCode.PhpReference) { EmitBoxing(typeCode); il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); } typeCode = PhpTypeCode.PhpReference; } else { // dereference: if (typeCode == PhpTypeCode.PhpReference) { il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); typeCode = PhpTypeCode.Object; } } }
private static void EmitPopValue(CodeGenerator /*!*/ codeGenerator, PhpTypeCode stackTypeCode) { // just pop the value that was meant to be written codeGenerator.IL.Emit(OpCodes.Pop); }
/// <summary> /// Emits IL instructions that box a literal value into its object representation. /// </summary> /// <param name="type"><see cref="PhpTypeCode"/> of the top item placed on evaluation stack.</param> /// <returns>The type code of an item on the top of evaluatuion stack after call.</returns> /// <remarks> /// The <see cref="PHP.Core.AST.Literal"/> to be boxed is expected on the evaluation stack. /// The boxed value is left on the evaluation stack. /// </remarks> public void EmitBoxing(PhpTypeCode type) { il.EmitBoxing(type); }
/// <summary> /// UnBox object containing value-type. /// </summary> /// <param name="type">Type of object to UnBox.</param> /// <remarks>Extracts the value contained within obj (of type O), it is equivalent to unbox followed by ldobj.</remarks> internal void EmitUnboxingForArg(PhpTypeCode type) { switch (type) { case PhpTypeCode.Integer: il.Emit(OpCodes.Unbox_Any, typeof(Int32)); break; case PhpTypeCode.LongInteger: il.Emit(OpCodes.Unbox_Any, typeof(Int64)); break; case PhpTypeCode.Double: il.Emit(OpCodes.Unbox_Any, typeof(Double)); break; case PhpTypeCode.Boolean: il.Emit(OpCodes.Unbox_Any, typeof(Boolean)); break; case PhpTypeCode.Void: il.Emit(OpCodes.Pop); break; } }
/// <summary> /// Ensures the object on top of the evaluation stack is writable, /// and so not shared by more PHP variables. /// </summary> internal void EmitEnsureWritable(PhpTypeCode typeCode) { switch (typeCode) { case PhpTypeCode.Boolean: case PhpTypeCode.Double: case PhpTypeCode.Integer: case PhpTypeCode.PhpCallable: case PhpTypeCode.PhpResource: case PhpTypeCode.PhpReference: case PhpTypeCode.String: case PhpTypeCode.Void: case PhpTypeCode.LongInteger: // these types cannot be lazy copied for sure break; default: // Operators.EnsureObjectIsWritable( <TOP> ) this.IL.Emit(OpCodes.Dup); this.EmitBoxing(typeCode); this.IL.Emit(OpCodes.Call, new Action<object>(Operators.EnsureObjectIsWritable).Method); break; } }