    001// Copyright 2011 The Apache Software Foundation
    002//
    003// Licensed under the Apache License, Version 2.0 (the "License");
    004// you may not use this file except in compliance with the License.
    005// You may obtain a copy of the License at
    006//
    007// http://www.apache.org/licenses/LICENSE-2.0
    008//
    009// Unless required by applicable law or agreed to in writing, software
    010// distributed under the License is distributed on an "AS IS" BASIS,
    011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012// See the License for the specific language governing permissions and
    013// limitations under the License.
    014
    015package org.apache.tapestry5.internal.plastic;
    016
    017import java.lang.reflect.Method;
    018import java.util.HashMap;
    019import java.util.Map;
    020
    021import org.apache.tapestry5.internal.plastic.InstructionBuilderState.LVInfo;
    022import org.apache.tapestry5.internal.plastic.asm.Label;
    023import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
    024import org.apache.tapestry5.internal.plastic.asm.Opcodes;
    025import org.apache.tapestry5.internal.plastic.asm.Type;
    026import org.apache.tapestry5.plastic.Condition;
    027import org.apache.tapestry5.plastic.InstructionBuilder;
    028import org.apache.tapestry5.plastic.InstructionBuilderCallback;
    029import org.apache.tapestry5.plastic.LocalVariable;
    030import org.apache.tapestry5.plastic.LocalVariableCallback;
    031import org.apache.tapestry5.plastic.MethodDescription;
    032import org.apache.tapestry5.plastic.PlasticField;
    033import org.apache.tapestry5.plastic.PlasticMethod;
    034import org.apache.tapestry5.plastic.PlasticUtils;
    035import org.apache.tapestry5.plastic.SwitchCallback;
    036import org.apache.tapestry5.plastic.TryCatchCallback;
    037import org.apache.tapestry5.plastic.WhenCallback;
    038import org.apache.tapestry5.plastic.WhileCallback;
    039
    040@SuppressWarnings("rawtypes")
    041public class InstructionBuilderImpl extends Lockable implements Opcodes, InstructionBuilder
    042{
    043    private static final int[] DUPE_OPCODES = new int[]
    044            {DUP, DUP_X1, DUP_X2};
    045
    046    /**
    047     * Maps from condition to opcode to jump to the false code block.
    048     */
    049    private static final Map<Condition, Integer> conditionToOpcode = new HashMap<>();
    050
    051    static
    052    {
    053        Map<Condition, Integer> m = conditionToOpcode;
    054
    055        m.put(Condition.NULL, IFNONNULL);
    056        m.put(Condition.NON_NULL, IFNULL);
    057        m.put(Condition.ZERO, IFNE);
    058        m.put(Condition.NON_ZERO, IFEQ);
    059        m.put(Condition.EQUAL, IF_ICMPNE);
    060        m.put(Condition.NOT_EQUAL, IF_ICMPEQ);
    061        m.put(Condition.LESS_THAN, IF_ICMPGE);
    062        m.put(Condition.GREATER, IF_ICMPLE);
    063    }
    064
    065    private static final Map<String, Integer> typeToSpecialComparisonOpcode = new HashMap<>();
    066
    067    static
    068    {
    069        Map<String, Integer> m = typeToSpecialComparisonOpcode;
    070
    071        m.put("long", LCMP);
    072        m.put("float", FCMPL);
    073        m.put("double", DCMPL);
    074    }
    075
    076    private static final Map<Object, Integer> constantOpcodes = new HashMap<>();
    077
    078    static
    079    {
    080        Map<Object, Integer> m = constantOpcodes;
    081
    082        m.put(Integer.valueOf(-1), ICONST_M1);
    083        m.put(Integer.valueOf(0), ICONST_0);
    084        m.put(Integer.valueOf(1), ICONST_1);
    085        m.put(Integer.valueOf(2), ICONST_2);
    086        m.put(Integer.valueOf(3), ICONST_3);
    087        m.put(Integer.valueOf(4), ICONST_4);
    088        m.put(Integer.valueOf(5), ICONST_5);
    089
    090        m.put(Long.valueOf(0), LCONST_0);
    091        m.put(Long.valueOf(1), LCONST_1);
    092
    093        m.put(Float.valueOf(0), FCONST_0);
    094        m.put(Float.valueOf(1), FCONST_1);
    095        m.put(Float.valueOf(2), FCONST_2);
    096
    097        m.put(Double.valueOf(0), DCONST_0);
    098        m.put(Double.valueOf(1), DCONST_1);
    099
    100        m.put(null, ACONST_NULL);
    101    }
    102
    103    protected final InstructionBuilderState state;
    104
    105    protected final MethodVisitor v;
    106
    107    protected final NameCache cache;
    108
    109    InstructionBuilderImpl(MethodDescription description, MethodVisitor visitor, NameCache cache)
    110    {
    111        InstructionBuilderState state = new InstructionBuilderState(description, visitor, cache);
    112        this.state = state;
    113
    114        // These are conveniences for values stored inside the state. In fact,
    115        // these fields predate the InstructionBuilderState type.
    116
    117        this.v = state.visitor;
    118        this.cache = state.nameCache;
    119    }
    120
    121    @Override
    122    public InstructionBuilder returnDefaultValue()
    123    {
    124        check();
    125
    126        PrimitiveType type = PrimitiveType.getByName(state.description.returnType);
    127
    128        if (type == null)
    129        {
    130            v.visitInsn(ACONST_NULL);
    131            v.visitInsn(ARETURN);
    132        } else
    133        {
    134            switch (type)
    135            {
    136                case VOID:
    137                    break;
    138
    139                case LONG:
    140                    v.visitInsn(LCONST_0);
    141                    break;
    142
    143                case FLOAT:
    144                    v.visitInsn(FCONST_0);
    145                    break;
    146
    147                case DOUBLE:
    148                    v.visitInsn(DCONST_0);
    149                    break;
    150
    151                default:
    152                    v.visitInsn(ICONST_0);
    153                    break;
    154            }
    155
    156            v.visitInsn(type.returnOpcode);
    157        }
    158
    159        return this;
    160    }
    161
    162    @Override
    163    public InstructionBuilder loadThis()
    164    {
    165        check();
    166
    167        v.visitVarInsn(ALOAD, 0);
    168
    169        return this;
    170    }
    171
    172    @Override
    173    public InstructionBuilder loadNull()
    174    {
    175        check();
    176
    177        v.visitInsn(ACONST_NULL);
    178
    179        return this;
    180    }
    181
    182    @Override
    183    public InstructionBuilder loadArgument(int index)
    184    {
    185        check();
    186
    187        PrimitiveType type = PrimitiveType.getByName(state.description.argumentTypes[index]);
    188
    189        int opcode = type == null ? ALOAD : type.loadOpcode;
    190
    191        v.visitVarInsn(state.argumentLoadOpcode[index], state.argumentIndex[index]);
    192
    193        return this;
    194    }
    195
    196    @Override
    197    public InstructionBuilder loadArguments()
    198    {
    199        check();
    200
    201        for (int i = 0; i < state.description.argumentTypes.length; i++)
    202        {
    203            loadArgument(i);
    204        }
    205
    206        return this;
    207    }
    208
    209    @Override
    210    public InstructionBuilder invokeSpecial(String containingClassName, MethodDescription description)
    211    {
    212        check();
    213
    214        doInvoke(INVOKESPECIAL, containingClassName, description, false);
    215
    216        return this;
    217    }
    218
    219    @Override
    220    public InstructionBuilder invokeVirtual(PlasticMethod method)
    221    {
    222        check();
    223
    224        assert method != null;
    225
    226        MethodDescription description = method.getDescription();
    227
    228        return invokeVirtual(method.getPlasticClass().getClassName(), description.returnType, description.methodName,
    229                description.argumentTypes);
    230    }
    231
    232    @Override
    233    public InstructionBuilder invokeVirtual(String className, String returnType, String methodName,
    234                                            String... argumentTypes)
    235    {
    236        check();
    237
    238        doInvoke(INVOKEVIRTUAL, className, returnType, methodName, false, argumentTypes);
    239
    240        return this;
    241    }
    242
    243    @Override
    244    public InstructionBuilder invokeInterface(String interfaceName, String returnType, String methodName,
    245                                              String... argumentTypes)
    246    {
    247        check();
    248
    249        doInvoke(INVOKEINTERFACE, interfaceName, returnType, methodName, true, argumentTypes);
    250
    251        return this;
    252    }
    253
    254    private void doInvoke(int opcode, String className, String returnType, String methodName, boolean isInterface,
    255                          String... argumentTypes)
    256    {
    257        v.visitMethodInsn(opcode, cache.toInternalName(className), methodName,
    258                cache.toMethodDescriptor(returnType, argumentTypes), isInterface);
    259    }
    260
    261    @Override
    262    public InstructionBuilder invokeStatic(Class clazz, Class returnType, String methodName, Class... argumentTypes)
    263    {
    264        doInvoke(INVOKESTATIC, clazz, returnType, methodName, false, argumentTypes);
    265
    266        return this;
    267    }
    268
    269    private void doInvoke(int opcode, Class clazz, Class returnType, String methodName, boolean isInterface,
    270                          Class... argumentTypes)
    271    {
    272        doInvoke(opcode, clazz.getName(), cache.toTypeName(returnType), methodName, isInterface,
    273                PlasticUtils.toTypeNames(argumentTypes));
    274    }
    275
    276    @Override
    277    public InstructionBuilder invoke(Method method)
    278    {
    279        check();
    280
    281        return invoke(method.getDeclaringClass(), method.getReturnType(), method.getName(), method.getParameterTypes());
    282    }
    283
    284    @Override
    285    public InstructionBuilder invoke(Class clazz, Class returnType, String methodName, Class... argumentTypes)
    286    {
    287        check();
    288
    289        doInvoke(clazz.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, clazz, returnType, methodName,
    290                clazz.isInterface(), argumentTypes);
    291
    292        return this;
    293    }
    294
    295    private void doInvoke(int opcode, String containingClassName, MethodDescription description, boolean isInterface)
    296    {
    297        v.visitMethodInsn(opcode, cache.toInternalName(containingClassName), description.methodName,
    298                cache.toDesc(description), isInterface);
    299    }
    300
    301    @Override
    302    public InstructionBuilder returnResult()
    303    {
    304        check();
    305
    306        PrimitiveType type = PrimitiveType.getByName(state.description.returnType);
    307
    308        int opcode = type == null ? ARETURN : type.returnOpcode;
    309
    310        v.visitInsn(opcode);
    311
    312        return this;
    313    }
    314
    315    @Override
    316    public InstructionBuilder boxPrimitive(String typeName)
    317    {
    318        check();
    319
    320        PrimitiveType type = PrimitiveType.getByName(typeName);
    321
    322        if (type != null && type != PrimitiveType.VOID)
    323        {
    324            v.visitMethodInsn(INVOKESTATIC, type.wrapperInternalName, "valueOf", type.valueOfMethodDescriptor,
    325                    false);
    326        }
    327
    328        return this;
    329    }
    330
    331    @Override
    332    public InstructionBuilder unboxPrimitive(String typeName)
    333    {
    334        check();
    335
    336        PrimitiveType type = PrimitiveType.getByName(typeName);
    337
    338        if (type != null)
    339        {
    340            doUnbox(type);
    341        }
    342
    343        return this;
    344    }
    345
    346    private void doUnbox(PrimitiveType type)
    347    {
    348        v.visitMethodInsn(INVOKEVIRTUAL, type.wrapperInternalName, type.toValueMethodName, type.toValueMethodDescriptor,
    349                false);
    350    }
    351
    352    @Override
    353    public InstructionBuilder getField(String className, String fieldName, String typeName)
    354    {
    355        check();
    356
    357        v.visitFieldInsn(GETFIELD, cache.toInternalName(className), fieldName, cache.toDesc(typeName));
    358
    359        return this;
    360    }
    361
    362    @Override
    363    public InstructionBuilder getStaticField(String className, String fieldName, String typeName)
    364    {
    365        check();
    366
    367        v.visitFieldInsn(GETSTATIC, cache.toInternalName(className), fieldName, cache.toDesc(typeName));
    368
    369        return this;
    370    }
    371
    372    @Override
    373    public InstructionBuilder getStaticField(String className, String fieldName, Class fieldType)
    374    {
    375        check();
    376
    377        return getStaticField(className, fieldName, cache.toTypeName(fieldType));
    378    }
    379
    380    @Override
    381    public InstructionBuilder putStaticField(String className, String fieldName, Class fieldType)
    382    {
    383        check();
    384
    385        return putStaticField(className, fieldName, cache.toTypeName(fieldType));
    386    }
    387
    388    @Override
    389    public InstructionBuilder putStaticField(String className, String fieldName, String typeName)
    390    {
    391        check();
    392
    393        v.visitFieldInsn(PUTSTATIC, cache.toInternalName(className), fieldName, cache.toDesc(typeName));
    394
    395        return this;
    396    }
    397
    398    @Override
    399    public InstructionBuilder getField(PlasticField field)
    400    {
    401        check();
    402
    403        return getField(field.getPlasticClass().getClassName(), field.getName(), field.getTypeName());
    404    }
    405
    406    @Override
    407    public InstructionBuilder putField(String className, String fieldName, String typeName)
    408    {
    409        check();
    410
    411        v.visitFieldInsn(PUTFIELD, cache.toInternalName(className), fieldName, cache.toDesc(typeName));
    412
    413        return this;
    414    }
    415
    416    @Override
    417    public InstructionBuilder putField(String className, String fieldName, Class fieldType)
    418    {
    419        check();
    420
    421        return putField(className, fieldName, cache.toTypeName(fieldType));
    422    }
    423
    424    @Override
    425    public InstructionBuilder getField(String className, String fieldName, Class fieldType)
    426    {
    427        check();
    428
    429        return getField(className, fieldName, cache.toTypeName(fieldType));
    430    }
    431
    432    @Override
    433    public InstructionBuilder loadArrayElement(int index, String elementType)
    434    {
    435        check();
    436
    437        loadConstant(index);
    438
    439        PrimitiveType type = PrimitiveType.getByName(elementType);
    440
    441        if (type == null)
    442        {
    443            v.visitInsn(AALOAD);
    444        } else
    445        {
    446            throw new RuntimeException("Access to non-object arrays is not yet supported.");
    447        }
    448
    449        return this;
    450    }
    451
    452    @Override
    453    public InstructionBuilder loadArrayElement()
    454    {
    455        check();
    456
    457        v.visitInsn(AALOAD);
    458
    459        return this;
    460    }
    461
    462    @Override
    463    public InstructionBuilder checkcast(String className)
    464    {
    465        check();
    466
    467        // Found out the hard way that array names are handled differently; you cast to the descriptor, not the internal
    468        // name.
    469
    470        String internalName = className.contains("[") ? cache.toDesc(className) : cache.toInternalName(className);
    471
    472        v.visitTypeInsn(CHECKCAST, internalName);
    473
    474        return this;
    475    }
    476
    477    @Override
    478    public InstructionBuilder checkcast(Class clazz)
    479    {
    480        check();
    481
    482        return checkcast(cache.toTypeName(clazz));
    483    }
    484
    485    @Override
    486    public InstructionBuilder instanceOf(String className)
    487    {
    488        check();
    489
    490        // Found out the hard way that array names are handled differently; you cast to the descriptor, not the internal
    491        // name.
    492
    493        String internalName = className.contains("[") ? cache.toDesc(className) : cache.toInternalName(className);
    494
    495        v.visitTypeInsn(INSTANCEOF, internalName);
    496
    497        return this;
    498    }
    499
    500    @Override
    501    public InstructionBuilder instanceOf(Class clazz)
    502    {
    503        check();
    504
    505        return instanceOf(cache.toTypeName(clazz));
    506    }
    507
    508    @Override
    509    public InstructionBuilder startTryCatch(TryCatchCallback callback)
    510    {
    511        check();
    512
    513        new TryCatchBlockImpl(this, state).doCallback(callback);
    514
    515        return this;
    516    }
    517
    518    @Override
    519    public InstructionBuilder newInstance(String className)
    520    {
    521        check();
    522
    523        v.visitTypeInsn(NEW, cache.toInternalName(className));
    524
    525        return this;
    526    }
    527
    528    @Override
    529    public InstructionBuilder newInstance(Class clazz)
    530    {
    531        check();
    532
    533        return newInstance(clazz.getName());
    534    }
    535
    536    @Override
    537    public InstructionBuilder invokeConstructor(String className, String... argumentTypes)
    538    {
    539        check();
    540
    541        doInvoke(INVOKESPECIAL, className, "void", "<init>", false, argumentTypes);
    542
    543        return this;
    544    }
    545
    546    @Override
    547    public InstructionBuilder invokeConstructor(Class clazz, Class... argumentTypes)
    548    {
    549        check();
    550
    551        return invokeConstructor(clazz.getName(), PlasticUtils.toTypeNames(argumentTypes));
    552    }
    553
    554    @Override
    555    public InstructionBuilder dupe(int depth)
    556    {
    557        check();
    558
    559        if (depth < 0 || depth >= DUPE_OPCODES.length)
    560            throw new IllegalArgumentException(String.format(
    561                    "Dupe depth %d is invalid; values from 0 to %d are allowed.", depth, DUPE_OPCODES.length - 1));
    562
    563        v.visitInsn(DUPE_OPCODES[depth]);
    564
    565        return this;
    566    }
    567
    568    @Override
    569    public InstructionBuilder dupe()
    570    {
    571        check();
    572
    573        v.visitInsn(DUP);
    574
    575        return this;
    576    }
    577
    578    @Override
    579    public InstructionBuilder pop()
    580    {
    581        check();
    582
    583        v.visitInsn(POP);
    584
    585        return this;
    586    }
    587
    588    @Override
    589    public InstructionBuilder swap()
    590    {
    591        check();
    592
    593        v.visitInsn(SWAP);
    594
    595        return this;
    596    }
    597
    598    @Override
    599    public InstructionBuilder loadConstant(Object constant)
    600    {
    601        check();
    602
    603        Integer opcode = constantOpcodes.get(constant);
    604
    605        if (opcode != null)
    606            v.visitInsn(opcode);
    607        else
    608            v.visitLdcInsn(constant);
    609
    610        return this;
    611    }
    612
    613    @Override
    614    public InstructionBuilder loadTypeConstant(String typeName)
    615    {
    616        check();
    617
    618        Type type = Type.getType(cache.toDesc(typeName));
    619
    620        v.visitLdcInsn(type);
    621
    622        return this;
    623    }
    624
    625    @Override
    626    public InstructionBuilder loadTypeConstant(Class clazz)
    627    {
    628        check();
    629
    630        Type type = Type.getType(clazz);
    631
    632        v.visitLdcInsn(type);
    633
    634        return this;
    635    }
    636
    637    @Override
    638    public InstructionBuilder castOrUnbox(String typeName)
    639    {
    640        check();
    641
    642        PrimitiveType type = PrimitiveType.getByName(typeName);
    643
    644        if (type == null)
    645            return checkcast(typeName);
    646
    647        v.visitTypeInsn(CHECKCAST, type.wrapperInternalName);
    648        doUnbox(type);
    649
    650        return this;
    651    }
    652
    653    @Override
    654    public InstructionBuilder throwException(String className, String message)
    655    {
    656        check();
    657
    658        newInstance(className).dupe().loadConstant(message);
    659
    660        invokeConstructor(className, "java.lang.String");
    661
    662        v.visitInsn(ATHROW);
    663
    664        return this;
    665    }
    666
    667    @Override
    668    public InstructionBuilder throwException(Class<? extends Throwable> exceptionType, String message)
    669    {
    670        check();
    671
    672        return throwException(cache.toTypeName(exceptionType), message);
    673    }
    674
    675    @Override
    676    public InstructionBuilder throwException()
    677    {
    678        check();
    679
    680        v.visitInsn(ATHROW);
    681
    682        return this;
    683    }
    684
    685    @Override
    686    public InstructionBuilder startSwitch(int min, int max, SwitchCallback callback)
    687    {
    688        check();
    689
    690        assert callback != null;
    691
    692        new SwitchBlockImpl(this, state, min, max).doCallback(callback);
    693
    694        return this;
    695    }
    696
    697    @Override
    698    public InstructionBuilder startVariable(String type, final LocalVariableCallback callback)
    699    {
    700        check();
    701
    702        final LocalVariable var = state.startVariable(type);
    703
    704        callback.doBuild(var, this);
    705
    706        state.stopVariable(var);
    707
    708        return this;
    709    }
    710
    711    @Override
    712    public InstructionBuilder storeVariable(LocalVariable var)
    713    {
    714        check();
    715
    716        state.store(var);
    717
    718        return this;
    719    }
    720
    721    @Override
    722    public InstructionBuilder loadVariable(LocalVariable var)
    723    {
    724        check();
    725
    726        state.load(var);
    727
    728        return this;
    729    }
    730
    731    @Override
    732    public InstructionBuilder when(Condition condition, final InstructionBuilderCallback ifTrue)
    733    {
    734        check();
    735
    736        assert ifTrue != null;
    737
    738        // This is nice for code coverage but could be more efficient, possibly generate
    739        // more efficient bytecode, if it talked to the v directly.
    740
    741        return when(condition, new WhenCallback()
    742        {
    743            @Override
    744            public void ifTrue(InstructionBuilder builder)
    745            {
    746                ifTrue.doBuild(builder);
    747            }
    748
    749            @Override
    750            public void ifFalse(InstructionBuilder builder)
    751            {
    752            }
    753        });
    754    }
    755
    756    @Override
    757    public InstructionBuilder when(Condition condition, final WhenCallback callback)
    758    {
    759        check();
    760
    761        assert condition != null;
    762        assert callback != null;
    763
    764        Label ifFalseLabel = new Label();
    765        Label endIfLabel = new Label();
    766
    767        v.visitJumpInsn(conditionToOpcode.get(condition), ifFalseLabel);
    768
    769        callback.ifTrue(this);
    770
    771        v.visitJumpInsn(GOTO, endIfLabel);
    772
    773        v.visitLabel(ifFalseLabel);
    774
    775        callback.ifFalse(this);
    776
    777        v.visitLabel(endIfLabel);
    778
    779        return this;
    780    }
    781
    782    @Override
    783    public InstructionBuilder doWhile(Condition condition, final WhileCallback callback)
    784    {
    785        check();
    786
    787        assert condition != null;
    788        assert callback != null;
    789
    790        Label doCheck = state.newLabel();
    791
    792        Label exitLoop = new Label();
    793
    794        callback.buildTest(this);
    795
    796        v.visitJumpInsn(conditionToOpcode.get(condition), exitLoop);
    797
    798        callback.buildBody(this);
    799
    800        v.visitJumpInsn(GOTO, doCheck);
    801
    802        v.visitLabel(exitLoop);
    803
    804        return this;
    805    }
    806
    807    @Override
    808    public InstructionBuilder increment(LocalVariable variable)
    809    {
    810        check();
    811
    812        LVInfo info = state.locals.get(variable);
    813
    814        v.visitIincInsn(info.offset, 1);
    815
    816        return this;
    817    }
    818
    819    @Override
    820    public InstructionBuilder arrayLength()
    821    {
    822        check();
    823
    824        v.visitInsn(ARRAYLENGTH);
    825
    826        return this;
    827    }
    828
    829    @Override
    830    public InstructionBuilder iterateArray(final InstructionBuilderCallback callback)
    831    {
    832        startVariable("int", new LocalVariableCallback()
    833        {
    834            @Override
    835            public void doBuild(final LocalVariable indexVariable, InstructionBuilder builder)
    836            {
    837                builder.loadConstant(0).storeVariable(indexVariable);
    838
    839                builder.doWhile(Condition.LESS_THAN, new WhileCallback()
    840                {
    841                    @Override
    842                    public void buildTest(InstructionBuilder builder)
    843                    {
    844                        builder.dupe().arrayLength();
    845                        builder.loadVariable(indexVariable).swap();
    846                    }
    847
    848                    @Override
    849                    public void buildBody(InstructionBuilder builder)
    850                    {
    851                        builder.dupe().loadVariable(indexVariable).loadArrayElement();
    852
    853                        callback.doBuild(builder);
    854
    855                        builder.increment(indexVariable);
    856                    }
    857                });
    858            }
    859        });
    860
    861        return this;
    862    }
    863
    864    @Override
    865    public InstructionBuilder dupeWide()
    866    {
    867        check();
    868
    869        v.visitInsn(DUP2);
    870
    871        return this;
    872    }
    873
    874    @Override
    875    public InstructionBuilder popWide()
    876    {
    877        check();
    878
    879        v.visitInsn(POP2);
    880
    881        return this;
    882    }
    883
    884    @Override
    885    public InstructionBuilder compareSpecial(String typeName)
    886    {
    887        check();
    888
    889        Integer opcode = typeToSpecialComparisonOpcode.get(typeName);
    890
    891        if (opcode == null)
    892            throw new IllegalArgumentException(String.format("Not a special primitive type: '%s'.", typeName));
    893
    894        v.visitInsn(opcode);
    895
    896        return this;
    897    }
    898
    899    void doCallback(InstructionBuilderCallback callback)
    900    {
    901        check();
    902
    903        if (callback != null)
    904            callback.doBuild(this);
    905
    906        lock();
    907    }
    908}