internal void EmitLoadArgsOnEvalStack(CallSignature/*!*/node, CodeGenerator/*!*/ codeGenerator, PhpRoutine/*!*/ routine) { ILEmitter il = codeGenerator.IL; int mandatory_count = (routine.Signature != null) ? routine.Signature.MandatoryParamCount : 0; int formal_count = (routine.Signature != null) ? routine.Signature.ParamCount : 0; int actual_count = node.Parameters.Count; PhpTypeCode param_type; // loads all actual parameters which are not superfluous: for (int i = 0; i < Math.Min(actual_count, formal_count); i++) { var p = node.Parameters[i]; codeGenerator.EmitBoxing(param_type = p.NodeCompiler<ActualParamCompiler>().Emit(p, codeGenerator)); // Actual param emitter should emit "boxing" to a reference if its access type is ReadRef. // That's why no operation is needed here and references should match. Debug.Assert((routine.Signature == null || routine.Signature.IsAlias(i)) == (param_type == PhpTypeCode.PhpReference)); } // loads missing mandatory arguments: for (int i = actual_count; i < mandatory_count; i++) { // CALL PhpException.MissingArgument(<i+1>,<name>); il.LdcI4(i + 1); il.Emit(OpCodes.Ldstr, routine.FullName); codeGenerator.EmitPhpException(Methods.PhpException.MissingArgument); // LOAD null; if (routine.Signature.IsAlias(i)) il.Emit(OpCodes.Newobj, Constructors.PhpReference_Void); else il.Emit(OpCodes.Ldnull); } // loads missing optional arguments: for (int i = Math.Max(mandatory_count, actual_count); i < formal_count; i++) { // LOAD Arg.Default; il.Emit(OpCodes.Ldsfld, Fields.Arg_Default); } }
/// <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; }
internal void EmitLoadTypeArgsOnEvalStack(CallSignature/*!*/node, CodeGenerator/*!*/ codeGenerator, PhpRoutine/*!*/ routine) { ILEmitter il = codeGenerator.IL; int mandatory_count = (routine.Signature != null) ? routine.Signature.MandatoryGenericParamCount : 0; int formal_count = (routine.Signature != null) ? routine.Signature.GenericParamCount : 0; int actual_count = node.GenericParams.Count; // loads all actual parameters which are not superfluous: for (int i = 0; i < Math.Min(actual_count, formal_count); i++) node.GenericParams[i].EmitLoadTypeDesc(codeGenerator, ResolveTypeFlags.UseAutoload | ResolveTypeFlags.ThrowErrors); // loads missing mandatory arguments: for (int i = actual_count; i < mandatory_count; i++) { // CALL PhpException.MissingTypeArgument(<i+1>,<name>); il.LdcI4(i + 1); il.Emit(OpCodes.Ldstr, routine.FullName); codeGenerator.EmitPhpException(Methods.PhpException.MissingTypeArgument); // LOAD DTypeDesc.ObjectTypeDesc; il.Emit(OpCodes.Ldsfld, Fields.DTypeDesc.ObjectTypeDesc); } // loads missing optional arguments: for (int i = Math.Max(mandatory_count, actual_count); i < formal_count; i++) { // LOAD Arg.DefaultType; il.Emit(OpCodes.Ldsfld, Fields.Arg_DefaultType); } }
/// <summary> /// Emits type hint test on the argument if specified. /// </summary> internal void EmitTypeHintTest(CodeGenerator/*!*/ codeGenerator) { int real_index = routine.FirstPhpParameterIndex + index; // not type hint specified: if (typeHint == null) return; Debug.Assert(resolvedTypeHint != null); ILEmitter il = codeGenerator.IL; Label endif_label = il.DefineLabel(); // IF (DEREF(ARG[argIdx]) is not of hint type) THEN il.Ldarg(real_index); if (PassedByRef) il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); resolvedTypeHint.EmitInstanceOf(codeGenerator, null); il.Emit(OpCodes.Brtrue, endif_label); // add a branch allowing null values if the argument is optional with null default value (since PHP 5.1.0); if (initValue != null && initValue.HasValue && initValue.Value == null) { // IF (DEREF(ARG[argIdx]) != null) THEN il.Ldarg(real_index); if (PassedByRef) il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); il.Emit(OpCodes.Brfalse, endif_label); } // CALL PhpException.InvalidArgumentType(<param_name>, <class_name>); il.Emit(OpCodes.Ldstr, name.ToString()); il.Emit(OpCodes.Ldstr, resolvedTypeHint.FullName); codeGenerator.EmitPhpException(Methods.PhpException.InvalidArgumentType); // END IF; // END IF; il.MarkLabel(endif_label); }
/// <summary> /// Emits error reporting call when "this" variable is used out of object context. /// </summary> private static void EmitThisUsedOutOfObjectThrow(CodeGenerator/*!*/ codeGenerator, bool wantRef) { codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); if (wantRef) codeGenerator.IL.Emit(OpCodes.Newobj, Constructors.PhpReference_Void); else codeGenerator.IL.Emit(OpCodes.Ldnull); }
/// <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); } }
internal override void EmitStoreRefAssign(CodeGenerator codeGenerator) { ILEmitter il = codeGenerator.IL; if (varName.IsThisVariableName) { // emit error throwing code il.Emit(OpCodes.Pop); codeGenerator.EmitPhpException(Methods.PhpException.CannotReassignThis); } else if (codeGenerator.VariableIsAutoGlobal(varName)) { // Check if the variable is auto-global codeGenerator.EmitAutoGlobalStoreRefAssign(varName); } else if (codeGenerator.OptimizedLocals) { VariablesTable.Entry entry = codeGenerator.CurrentVariablesTable[varName]; entry.Variable.EmitStore(il); } else { // call instance IDictionary.set_Item(object, object) // OBSOLETE: il.Emit(OpCodes.Callvirt, Methods.IDictionary_SetItem); il.Emit(OpCodes.Call, Methods.Operators.SetVariableRef); } }
internal override void EmitStoreAssign(CodeGenerator codeGenerator) { ILEmitter il = codeGenerator.IL; if (varName.IsThisVariableName) { // emit error throwing code il.Emit(OpCodes.Pop); codeGenerator.EmitPhpException(Methods.PhpException.CannotReassignThis); } else if (codeGenerator.VariableIsAutoGlobal(varName)) { // Check if the variable is auto-global codeGenerator.EmitAutoGlobalStoreAssign(); } else if (codeGenerator.OptimizedLocals) { // Template: // "WRITE($x,value);" // **** // if specified variable is of type PhpReference // ldloc local // **** // Otherwise do nothing VariablesTable.Entry entry = codeGenerator.CurrentVariablesTable[varName]; if (entry.IsPhpReference) il.Emit(OpCodes.Stfld, Fields.PhpReference_Value); else entry.Variable.EmitStore(il); } else { // CALL Operators.SetVariable(STACK:table,STACK:name,STACK:value); il.Emit(OpCodes.Call, Methods.Operators.SetVariable); } }
internal override void Emit(CodeGenerator/*!*/ codeGenerator) { Statistics.AST.AddNode("Class.MethodDecl"); base.Emit(codeGenerator); // emit attributes on return value, generic and regular parameters: signature.Emit(codeGenerator); typeSignature.Emit(codeGenerator); if(method.IsDllImport) { //TODO: Support for DllImport Debug.Assert(false, "DllImport - not supported"); } else if(!method.IsAbstract) { // returns immediately if the method is abstract: codeGenerator.EnterMethodDeclaration(method); // emits the arg-full overload: codeGenerator.EmitArgfullOverloadBody(method, body, entireDeclarationPosition, declarationBodyPosition); // restores original code generator settings: codeGenerator.LeaveMethodDeclaration(); } else { // static abstract method is non-abstract in CLR => needs to have a body: if (method.IsStatic) { ILEmitter il = new ILEmitter(method.ArgFullInfo); il.Emit(OpCodes.Ldstr, method.DeclaringType.FullName); il.Emit(OpCodes.Ldstr, method.FullName); codeGenerator.EmitPhpException(il, Methods.PhpException.AbstractMethodCalled); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ret); } } // emits stubs for overridden/implemented methods and export stubs: codeGenerator.EmitOverrideAndExportStubs(method); }