internal override PhpTypeCode Emit(CodeGenerator codeGenerator) { Debug.Assert(access == AccessType.Read || access == AccessType.None); Statistics.AST.AddNode("Class.Concat." + expressions.Count); PhpTypeCode result; // // For low numbers call specialized methods switch (expressions.Count) { case 1: result = expressions[0].Emit(codeGenerator); if (result != PhpTypeCode.PhpBytes && result != PhpTypeCode.String) { var lbl = codeGenerator.IL.DefineLabel(); codeGenerator.EmitBoxing(result); codeGenerator.IL.Emit(OpCodes.Dup); codeGenerator.IL.Emit(OpCodes.Isinst,typeof(PhpBytes)); // IF (STACK) codeGenerator.IL.Emit(OpCodes.Brtrue_S,lbl); if (true) { codeGenerator.IL.Emit(OpCodes.Call, Methods.Convert.ObjectToString); } // ELSE codeGenerator.IL.MarkLabel(lbl, true); //END IF result = PhpTypeCode.Object; } break; case 2: result = EmitConcat(codeGenerator, expressions[0], expressions[1]); break; default: codeGenerator.EmitObjectArrayPopulation(expressions); codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.Concat.ObjectArray); result = PhpTypeCode.Object; // string, PhpBytes break; } switch (access) { case AccessType.Read: // do nothing break; case AccessType.None: // pop result from stack codeGenerator.IL.Emit(OpCodes.Pop); result = PhpTypeCode.Void; break; } return result; }