X-Git-Url: http://git.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/Expression.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/Expression.java index 69bd341..0b6c568 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/Expression.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/Expression.java @@ -18,379 +18,393 @@ import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope; import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding; public class Expression extends Statement { - - //some expression may not be used - from a java semantic point - //of view only - as statements. Other may. In order to avoid the creation - //of wrappers around expression in order to tune them as expression - //Expression is a subclass of Statement. See the message isValidJavaStatement() + + // some expression may not be used - from a java semantic point + // of view only - as statements. Other may. In order to avoid the creation + // of wrappers around expression in order to tune them as expression + // Expression is a subclass of Statement. See the message + // isValidJavaStatement() public int implicitConversion; + public TypeBinding resolvedType; - + public Constant constant; public Expression() { super(); } - public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { + public FlowInfo analyseCode(BlockScope currentScope, + FlowContext flowContext, FlowInfo flowInfo) { return flowInfo; } - public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) { + public FlowInfo analyseCode(BlockScope currentScope, + FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) { return analyseCode(currentScope, flowContext, flowInfo); } /** * Constant usable for bytecode pattern optimizations, but cannot be inlined - * since it is not strictly equivalent to the definition of constant expressions. - * In particular, some side-effects may be required to occur (only the end value - * is known). - * Constant is known to be of boolean type - */ + * since it is not strictly equivalent to the definition of constant + * expressions. In particular, some side-effects may be required to occur + * (only the end value is known). Constant is known to be of boolean type + */ public Constant optimizedBooleanConstant() { return this.constant; } - public static final boolean isConstantValueRepresentable( - Constant constant, - int constantTypeID, - int targetTypeID) { + public static final boolean isConstantValueRepresentable(Constant constant, + int constantTypeID, int targetTypeID) { - //true if there is no loss of precision while casting. + // true if there is no loss of precision while casting. // constantTypeID == constant.typeID if (targetTypeID == constantTypeID) return true; switch (targetTypeID) { - case T_char : - switch (constantTypeID) { - case T_char : - return true; - case T_double : - return constant.doubleValue() == constant.charValue(); - case T_float : - return constant.floatValue() == constant.charValue(); - case T_int : - return constant.intValue() == constant.charValue(); - case T_short : - return constant.shortValue() == constant.charValue(); - case T_byte : - return constant.byteValue() == constant.charValue(); - case T_long : - return constant.longValue() == constant.charValue(); - default : - return false;//boolean - } - - case T_float : - switch (constantTypeID) { - case T_char : - return constant.charValue() == constant.floatValue(); - case T_double : - return constant.doubleValue() == constant.floatValue(); - case T_float : - return true; - case T_int : - return constant.intValue() == constant.floatValue(); - case T_short : - return constant.shortValue() == constant.floatValue(); - case T_byte : - return constant.byteValue() == constant.floatValue(); - case T_long : - return constant.longValue() == constant.floatValue(); - default : - return false;//boolean - } - - case T_double : - switch (constantTypeID) { - case T_char : - return constant.charValue() == constant.doubleValue(); - case T_double : - return true; - case T_float : - return constant.floatValue() == constant.doubleValue(); - case T_int : - return constant.intValue() == constant.doubleValue(); - case T_short : - return constant.shortValue() == constant.doubleValue(); - case T_byte : - return constant.byteValue() == constant.doubleValue(); - case T_long : - return constant.longValue() == constant.doubleValue(); - default : - return false; //boolean - } - - case T_byte : - switch (constantTypeID) { - case T_char : - return constant.charValue() == constant.byteValue(); - case T_double : - return constant.doubleValue() == constant.byteValue(); - case T_float : - return constant.floatValue() == constant.byteValue(); - case T_int : - return constant.intValue() == constant.byteValue(); - case T_short : - return constant.shortValue() == constant.byteValue(); - case T_byte : - return true; - case T_long : - return constant.longValue() == constant.byteValue(); - default : - return false; //boolean - } - - case T_short : - switch (constantTypeID) { - case T_char : - return constant.charValue() == constant.shortValue(); - case T_double : - return constant.doubleValue() == constant.shortValue(); - case T_float : - return constant.floatValue() == constant.shortValue(); - case T_int : - return constant.intValue() == constant.shortValue(); - case T_short : - return true; - case T_byte : - return constant.byteValue() == constant.shortValue(); - case T_long : - return constant.longValue() == constant.shortValue(); - default : - return false; //boolean - } - - case T_int : - switch (constantTypeID) { - case T_char : - return constant.charValue() == constant.intValue(); - case T_double : - return constant.doubleValue() == constant.intValue(); - case T_float : - return constant.floatValue() == constant.intValue(); - case T_int : - return true; - case T_short : - return constant.shortValue() == constant.intValue(); - case T_byte : - return constant.byteValue() == constant.intValue(); - case T_long : - return constant.longValue() == constant.intValue(); - default : - return false; //boolean - } - - case T_long : - switch (constantTypeID) { - case T_char : - return constant.charValue() == constant.longValue(); - case T_double : - return constant.doubleValue() == constant.longValue(); - case T_float : - return constant.floatValue() == constant.longValue(); - case T_int : - return constant.intValue() == constant.longValue(); - case T_short : - return constant.shortValue() == constant.longValue(); - case T_byte : - return constant.byteValue() == constant.longValue(); - case T_long : - return true; - default : - return false; //boolean - } - - default : - return false; //boolean - } + case T_char: + switch (constantTypeID) { + case T_char: + return true; + case T_double: + return constant.doubleValue() == constant.charValue(); + case T_float: + return constant.floatValue() == constant.charValue(); + case T_int: + return constant.intValue() == constant.charValue(); + case T_short: + return constant.shortValue() == constant.charValue(); + case T_byte: + return constant.byteValue() == constant.charValue(); + case T_long: + return constant.longValue() == constant.charValue(); + default: + return false;// boolean + } + + case T_float: + switch (constantTypeID) { + case T_char: + return constant.charValue() == constant.floatValue(); + case T_double: + return constant.doubleValue() == constant.floatValue(); + case T_float: + return true; + case T_int: + return constant.intValue() == constant.floatValue(); + case T_short: + return constant.shortValue() == constant.floatValue(); + case T_byte: + return constant.byteValue() == constant.floatValue(); + case T_long: + return constant.longValue() == constant.floatValue(); + default: + return false;// boolean + } + + case T_double: + switch (constantTypeID) { + case T_char: + return constant.charValue() == constant.doubleValue(); + case T_double: + return true; + case T_float: + return constant.floatValue() == constant.doubleValue(); + case T_int: + return constant.intValue() == constant.doubleValue(); + case T_short: + return constant.shortValue() == constant.doubleValue(); + case T_byte: + return constant.byteValue() == constant.doubleValue(); + case T_long: + return constant.longValue() == constant.doubleValue(); + default: + return false; // boolean + } + + case T_byte: + switch (constantTypeID) { + case T_char: + return constant.charValue() == constant.byteValue(); + case T_double: + return constant.doubleValue() == constant.byteValue(); + case T_float: + return constant.floatValue() == constant.byteValue(); + case T_int: + return constant.intValue() == constant.byteValue(); + case T_short: + return constant.shortValue() == constant.byteValue(); + case T_byte: + return true; + case T_long: + return constant.longValue() == constant.byteValue(); + default: + return false; // boolean + } + + case T_short: + switch (constantTypeID) { + case T_char: + return constant.charValue() == constant.shortValue(); + case T_double: + return constant.doubleValue() == constant.shortValue(); + case T_float: + return constant.floatValue() == constant.shortValue(); + case T_int: + return constant.intValue() == constant.shortValue(); + case T_short: + return true; + case T_byte: + return constant.byteValue() == constant.shortValue(); + case T_long: + return constant.longValue() == constant.shortValue(); + default: + return false; // boolean + } + + case T_int: + switch (constantTypeID) { + case T_char: + return constant.charValue() == constant.intValue(); + case T_double: + return constant.doubleValue() == constant.intValue(); + case T_float: + return constant.floatValue() == constant.intValue(); + case T_int: + return true; + case T_short: + return constant.shortValue() == constant.intValue(); + case T_byte: + return constant.byteValue() == constant.intValue(); + case T_long: + return constant.longValue() == constant.intValue(); + default: + return false; // boolean + } + + case T_long: + switch (constantTypeID) { + case T_char: + return constant.charValue() == constant.longValue(); + case T_double: + return constant.doubleValue() == constant.longValue(); + case T_float: + return constant.floatValue() == constant.longValue(); + case T_int: + return constant.intValue() == constant.longValue(); + case T_short: + return constant.shortValue() == constant.longValue(); + case T_byte: + return constant.byteValue() == constant.longValue(); + case T_long: + return true; + default: + return false; // boolean + } + + default: + return false; // boolean + } } /** * Expression statements are plain expressions, however they generate like * normal expressions with no value required. - * - * @param currentScope net.sourceforge.phpdt.internal.compiler.lookup.BlockScope - * @param codeStream net.sourceforge.phpdt.internal.compiler.codegen.CodeStream + * + * @param currentScope + * net.sourceforge.phpdt.internal.compiler.lookup.BlockScope + * @param codeStream + * net.sourceforge.phpdt.internal.compiler.codegen.CodeStream */ -// public void generateCode(BlockScope currentScope, CodeStream codeStream) { -// -// if ((bits & IsReachableMASK) == 0) { -// return; -// } -// generateCode(currentScope, codeStream, false); -// } - + // public void generateCode(BlockScope currentScope, CodeStream codeStream) + // { + // + // if ((bits & IsReachableMASK) == 0) { + // return; + // } + // generateCode(currentScope, codeStream, false); + // } /** - * Every expression is responsible for generating its implicit conversion when necessary. - * - * @param currentScope net.sourceforge.phpdt.internal.compiler.lookup.BlockScope - * @param codeStream net.sourceforge.phpdt.internal.compiler.codegen.CodeStream - * @param valueRequired boolean + * Every expression is responsible for generating its implicit conversion + * when necessary. + * + * @param currentScope + * net.sourceforge.phpdt.internal.compiler.lookup.BlockScope + * @param codeStream + * net.sourceforge.phpdt.internal.compiler.codegen.CodeStream + * @param valueRequired + * boolean */ -// public void generateCode( -// BlockScope currentScope, -// CodeStream codeStream, -// boolean valueRequired) { -// -// if (constant != NotAConstant) { -// // generate a constant expression -// int pc = codeStream.position; -// codeStream.generateConstant(constant, implicitConversion); -// codeStream.recordPositionsFrom(pc, this.sourceStart); -// } else { -// // actual non-constant code generation -// throw new ShouldNotImplement(ProjectPrefUtil.bind("ast.missingCode")); //$NON-NLS-1$ -// } -// } - + // public void generateCode( + // BlockScope currentScope, + // CodeStream codeStream, + // boolean valueRequired) { + // + // if (constant != NotAConstant) { + // // generate a constant expression + // int pc = codeStream.position; + // codeStream.generateConstant(constant, implicitConversion); + // codeStream.recordPositionsFrom(pc, this.sourceStart); + // } else { + // // actual non-constant code generation + // throw new ShouldNotImplement(ProjectPrefUtil.bind("ast.missingCode")); + // //$NON-NLS-1$ + // } + // } /** * Default generation of a boolean value */ -// public void generateOptimizedBoolean( -// BlockScope currentScope, -// CodeStream codeStream, -// Label trueLabel, -// Label falseLabel, -// boolean valueRequired) { -// -// // a label valued to nil means: by default we fall through the case... -// // both nil means we leave the value on the stack -// -// if ((constant != Constant.NotAConstant) && (constant.typeID() == T_boolean)) { -// int pc = codeStream.position; -// if (constant.booleanValue() == true) { -// // constant == true -// if (valueRequired) { -// if (falseLabel == null) { -// // implicit falling through the FALSE case -// if (trueLabel != null) { -// codeStream.goto_(trueLabel); -// } -// } -// } -// } else { -// if (valueRequired) { -// if (falseLabel != null) { -// // implicit falling through the TRUE case -// if (trueLabel == null) { -// codeStream.goto_(falseLabel); -// } -// } -// } -// } -// codeStream.recordPositionsFrom(pc, this.sourceStart); -// return; -// } -// generateCode(currentScope, codeStream, valueRequired); -// // branching -// int position = codeStream.position; -// if (valueRequired) { -// if (falseLabel == null) { -// if (trueLabel != null) { -// // Implicit falling through the FALSE case -// codeStream.ifne(trueLabel); -// } -// } else { -// if (trueLabel == null) { -// // Implicit falling through the TRUE case -// codeStream.ifeq(falseLabel); -// } else { -// // No implicit fall through TRUE/FALSE --> should never occur -// } -// } -// } -// // reposition the endPC -// codeStream.updateLastRecordedEndPC(position); -// } -// -// /* Optimized (java) code generation for string concatenations that involve StringBuffer -// * creation: going through this path means that there is no need for a new StringBuffer -// * creation, further operands should rather be only appended to the current one. -// * By default: no optimization. -// */ -// public void generateOptimizedStringBuffer( -// BlockScope blockScope, -// net.sourceforge.phpdt.internal.compiler.codegen.CodeStream codeStream, -// int typeID) { -// -// generateCode(blockScope, codeStream, true); -// codeStream.invokeStringBufferAppendForType(typeID); -// } - - /* Optimized (java) code generation for string concatenations that involve StringBuffer - * creation: going through this path means that there is no need for a new StringBuffer - * creation, further operands should rather be only appended to the current one. + // public void generateOptimizedBoolean( + // BlockScope currentScope, + // CodeStream codeStream, + // Label trueLabel, + // Label falseLabel, + // boolean valueRequired) { + // + // // a label valued to nil means: by default we fall through the case... + // // both nil means we leave the value on the stack + // + // if ((constant != Constant.NotAConstant) && (constant.typeID() == + // T_boolean)) { + // int pc = codeStream.position; + // if (constant.booleanValue() == true) { + // // constant == true + // if (valueRequired) { + // if (falseLabel == null) { + // // implicit falling through the FALSE case + // if (trueLabel != null) { + // codeStream.goto_(trueLabel); + // } + // } + // } + // } else { + // if (valueRequired) { + // if (falseLabel != null) { + // // implicit falling through the TRUE case + // if (trueLabel == null) { + // codeStream.goto_(falseLabel); + // } + // } + // } + // } + // codeStream.recordPositionsFrom(pc, this.sourceStart); + // return; + // } + // generateCode(currentScope, codeStream, valueRequired); + // // branching + // int position = codeStream.position; + // if (valueRequired) { + // if (falseLabel == null) { + // if (trueLabel != null) { + // // Implicit falling through the FALSE case + // codeStream.ifne(trueLabel); + // } + // } else { + // if (trueLabel == null) { + // // Implicit falling through the TRUE case + // codeStream.ifeq(falseLabel); + // } else { + // // No implicit fall through TRUE/FALSE --> should never occur + // } + // } + // } + // // reposition the endPC + // codeStream.updateLastRecordedEndPC(position); + // } + // + // /* Optimized (java) code generation for string concatenations that + // involve StringBuffer + // * creation: going through this path means that there is no need for a new + // StringBuffer + // * creation, further operands should rather be only appended to the + // current one. + // * By default: no optimization. + // */ + // public void generateOptimizedStringBuffer( + // BlockScope blockScope, + // net.sourceforge.phpdt.internal.compiler.codegen.CodeStream codeStream, + // int typeID) { + // + // generateCode(blockScope, codeStream, true); + // codeStream.invokeStringBufferAppendForType(typeID); + // } + /* + * Optimized (java) code generation for string concatenations that involve + * StringBuffer creation: going through this path means that there is no + * need for a new StringBuffer creation, further operands should rather be + * only appended to the current one. */ -// public void generateOptimizedStringBufferCreation( -// BlockScope blockScope, -// CodeStream codeStream, -// int typeID) { -// -// // Optimization only for integers and strings -// if (typeID == T_Object) { -// // in the case the runtime value of valueOf(Object) returns null, we have to use append(Object) instead of directly valueOf(Object) -// // append(Object) returns append(valueOf(Object)), which means that the null case is handled by append(String). -// codeStream.newStringBuffer(); -// codeStream.dup(); -// codeStream.invokeStringBufferDefaultConstructor(); -// generateCode(blockScope, codeStream, true); -// codeStream.invokeStringBufferAppendForType(T_Object); -// return; -// } -// codeStream.newStringBuffer(); -// codeStream.dup(); -// if (typeID == T_String || typeID == T_null) { -// if (constant != NotAConstant) { -// codeStream.ldc(constant.stringValue()); -// } else { -// generateCode(blockScope, codeStream, true); -// codeStream.invokeStringValueOf(T_Object); -// } -// } else { -// generateCode(blockScope, codeStream, true); -// codeStream.invokeStringValueOf(typeID); -// } -// codeStream.invokeStringBufferStringConstructor(); -// } - - // Base types need that the widening is explicitly done by the compiler using some bytecode like i2f - public void implicitWidening( - TypeBinding runtimeTimeType, - TypeBinding compileTimeType) { + // public void generateOptimizedStringBufferCreation( + // BlockScope blockScope, + // CodeStream codeStream, + // int typeID) { + // + // // Optimization only for integers and strings + // if (typeID == T_Object) { + // // in the case the runtime value of valueOf(Object) returns null, we have + // to use append(Object) instead of directly valueOf(Object) + // // append(Object) returns append(valueOf(Object)), which means that the + // null case is handled by append(String). + // codeStream.newStringBuffer(); + // codeStream.dup(); + // codeStream.invokeStringBufferDefaultConstructor(); + // generateCode(blockScope, codeStream, true); + // codeStream.invokeStringBufferAppendForType(T_Object); + // return; + // } + // codeStream.newStringBuffer(); + // codeStream.dup(); + // if (typeID == T_String || typeID == T_null) { + // if (constant != NotAConstant) { + // codeStream.ldc(constant.stringValue()); + // } else { + // generateCode(blockScope, codeStream, true); + // codeStream.invokeStringValueOf(T_Object); + // } + // } else { + // generateCode(blockScope, codeStream, true); + // codeStream.invokeStringValueOf(typeID); + // } + // codeStream.invokeStringBufferStringConstructor(); + // } + // Base types need that the widening is explicitly done by the compiler + // using some bytecode like i2f + public void implicitWidening(TypeBinding runtimeTimeType, + TypeBinding compileTimeType) { if (runtimeTimeType == null || compileTimeType == null) return; -// if (compileTimeType.id == T_null) { -// // this case is possible only for constant null -// // The type of runtime is a reference type -// // The code gen use the constant id thus any value -// // for the runtime id (akak the <<4) could be used. -// // T_Object is used as some general T_reference -// implicitConversion = (T_Object << 4) + T_null; -// return; -// } + // if (compileTimeType.id == T_null) { + // // this case is possible only for constant null + // // The type of runtime is a reference type + // // The code gen use the constant id thus any value + // // for the runtime id (akak the <<4) could be used. + // // T_Object is used as some general T_reference + // implicitConversion = (T_Object << 4) + T_null; + // return; + // } switch (runtimeTimeType.id) { - case T_byte : - case T_short : - case T_char : - implicitConversion = (T_int << 4) + compileTimeType.id; - break; - case T_String : - case T_float : - case T_boolean : - case T_double : - case T_int : //implicitConversion may result in i2i which will result in NO code gen - case T_long : - implicitConversion = (runtimeTimeType.id << 4) + compileTimeType.id; - break; - default : //nothing on regular object ref + case T_byte: + case T_short: + case T_char: + implicitConversion = (T_int << 4) + compileTimeType.id; + break; + case T_String: + case T_float: + case T_boolean: + case T_double: + case T_int: // implicitConversion may result in i2i which will result in + // NO code gen + case T_long: + implicitConversion = (runtimeTimeType.id << 4) + compileTimeType.id; + break; + default: // nothing on regular object ref } } @@ -399,25 +413,26 @@ public class Expression extends Statement { return false; } - //Return true if the conversion is done AUTOMATICALLY by the vm - //while the javaVM is an int based-machine, thus for example pushing - //a byte onto the stack , will automatically creates a int on the stack - //(this request some work d be done by the VM on signed numbers) + // Return true if the conversion is done AUTOMATICALLY by the vm + // while the javaVM is an int based-machine, thus for example pushing + // a byte onto the stack , will automatically creates a int on the stack + // (this request some work d be done by the VM on signed numbers) public boolean isConstantValueOfTypeAssignableToType( - TypeBinding constantType, - TypeBinding targetType) { + TypeBinding constantType, TypeBinding targetType) { if (constant == Constant.NotAConstant) return false; if (constantType == targetType) return true; if (constantType.isBaseType() && targetType.isBaseType()) { - //No free assignment conversion from anything but to integral ones. - if ((constantType == IntBinding - || BaseTypeBinding.isWidening(T_int, constantType.id)) - && (BaseTypeBinding.isNarrowing(targetType.id, T_int))) { - //use current explicit conversion in order to get some new value to compare with current one - return isConstantValueRepresentable(constant, constantType.id, targetType.id); + // No free assignment conversion from anything but to integral ones. + if ((constantType == IntBinding || BaseTypeBinding.isWidening( + T_int, constantType.id)) + && (BaseTypeBinding.isNarrowing(targetType.id, T_int))) { + // use current explicit conversion in order to get some new + // value to compare with current one + return isConstantValueRepresentable(constant, constantType.id, + targetType.id); } } return false; @@ -426,19 +441,21 @@ public class Expression extends Statement { public boolean isTypeReference() { return false; } + public StringBuffer print(int indent, StringBuffer output) { printIndent(indent, output); return printExpression(indent, output); } public StringBuffer printExpression(int indent, StringBuffer output) { - output.append(super.toString(0)); - return output; + output.append(super.toString(0)); + return output; } - + public StringBuffer printStatement(int indent, StringBuffer output) { return print(indent, output).append(";"); //$NON-NLS-1$ } + public void resolve(BlockScope scope) { // drops the returning expression's type whatever the type is. @@ -452,16 +469,18 @@ public class Expression extends Statement { return null; } - public TypeBinding resolveTypeExpecting( - BlockScope scope, - TypeBinding expectedType) { + public TypeBinding resolveTypeExpecting(BlockScope scope, + TypeBinding expectedType) { TypeBinding expressionType = this.resolveType(scope); - if (expressionType == null) return null; - if (expressionType == expectedType) return expressionType; - + if (expressionType == null) + return null; + if (expressionType == expectedType) + return expressionType; + if (!expressionType.isCompatibleWith(expectedType)) { - scope.problemReporter().typeMismatchError(expressionType, expectedType, this); + scope.problemReporter().typeMismatchError(expressionType, + expectedType, this); return null; } return expressionType; @@ -469,37 +488,39 @@ public class Expression extends Statement { public String toString(int tab) { - //Subclass re-define toStringExpression + // Subclass re-define toStringExpression String s = tabString(tab); if (constant != null) - //before TC has runned + // before TC has runned if (constant != NotAConstant) - //after the TC has runned + // after the TC has runned s += " /*cst:" + constant.toString() + "*/ "; //$NON-NLS-1$ //$NON-NLS-2$ return s + toStringExpression(tab); } - //Subclass re-define toStringExpression - //This method is abstract and should never be called - //but we provide some code that is running.....just in case - //of developpement time (while every thing is not built) + // Subclass re-define toStringExpression + // This method is abstract and should never be called + // but we provide some code that is running.....just in case + // of developpement time (while every thing is not built) public String toStringExpression() { return super.toString(0); } public String toStringExpression(int tab) { - // default is regular toString expression (qualified allocation expressions redifine this method) + // default is regular toString expression (qualified allocation + // expressions redifine this method) return this.toStringExpression(); } public Expression toTypeReference() { - //by default undefined + // by default undefined - //this method is meanly used by the parser in order to transform - //an expression that is used as a type reference in a cast .... - //--appreciate the fact that castExpression and ExpressionWithParenthesis - //--starts with the same pattern..... + // this method is meanly used by the parser in order to transform + // an expression that is used as a type reference in a cast .... + // --appreciate the fact that castExpression and + // ExpressionWithParenthesis + // --starts with the same pattern..... return this; }