org.elasticsearch.painless.WriterExternal.java Source code

Java tutorial

Introduction

Here is the source code for org.elasticsearch.painless.WriterExternal.java

Source

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.elasticsearch.painless;

import org.antlr.v4.runtime.ParserRuleContext;
import org.elasticsearch.painless.Definition.Cast;
import org.elasticsearch.painless.Definition.Constructor;
import org.elasticsearch.painless.Definition.Field;
import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.Definition.Sort;
import org.elasticsearch.painless.Definition.Type;
import org.elasticsearch.painless.Metadata.ExpressionMetadata;
import org.elasticsearch.painless.Metadata.ExtNodeMetadata;
import org.elasticsearch.painless.Metadata.ExternalMetadata;
import org.elasticsearch.painless.PainlessParser.ExpressionContext;
import org.elasticsearch.painless.PainlessParser.ExtbraceContext;
import org.elasticsearch.painless.PainlessParser.ExtcallContext;
import org.elasticsearch.painless.PainlessParser.ExtcastContext;
import org.elasticsearch.painless.PainlessParser.ExtdotContext;
import org.elasticsearch.painless.PainlessParser.ExtfieldContext;
import org.elasticsearch.painless.PainlessParser.ExtnewContext;
import org.elasticsearch.painless.PainlessParser.ExtprecContext;
import org.elasticsearch.painless.PainlessParser.ExtstartContext;
import org.elasticsearch.painless.PainlessParser.ExtstringContext;
import org.elasticsearch.painless.PainlessParser.ExtvarContext;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.GeneratorAdapter;

import java.util.List;

import static org.elasticsearch.painless.PainlessParser.ADD;
import static org.elasticsearch.painless.PainlessParser.DIV;
import static org.elasticsearch.painless.PainlessParser.MUL;
import static org.elasticsearch.painless.PainlessParser.REM;
import static org.elasticsearch.painless.PainlessParser.SUB;
import static org.elasticsearch.painless.WriterConstants.TOBYTEEXACT_INT;
import static org.elasticsearch.painless.WriterConstants.TOBYTEEXACT_LONG;
import static org.elasticsearch.painless.WriterConstants.TOBYTEWOOVERFLOW_DOUBLE;
import static org.elasticsearch.painless.WriterConstants.TOBYTEWOOVERFLOW_FLOAT;
import static org.elasticsearch.painless.WriterConstants.TOCHAREXACT_INT;
import static org.elasticsearch.painless.WriterConstants.TOCHAREXACT_LONG;
import static org.elasticsearch.painless.WriterConstants.TOCHARWOOVERFLOW_DOUBLE;
import static org.elasticsearch.painless.WriterConstants.TOCHARWOOVERFLOW_FLOAT;
import static org.elasticsearch.painless.WriterConstants.TOFLOATWOOVERFLOW_DOUBLE;
import static org.elasticsearch.painless.WriterConstants.TOINTEXACT_LONG;
import static org.elasticsearch.painless.WriterConstants.TOINTWOOVERFLOW_DOUBLE;
import static org.elasticsearch.painless.WriterConstants.TOINTWOOVERFLOW_FLOAT;
import static org.elasticsearch.painless.WriterConstants.TOLONGWOOVERFLOW_DOUBLE;
import static org.elasticsearch.painless.WriterConstants.TOLONGWOOVERFLOW_FLOAT;
import static org.elasticsearch.painless.WriterConstants.TOSHORTEXACT_INT;
import static org.elasticsearch.painless.WriterConstants.TOSHORTEXACT_LONG;
import static org.elasticsearch.painless.WriterConstants.TOSHORTWOOVERFLOW_DOUBLE;
import static org.elasticsearch.painless.WriterConstants.TOSHORTWOOVERFLOW_FLOAT;

class WriterExternal {
    private final Metadata metadata;
    private final Definition definition;
    private final CompilerSettings settings;

    private final GeneratorAdapter execute;

    private final Writer writer;
    private final WriterUtility utility;
    private final WriterCaster caster;

    WriterExternal(final Metadata metadata, final GeneratorAdapter execute, final Writer writer,
            final WriterUtility utility, final WriterCaster caster) {
        this.metadata = metadata;
        definition = metadata.definition;
        settings = metadata.settings;

        this.execute = execute;

        this.writer = writer;
        this.utility = utility;
        this.caster = caster;
    }

    void processExtstart(final ExtstartContext ctx) {
        final ExternalMetadata startemd = metadata.getExternalMetadata(ctx);

        if (startemd.token == ADD) {
            final ExpressionMetadata storeemd = metadata.getExpressionMetadata(startemd.storeExpr);

            if (startemd.current.sort == Sort.STRING || storeemd.from.sort == Sort.STRING) {
                utility.writeNewStrings();
                utility.addStrings(startemd.storeExpr);
            }
        }

        final ExtprecContext precctx = ctx.extprec();
        final ExtcastContext castctx = ctx.extcast();
        final ExtvarContext varctx = ctx.extvar();
        final ExtnewContext newctx = ctx.extnew();
        final ExtstringContext stringctx = ctx.extstring();

        if (precctx != null) {
            writer.visit(precctx);
        } else if (castctx != null) {
            writer.visit(castctx);
        } else if (varctx != null) {
            writer.visit(varctx);
        } else if (newctx != null) {
            writer.visit(newctx);
        } else if (stringctx != null) {
            writer.visit(stringctx);
        } else {
            throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state.");
        }
    }

    void processExtprec(final ExtprecContext ctx) {
        final ExtprecContext precctx = ctx.extprec();
        final ExtcastContext castctx = ctx.extcast();
        final ExtvarContext varctx = ctx.extvar();
        final ExtnewContext newctx = ctx.extnew();
        final ExtstringContext stringctx = ctx.extstring();

        if (precctx != null) {
            writer.visit(precctx);
        } else if (castctx != null) {
            writer.visit(castctx);
        } else if (varctx != null) {
            writer.visit(varctx);
        } else if (newctx != null) {
            writer.visit(newctx);
        } else if (stringctx != null) {
            writer.visit(stringctx);
        } else {
            throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state.");
        }

        final ExtdotContext dotctx = ctx.extdot();
        final ExtbraceContext bracectx = ctx.extbrace();

        if (dotctx != null) {
            writer.visit(dotctx);
        } else if (bracectx != null) {
            writer.visit(bracectx);
        }
    }

    void processExtcast(final ExtcastContext ctx) {
        ExtNodeMetadata castenmd = metadata.getExtNodeMetadata(ctx);

        final ExtprecContext precctx = ctx.extprec();
        final ExtcastContext castctx = ctx.extcast();
        final ExtvarContext varctx = ctx.extvar();
        final ExtnewContext newctx = ctx.extnew();
        final ExtstringContext stringctx = ctx.extstring();

        if (precctx != null) {
            writer.visit(precctx);
        } else if (castctx != null) {
            writer.visit(castctx);
        } else if (varctx != null) {
            writer.visit(varctx);
        } else if (newctx != null) {
            writer.visit(newctx);
        } else if (stringctx != null) {
            writer.visit(stringctx);
        } else {
            throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state.");
        }

        caster.checkWriteCast(ctx, castenmd.castTo);
    }

    void processExtbrace(final ExtbraceContext ctx) {
        final ExpressionContext exprctx = ctx.expression();

        writer.visit(exprctx);
        writeLoadStoreExternal(ctx);

        final ExtdotContext dotctx = ctx.extdot();
        final ExtbraceContext bracectx = ctx.extbrace();

        if (dotctx != null) {
            writer.visit(dotctx);
        } else if (bracectx != null) {
            writer.visit(bracectx);
        }
    }

    void processExtdot(final ExtdotContext ctx) {
        final ExtcallContext callctx = ctx.extcall();
        final ExtfieldContext fieldctx = ctx.extfield();

        if (callctx != null) {
            writer.visit(callctx);
        } else if (fieldctx != null) {
            writer.visit(fieldctx);
        }
    }

    void processExtcall(final ExtcallContext ctx) {
        writeCallExternal(ctx);

        final ExtdotContext dotctx = ctx.extdot();
        final ExtbraceContext bracectx = ctx.extbrace();

        if (dotctx != null) {
            writer.visit(dotctx);
        } else if (bracectx != null) {
            writer.visit(bracectx);
        }
    }

    void processExtvar(final ExtvarContext ctx) {
        writeLoadStoreExternal(ctx);

        final ExtdotContext dotctx = ctx.extdot();
        final ExtbraceContext bracectx = ctx.extbrace();

        if (dotctx != null) {
            writer.visit(dotctx);
        } else if (bracectx != null) {
            writer.visit(bracectx);
        }
    }

    void processExtfield(final ExtfieldContext ctx) {
        writeLoadStoreExternal(ctx);

        final ExtdotContext dotctx = ctx.extdot();
        final ExtbraceContext bracectx = ctx.extbrace();

        if (dotctx != null) {
            writer.visit(dotctx);
        } else if (bracectx != null) {
            writer.visit(bracectx);
        }
    }

    void processExtnew(final ExtnewContext ctx) {
        writeNewExternal(ctx);

        final ExtdotContext dotctx = ctx.extdot();

        if (dotctx != null) {
            writer.visit(dotctx);
        }
    }

    void processExtstring(final ExtstringContext ctx) {
        final ExtNodeMetadata stringenmd = metadata.getExtNodeMetadata(ctx);

        utility.writeConstant(ctx, stringenmd.target);

        final ExtdotContext dotctx = ctx.extdot();
        final ExtbraceContext bracectx = ctx.extbrace();

        if (dotctx != null) {
            writer.visit(dotctx);
        } else if (bracectx != null) {
            writer.visit(bracectx);
        }
    }

    private void writeLoadStoreExternal(final ParserRuleContext source) {
        final ExtNodeMetadata sourceenmd = metadata.getExtNodeMetadata(source);
        final ExternalMetadata parentemd = metadata.getExternalMetadata(sourceenmd.parent);

        if (sourceenmd.target == null) {
            return;
        }

        final boolean length = "#length".equals(sourceenmd.target);
        final boolean array = "#brace".equals(sourceenmd.target);
        final boolean name = sourceenmd.target instanceof String && !length && !array;
        final boolean variable = sourceenmd.target instanceof Integer;
        final boolean field = sourceenmd.target instanceof Field;
        final boolean shortcut = sourceenmd.target instanceof Object[];

        if (!length && !variable && !field && !array && !name && !shortcut) {
            throw new IllegalStateException(WriterUtility.error(source) + "Target not found for load/store.");
        }

        final boolean maplist = shortcut && (boolean) ((Object[]) sourceenmd.target)[2];
        final Object constant = shortcut ? ((Object[]) sourceenmd.target)[3] : null;

        final boolean x1 = field || name || (shortcut && !maplist);
        final boolean x2 = array || (shortcut && maplist);

        if (length) {
            execute.arrayLength();
        } else if (sourceenmd.last && parentemd.storeExpr != null) {
            final ExpressionMetadata expremd = metadata.getExpressionMetadata(parentemd.storeExpr);
            final boolean cat = utility.containsStrings(parentemd.storeExpr);

            if (cat) {
                if (maplist && constant != null) {
                    utility.writeConstant(source, constant);
                }

                if (field || name || (shortcut && !maplist)) {
                    execute.dupX1();
                } else if (array || maplist) {
                    execute.dup2X1();
                }

                writeLoadStoreInstruction(source, false, variable, field, name, array, shortcut);
                utility.writeAppendStrings(sourceenmd.type.sort);
                writer.visit(parentemd.storeExpr);

                if (utility.containsStrings(parentemd.storeExpr)) {
                    utility.writeAppendStrings(expremd.to.sort);
                    utility.removeStrings(parentemd.storeExpr);
                }

                utility.writeToStrings();
                caster.checkWriteCast(source, sourceenmd.castTo);

                if (parentemd.read) {
                    utility.writeDup(sourceenmd.type.sort.size, x1, x2);
                }

                writeLoadStoreInstruction(source, true, variable, field, name, array, shortcut);
            } else if (parentemd.token > 0) {
                final int token = parentemd.token;

                if (maplist && constant != null) {
                    utility.writeConstant(source, constant);
                }

                if (field || name || (shortcut && !maplist)) {
                    execute.dup();
                } else if (array || maplist) {
                    execute.dup2();
                }

                writeLoadStoreInstruction(source, false, variable, field, name, array, shortcut);

                if (parentemd.read && parentemd.post) {
                    utility.writeDup(sourceenmd.type.sort.size, x1, x2);
                }

                caster.checkWriteCast(source, sourceenmd.castFrom);
                writer.visit(parentemd.storeExpr);

                utility.writeBinaryInstruction(source, sourceenmd.promote, token);

                boolean exact = false;

                if (!settings.getNumericOverflow() && expremd.typesafe && sourceenmd.type.sort != Sort.DEF
                        && (token == MUL || token == DIV || token == REM || token == ADD || token == SUB)) {
                    exact = writeExactInstruction(sourceenmd.type.sort, sourceenmd.promote.sort);
                }

                if (!exact) {
                    caster.checkWriteCast(source, sourceenmd.castTo);
                }

                if (parentemd.read && !parentemd.post) {
                    utility.writeDup(sourceenmd.type.sort.size, x1, x2);
                }

                writeLoadStoreInstruction(source, true, variable, field, name, array, shortcut);
            } else {
                if (constant != null) {
                    utility.writeConstant(source, constant);
                }

                writer.visit(parentemd.storeExpr);

                if (parentemd.read) {
                    utility.writeDup(sourceenmd.type.sort.size, x1, x2);
                }

                writeLoadStoreInstruction(source, true, variable, field, name, array, shortcut);
            }
        } else {
            if (constant != null) {
                utility.writeConstant(source, constant);
            }

            writeLoadStoreInstruction(source, false, variable, field, name, array, shortcut);
        }
    }

    private void writeLoadStoreInstruction(final ParserRuleContext source, final boolean store,
            final boolean variable, final boolean field, final boolean name, final boolean array,
            final boolean shortcut) {
        final ExtNodeMetadata sourceemd = metadata.getExtNodeMetadata(source);

        if (variable) {
            writeLoadStoreVariable(source, store, sourceemd.type, (int) sourceemd.target);
        } else if (field) {
            writeLoadStoreField(store, (Field) sourceemd.target);
        } else if (name) {
            writeLoadStoreField(source, store, (String) sourceemd.target);
        } else if (array) {
            writeLoadStoreArray(source, store, sourceemd.type);
        } else if (shortcut) {
            Object[] targets = (Object[]) sourceemd.target;
            writeLoadStoreShortcut(store, (Method) targets[0], (Method) targets[1]);
        } else {
            throw new IllegalStateException(
                    WriterUtility.error(source) + "Load/Store requires a variable, field, or array.");
        }
    }

    private void writeLoadStoreVariable(final ParserRuleContext source, final boolean store, final Type type,
            int slot) {
        if (type.sort == Sort.VOID) {
            throw new IllegalStateException(WriterUtility.error(source) + "Cannot load/store void type.");
        }

        if (store) {
            execute.visitVarInsn(type.type.getOpcode(Opcodes.ISTORE), slot);
        } else {
            execute.visitVarInsn(type.type.getOpcode(Opcodes.ILOAD), slot);
        }
    }

    private void writeLoadStoreField(final boolean store, final Field field) {
        if (java.lang.reflect.Modifier.isStatic(field.reflect.getModifiers())) {
            if (store) {
                execute.putStatic(field.owner.type, field.reflect.getName(), field.type.type);
            } else {
                execute.getStatic(field.owner.type, field.reflect.getName(), field.type.type);

                if (!field.generic.clazz.equals(field.type.clazz)) {
                    execute.checkCast(field.generic.type);
                }
            }
        } else {
            if (store) {
                execute.putField(field.owner.type, field.reflect.getName(), field.type.type);
            } else {
                execute.getField(field.owner.type, field.reflect.getName(), field.type.type);

                if (!field.generic.clazz.equals(field.type.clazz)) {
                    execute.checkCast(field.generic.type);
                }
            }
        }
    }

    private void writeLoadStoreField(final ParserRuleContext source, final boolean store, final String name) {
        if (store) {
            execute.visitInvokeDynamicInsn(name, WriterConstants.DEF_DYNAMIC_STORE_FIELD_DESC,
                    WriterConstants.DEF_BOOTSTRAP_HANDLE, new Object[] { DynamicCallSite.STORE });
        } else {
            execute.visitInvokeDynamicInsn(name, WriterConstants.DEF_DYNAMIC_LOAD_FIELD_DESC,
                    WriterConstants.DEF_BOOTSTRAP_HANDLE, new Object[] { DynamicCallSite.LOAD });
        }
    }

    private void writeLoadStoreArray(final ParserRuleContext source, final boolean store, final Type type) {
        if (type.sort == Sort.VOID) {
            throw new IllegalStateException(WriterUtility.error(source) + "Cannot load/store void type.");
        }

        if (type.sort == Sort.DEF) {
            if (store) {
                execute.visitInvokeDynamicInsn("arrayStore", WriterConstants.DEF_DYNAMIC_ARRAY_STORE_DESC,
                        WriterConstants.DEF_BOOTSTRAP_HANDLE, new Object[] { DynamicCallSite.ARRAY_STORE });
            } else {
                execute.visitInvokeDynamicInsn("arrayLoad", WriterConstants.DEF_DYNAMIC_ARRAY_LOAD_DESC,
                        WriterConstants.DEF_BOOTSTRAP_HANDLE, new Object[] { DynamicCallSite.ARRAY_LOAD });
            }
        } else {
            if (store) {
                execute.arrayStore(type.type);
            } else {
                execute.arrayLoad(type.type);
            }
        }
    }

    private void writeLoadStoreShortcut(final boolean store, final Method getter, final Method setter) {
        final Method method = store ? setter : getter;

        if (java.lang.reflect.Modifier.isInterface(getter.owner.clazz.getModifiers())) {
            execute.invokeInterface(method.owner.type, method.method);
        } else {
            execute.invokeVirtual(method.owner.type, method.method);
        }

        if (store) {
            utility.writePop(method.rtn.type.getSize());
        } else if (!method.rtn.clazz.equals(method.handle.type().returnType())) {
            execute.checkCast(method.rtn.type);
        }
    }

    /**
     * Called for any compound assignment (including increment/decrement instructions).
     * We have to be stricter than writeBinary, and do overflow checks against the original type's size
     * instead of the promoted type's size, since the result will be implicitly cast back.
     *
     * @return This will be true if an instruction is written, false otherwise.
     */
    private boolean writeExactInstruction(final Sort osort, final Sort psort) {
        if (psort == Sort.DOUBLE) {
            if (osort == Sort.FLOAT) {
                execute.invokeStatic(definition.utilityType.type, TOFLOATWOOVERFLOW_DOUBLE);
            } else if (osort == Sort.FLOAT_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOFLOATWOOVERFLOW_DOUBLE);
                execute.checkCast(definition.floatobjType.type);
            } else if (osort == Sort.LONG) {
                execute.invokeStatic(definition.utilityType.type, TOLONGWOOVERFLOW_DOUBLE);
            } else if (osort == Sort.LONG_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOLONGWOOVERFLOW_DOUBLE);
                execute.checkCast(definition.longobjType.type);
            } else if (osort == Sort.INT) {
                execute.invokeStatic(definition.utilityType.type, TOINTWOOVERFLOW_DOUBLE);
            } else if (osort == Sort.INT_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOINTWOOVERFLOW_DOUBLE);
                execute.checkCast(definition.intobjType.type);
            } else if (osort == Sort.CHAR) {
                execute.invokeStatic(definition.utilityType.type, TOCHARWOOVERFLOW_DOUBLE);
            } else if (osort == Sort.CHAR_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOCHARWOOVERFLOW_DOUBLE);
                execute.checkCast(definition.charobjType.type);
            } else if (osort == Sort.SHORT) {
                execute.invokeStatic(definition.utilityType.type, TOSHORTWOOVERFLOW_DOUBLE);
            } else if (osort == Sort.SHORT_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOSHORTWOOVERFLOW_DOUBLE);
                execute.checkCast(definition.shortobjType.type);
            } else if (osort == Sort.BYTE) {
                execute.invokeStatic(definition.utilityType.type, TOBYTEWOOVERFLOW_DOUBLE);
            } else if (osort == Sort.BYTE_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOBYTEWOOVERFLOW_DOUBLE);
                execute.checkCast(definition.byteobjType.type);
            } else {
                return false;
            }
        } else if (psort == Sort.FLOAT) {
            if (osort == Sort.LONG) {
                execute.invokeStatic(definition.utilityType.type, TOLONGWOOVERFLOW_FLOAT);
            } else if (osort == Sort.LONG_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOLONGWOOVERFLOW_FLOAT);
                execute.checkCast(definition.longobjType.type);
            } else if (osort == Sort.INT) {
                execute.invokeStatic(definition.utilityType.type, TOINTWOOVERFLOW_FLOAT);
            } else if (osort == Sort.INT_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOINTWOOVERFLOW_FLOAT);
                execute.checkCast(definition.intobjType.type);
            } else if (osort == Sort.CHAR) {
                execute.invokeStatic(definition.utilityType.type, TOCHARWOOVERFLOW_FLOAT);
            } else if (osort == Sort.CHAR_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOCHARWOOVERFLOW_FLOAT);
                execute.checkCast(definition.charobjType.type);
            } else if (osort == Sort.SHORT) {
                execute.invokeStatic(definition.utilityType.type, TOSHORTWOOVERFLOW_FLOAT);
            } else if (osort == Sort.SHORT_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOSHORTWOOVERFLOW_FLOAT);
                execute.checkCast(definition.shortobjType.type);
            } else if (osort == Sort.BYTE) {
                execute.invokeStatic(definition.utilityType.type, TOBYTEWOOVERFLOW_FLOAT);
            } else if (osort == Sort.BYTE_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOBYTEWOOVERFLOW_FLOAT);
                execute.checkCast(definition.byteobjType.type);
            } else {
                return false;
            }
        } else if (psort == Sort.LONG) {
            if (osort == Sort.INT) {
                execute.invokeStatic(definition.mathType.type, TOINTEXACT_LONG);
            } else if (osort == Sort.INT_OBJ) {
                execute.invokeStatic(definition.mathType.type, TOINTEXACT_LONG);
                execute.checkCast(definition.intobjType.type);
            } else if (osort == Sort.CHAR) {
                execute.invokeStatic(definition.utilityType.type, TOCHAREXACT_LONG);
            } else if (osort == Sort.CHAR_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOCHAREXACT_LONG);
                execute.checkCast(definition.charobjType.type);
            } else if (osort == Sort.SHORT) {
                execute.invokeStatic(definition.utilityType.type, TOSHORTEXACT_LONG);
            } else if (osort == Sort.SHORT_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOSHORTEXACT_LONG);
                execute.checkCast(definition.shortobjType.type);
            } else if (osort == Sort.BYTE) {
                execute.invokeStatic(definition.utilityType.type, TOBYTEEXACT_LONG);
            } else if (osort == Sort.BYTE_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOBYTEEXACT_LONG);
                execute.checkCast(definition.byteobjType.type);
            } else {
                return false;
            }
        } else if (psort == Sort.INT) {
            if (osort == Sort.CHAR) {
                execute.invokeStatic(definition.utilityType.type, TOCHAREXACT_INT);
            } else if (osort == Sort.CHAR_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOCHAREXACT_INT);
                execute.checkCast(definition.charobjType.type);
            } else if (osort == Sort.SHORT) {
                execute.invokeStatic(definition.utilityType.type, TOSHORTEXACT_INT);
            } else if (osort == Sort.SHORT_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOSHORTEXACT_INT);
                execute.checkCast(definition.shortobjType.type);
            } else if (osort == Sort.BYTE) {
                execute.invokeStatic(definition.utilityType.type, TOBYTEEXACT_INT);
            } else if (osort == Sort.BYTE_OBJ) {
                execute.invokeStatic(definition.utilityType.type, TOBYTEEXACT_INT);
                execute.checkCast(definition.byteobjType.type);
            } else {
                return false;
            }
        } else {
            return false;
        }

        return true;
    }

    private void writeNewExternal(final ExtnewContext source) {
        final ExtNodeMetadata sourceenmd = metadata.getExtNodeMetadata(source);
        final ExternalMetadata parentemd = metadata.getExternalMetadata(sourceenmd.parent);

        final boolean makearray = "#makearray".equals(sourceenmd.target);
        final boolean constructor = sourceenmd.target instanceof Constructor;

        if (!makearray && !constructor) {
            throw new IllegalStateException(WriterUtility.error(source) + "Target not found for new call.");
        }

        if (makearray) {
            for (final ExpressionContext exprctx : source.expression()) {
                writer.visit(exprctx);
            }

            if (sourceenmd.type.sort == Sort.ARRAY) {
                execute.visitMultiANewArrayInsn(sourceenmd.type.type.getDescriptor(),
                        sourceenmd.type.type.getDimensions());
            } else {
                execute.newArray(sourceenmd.type.type);
            }
        } else {
            execute.newInstance(sourceenmd.type.type);

            if (parentemd.read) {
                execute.dup();
            }

            for (final ExpressionContext exprctx : source.arguments().expression()) {
                writer.visit(exprctx);
            }

            final Constructor target = (Constructor) sourceenmd.target;
            execute.invokeConstructor(target.owner.type, target.method);
        }
    }

    private void writeCallExternal(final ExtcallContext source) {
        final ExtNodeMetadata sourceenmd = metadata.getExtNodeMetadata(source);

        final boolean method = sourceenmd.target instanceof Method;
        final boolean def = sourceenmd.target instanceof String;

        if (!method && !def) {
            throw new IllegalStateException(WriterUtility.error(source) + "Target not found for call.");
        }

        final List<ExpressionContext> arguments = source.arguments().expression();

        if (method) {
            for (final ExpressionContext exprctx : arguments) {
                writer.visit(exprctx);
            }

            final Method target = (Method) sourceenmd.target;

            if (java.lang.reflect.Modifier.isStatic(target.reflect.getModifiers())) {
                execute.invokeStatic(target.owner.type, target.method);
            } else if (java.lang.reflect.Modifier.isInterface(target.owner.clazz.getModifiers())) {
                execute.invokeInterface(target.owner.type, target.method);
            } else {
                execute.invokeVirtual(target.owner.type, target.method);
            }

            if (!target.rtn.clazz.equals(target.handle.type().returnType())) {
                execute.checkCast(target.rtn.type);
            }
        } else {
            writeDynamicCallExternal(source);
        }
    }

    private void writeDynamicCallExternal(final ExtcallContext source) {
        final ExtNodeMetadata sourceenmd = metadata.getExtNodeMetadata(source);
        final List<ExpressionContext> arguments = source.arguments().expression();

        StringBuilder signature = new StringBuilder();
        signature.append('(');
        // first parameter is the receiver, we never know its type: always Object
        signature.append(WriterConstants.OBJECT_TYPE.getDescriptor());

        for (int i = 0; i < arguments.size(); i++) {
            ExpressionMetadata arg = metadata.getExpressionMetadata(arguments.get(i));
            // disable any implicit casts/conversion for arguments, let invokeDynamic take care
            arg.to = arg.from;
            arg.cast = new Cast(arg.from, arg.from);
            signature.append(arg.from.type.getDescriptor());
            writer.visit(arguments.get(i));
        }
        signature.append(')');
        // return value: currently always Object. making this better may be tricky...
        signature.append(WriterConstants.OBJECT_TYPE.getDescriptor());
        execute.visitInvokeDynamicInsn((String) sourceenmd.target, signature.toString(),
                WriterConstants.DEF_BOOTSTRAP_HANDLE, new Object[] { DynamicCallSite.METHOD_CALL });
    }
}