MemberDefinition f1 = stClsDef.matchMethod(env, sourceClass, idValueOf, argType1); asm.add(where, opc_invokestatic, f1); } // FIX FOR 4030173 // If the argument was null, then value is "null", but if the // argument was not null, 'toString' was called and could have // returned null. We call 'valueOf' again to make sure that // the result is a non-null string. See JLS 15.17.1.1. The // approach taken here minimizes code size -- open code would // be faster. The 'toString' method for an array class cannot // be overridden, thus we know that it will never return null. if (!type.inMask(TM_ARRAY|TM_NULL)) { Type argType2[] = {Type.tString}; MemberDefinition f2 = stClsDef.matchMethod(env, sourceClass, idValueOf, argType2); asm.add(where, opc_invokestatic, f2); } } else { // Primitive type Type argType[] = {type}; MemberDefinition f = stClsDef.matchMethod(env, sourceClass, idValueOf, argType); asm.add(where, opc_invokestatic, f); } } /** * Convert this expression to a string and append it to the string * buffer on the top of the stack. * If the needBuffer argument is true, the string buffer needs to be * created, initialized, and pushed on the stack, first. */ void codeAppend(Environment env, Context ctx, Assembler asm, ClassDeclaration sbClass, boolean needBuffer) throws ClassNotFound, AmbiguousMember { ClassDefinition sourceClass = ctx.field.getClassDefinition(); ClassDefinition sbClsDef = sbClass.getClassDefinition(env); MemberDefinition f; if (needBuffer) { // need to create the string buffer asm.add(where, opc_new, sbClass); // create the class asm.add(where, opc_dup); if (equals("")) { // make an empty string buffer f = sbClsDef.matchMethod(env, sourceClass, idInit); } else { // optimize by initializing the buffer with the string codeValue(env, ctx, asm); ensureString(env, ctx, asm); Type argType[] = {Type.tString}; f = sbClsDef.matchMethod(env, sourceClass, idInit, argType); } asm.add(where, opc_invokespecial, f); } else { // append this item to the string buffer codeValue(env, ctx, asm); // FIX FOR 4071548 // 'StringBuffer.append' converts its argument as if by // 'valueOf', treating character arrays specially. This // violates JLS 15.17.1.1, which requires that concatenation // convert non-primitive arguments using 'toString'. We force // the treatment of all reference types as type 'Object', thus // invoking an overloading of 'append' that has the required // semantics. Type argType[] = { (type.inMask(TM_REFERENCE) && type != Type.tString) ? Type.tObject : type }; f = sbClsDef.matchMethod(env, sourceClass, idAppend, argType); asm.add(where, opc_invokevirtual, f); } } /** * Code */ void codeDup(Environment env, Context ctx, Assembler asm, int items, int depth) { switch (items) { case 0: return; case 1: switch (depth) { case 0: asm.add(where, opc_dup); return; case 1: asm.add(where, opc_dup_x1); return; case 2: asm.add(where, opc_dup_x2); return; } break; case 2: switch (depth) { case 0: asm.add(where, opc_dup2); return; case 1: asm.add(where, opc_dup2_x1); return; case 2: asm.add(where, opc_dup2_x2); return; } break; } throw new CompilerError("can't dup: " + items + ", " + depth); } void codeConversion(Environment env, Context ctx, Assembler asm, Type f, Type t) { int from = f.getTypeCode(); int to = t.getTypeCode(); switch (to) { case TC_BOOLEAN: if (from != TC_BOOLEAN) { break; } return; case TC_BYTE: if (from != TC_BYTE) { codeConversion(env, ctx, asm, f, Type.tInt); asm.add(where, opc_i2b); } return; case TC_CHAR: if (from != TC_CHAR) { codeConversion(env, ctx, asm, f, Type.tInt); asm.add(where, opc_i2c); } return; case TC_SHORT: if (from != TC_SHORT) { codeConversion(env, ctx, asm, f, Type.tInt); asm.add(where, opc_i2s); } return; case TC_INT: switch (from) { case TC_BYTE: case TC_CHAR: case TC_SHORT: case TC_INT: return; case TC_LONG: asm.add(where, opc_l2i); return; case TC_FLOAT: asm.add(where, opc_f2i); return; case TC_DOUBLE: asm.add(where, opc_d2i); return; } break; case TC_LONG: switch (from) { case TC_BYTE: case TC_CHAR: case TC_SHORT: case TC_INT: asm.add(where, opc_i2l); return; case TC_LONG: return; case TC_FLOAT: asm.add(where, opc_f2l); return; case TC_DOUBLE: asm.add(where, opc_d2l); return; } break; case TC_FLOAT: switch (from) { case TC_BYTE: case TC_CHAR: case TC_SHORT: case TC_INT: asm.add(where, opc_i2f); return; case TC_LONG: asm.add(where, opc_l2f); return; case TC_FLOAT: return; case TC_DOUBLE: asm.add(where, opc_d2f); return; } break; case TC_DOUBLE: switch (from) { case TC_BYTE: case TC_CHAR: case TC_SHORT: case TC_INT: asm.add(where, opc_i2d); return; case TC_LONG: asm.add(where, opc_l2d); return; case TC_FLOAT: asm.add(where, opc_f2d); return; case TC_DOUBLE: return; } break; case TC_CLASS: switch (from) { case TC_NULL: return; case TC_CLASS: case TC_ARRAY: try { if (!env.implicitCast(f, t)) { asm.add(where, opc_checkcast, env.getClassDeclaration(t)); } } catch (ClassNotFound e) { throw new CompilerError(e); } return; } break; case TC_ARRAY: switch (from) { case TC_NULL: return; case TC_CLASS: case TC_ARRAY: try { if (!env.implicitCast(f, t)) { asm.add(where, opc_checkcast, t); } return; } catch (ClassNotFound e) { throw new CompilerError(e); } } break; } throw new CompilerError("codeConversion: " + from + ", " + to); } /** * Check if the first thing is a constructor invocation */ public Expression firstConstructor() { return null; } /** * Create a copy of the expression for method inlining */ public Expression copyInline(Context ctx) { return (Expression)clone(); } /** * Print */ public void print(PrintStream out) { out.print(opNames[op]); }}Lines 501–775 of 775. Previous lines
MemberDefinition f1 = stClsDef.matchMethod(env, sourceClass, idValueOf, argType1); asm.add(where, opc_invokestatic, f1); } // FIX FOR 4030173 // If the argument was null, then value is "null", but if the // argument was not null, 'toString' was called and could have // returned null. We call 'valueOf' again to make sure that // the result is a non-null string. See JLS 15.17.1.1. The // approach taken here minimizes code size -- open code would // be faster. The 'toString' method for an array class cannot // be overridden, thus we know that it will never return null. if (!type.inMask(TM_ARRAY|TM_NULL)) { Type argType2[] = {Type.tString}; MemberDefinition f2 = stClsDef.matchMethod(env, sourceClass, idValueOf, argType2); asm.add(where, opc_invokestatic, f2); } } else { // Primitive type Type argType[] = {type}; MemberDefinition f = stClsDef.matchMethod(env, sourceClass, idValueOf, argType); asm.add(where, opc_invokestatic, f); } } /** * Convert this expression to a string and append it to the string * buffer on the top of the stack. * If the needBuffer argument is true, the string buffer needs to be * created, initialized, and pushed on the stack, first. */ void codeAppend(Environment env, Context ctx, Assembler asm, ClassDeclaration sbClass, boolean needBuffer) throws ClassNotFound, AmbiguousMember { ClassDefinition sourceClass = ctx.field.getClassDefinition(); ClassDefinition sbClsDef = sbClass.getClassDefinition(env); MemberDefinition f; if (needBuffer) { // need to create the string buffer asm.add(where, opc_new, sbClass); // create the class asm.add(where, opc_dup); if (equals("")) { // make an empty string buffer f = sbClsDef.matchMethod(env, sourceClass, idInit); } else { // optimize by initializing the buffer with the string codeValue(env, ctx, asm); ensureString(env, ctx, asm); Type argType[] = {Type.tString}; f = sbClsDef.matchMethod(env, sourceClass, idInit, argType); } asm.add(where, opc_invokespecial, f); } else { // append this item to the string buffer codeValue(env, ctx, asm); // FIX FOR 4071548 // 'StringBuffer.append' converts its argument as if by // 'valueOf', treating character arrays specially. This // violates JLS 15.17.1.1, which requires that concatenation // convert non-primitive arguments using 'toString'. We force // the treatment of all reference types as type 'Object', thus // invoking an overloading of 'append' that has the required // semantics. Type argType[] = { (type.inMask(TM_REFERENCE) && type != Type.tString) ? Type.tObject : type }; f = sbClsDef.matchMethod(env, sourceClass, idAppend, argType); asm.add(where, opc_invokevirtual, f); } } /** * Code */ void codeDup(Environment env, Context ctx, Assembler asm, int items, int depth) { switch (items) { case 0: return; case 1: switch (depth) { case 0: asm.add(where, opc_dup); return; case 1: asm.add(where, opc_dup_x1); return; case 2: asm.add(where, opc_dup_x2); return; } break; case 2: switch (depth) { case 0: asm.add(where, opc_dup2); return; case 1: asm.add(where, opc_dup2_x1); return; case 2: asm.add(where, opc_dup2_x2); return; } break; } throw new CompilerError("can't dup: " + items + ", " + depth); } void codeConversion(Environment env, Context ctx, Assembler asm, Type f, Type t) { int from = f.getTypeCode(); int to = t.getTypeCode(); switch (to) { case TC_BOOLEAN: if (from != TC_BOOLEAN) { break; } return; case TC_BYTE: if (from != TC_BYTE) { codeConversion(env, ctx, asm, f, Type.tInt); asm.add(where, opc_i2b); } return; case TC_CHAR: if (from != TC_CHAR) { codeConversion(env, ctx, asm, f, Type.tInt); asm.add(where, opc_i2c); } return; case TC_SHORT: if (from != TC_SHORT) { codeConversion(env, ctx, asm, f, Type.tInt); asm.add(where, opc_i2s); } return; case TC_INT: switch (from) { case TC_BYTE: case TC_CHAR: case TC_SHORT: case TC_INT: return; case TC_LONG: asm.add(where, opc_l2i); return; case TC_FLOAT: asm.add(where, opc_f2i); return; case TC_DOUBLE: asm.add(where, opc_d2i); return; } break; case TC_LONG: switch (from) { case TC_BYTE: case TC_CHAR: case TC_SHORT: case TC_INT: asm.add(where, opc_i2l); return; case TC_LONG: return; case TC_FLOAT: asm.add(where, opc_f2l); return; case TC_DOUBLE: asm.add(where, opc_d2l); return; } break; case TC_FLOAT: switch (from) { case TC_BYTE: case TC_CHAR: case TC_SHORT: case TC_INT: asm.add(where, opc_i2f); return; case TC_LONG: asm.add(where, opc_l2f); return; case TC_FLOAT: return; case TC_DOUBLE: asm.add(where, opc_d2f); return; } break; case TC_DOUBLE: switch (from) { case TC_BYTE: case TC_CHAR: case TC_SHORT: case TC_INT: asm.add(where, opc_i2d); return; case TC_LONG: asm.add(where, opc_l2d); return; case TC_FLOAT: asm.add(where, opc_f2d); return; case TC_DOUBLE: return; } break; case TC_CLASS: switch (from) { case TC_NULL: return; case TC_CLASS: case TC_ARRAY: try { if (!env.implicitCast(f, t)) { asm.add(where, opc_checkcast, env.getClassDeclaration(t)); } } catch (ClassNotFound e) { throw new CompilerError(e); } return; } break; case TC_ARRAY: switch (from) { case TC_NULL: return; case TC_CLASS: case TC_ARRAY: try { if (!env.implicitCast(f, t)) { asm.add(where, opc_checkcast, t); } return; } catch (ClassNotFound e) { throw new CompilerError(e); } } break; } throw new CompilerError("codeConversion: " + from + ", " + to); } /** * Check if the first thing is a constructor invocation */ public Expression firstConstructor() { return null; } /** * Create a copy of the expression for method inlining */ public Expression copyInline(Context ctx) { return (Expression)clone(); } /** * Print */ public void print(PrintStream out) { out.print(opNames[op]); } }