All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.questdb.std.BytecodeAssembler Maven / Gradle / Ivy

/*******************************************************************************
 *     ___                  _   ____  ____
 *    / _ \ _   _  ___  ___| |_|  _ \| __ )
 *   | | | | | | |/ _ \/ __| __| | | |  _ \
 *   | |_| | |_| |  __/\__ \ |_| |_| | |_) |
 *    \__\_\\__,_|\___||___/\__|____/|____/
 *
 *  Copyright (c) 2014-2019 Appsicle
 *  Copyright (c) 2019-2020 QuestDB
 *
 *  Licensed 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 io.questdb.std;

import io.questdb.log.Log;
import io.questdb.log.LogFactory;
import io.questdb.std.ex.BytecodeException;
import io.questdb.std.str.AbstractCharSink;
import io.questdb.std.str.CharSink;

import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class BytecodeAssembler {

    private static final int ACC_PUBLIC = 0x01;
    private static final int ACC_PRIVATE = 0x02;
    private static final Log LOG = LogFactory.getLog(BytecodeAssembler.class);
    private static final int aload = 0x19;
    private static final int aload_0 = 0x2a;
    private static final int aload_1 = 0x2b;
    private static final int aload_2 = 0x2c;
    private static final int aload_3 = 0x2d;
    private static final int istore = 0x36;
    private static final int istore_0 = 0x3b;
    private static final int istore_1 = 0x3c;
    private static final int istore_2 = 0x3d;
    private static final int istore_3 = 0x3e;
    private static final int lstore = 0x37;
    private static final int lstore_0 = 0x3f;
    private static final int lstore_1 = 0x40;
    private static final int lstore_2 = 0x41;
    private static final int lstore_3 = 0x42;
    private static final int iinc = 0x84;
    private static final int lload = 0x16;
    private static final int lload_0 = 0x1e;
    private static final int lload_1 = 0x1f;
    private static final int lload_2 = 0x20;
    private static final int lload_3 = 0x21;
    private static final int iload = 0x15;
    private static final int iload_0 = 0x1a;
    private static final int iload_1 = 0x1b;
    private static final int iload_2 = 0x1c;
    private static final int iload_3 = 0x1d;
    private static final int iconst_m1 = 2;
    private static final int iconst_0 = 3;
    private static final int bipush = 0x10;
    private static final int sipush = 0x11;
    private static final int invokespecial = 183;
    private static final int O_POOL_COUNT = 8;
    private final Utf8Appender utf8Appender = new Utf8Appender();
    private final CharSequenceIntHashMap utf8Cache = new CharSequenceIntHashMap();
    private final ObjIntHashMap> classCache = new ObjIntHashMap<>();
    private ByteBuffer buf;
    private int poolCount;
    private int objectClassIndex;
    private int defaultConstructorNameIndex;
    private int defaultConstructorDescIndex;
    private int defaultConstructorMethodIndex;
    private int codeAttributeIndex;
    private int codeAttributeStart;
    private int codeStart;
    private int stackMapTableCut;
    private Class host;

    public BytecodeAssembler() {
        this.buf = ByteBuffer.allocate(1024 * 1024).order(ByteOrder.BIG_ENDIAN);
        this.poolCount = 1;
    }

    public void aload(int value) {
        optimisedIO(aload_0, aload_1, aload_2, aload_3, aload, value);
    }

    public void append_frame(int itemCount, int offset) {
        putByte(0xfc + itemCount - 1);
        putShort(offset);
    }

    public void d2f() {
        putShort(0x90);
    }

    public void d2i() {
        putShort(0x8E);
    }

    public void d2l() {
        putShort(0x8F);
    }

    public void defineClass(int thisClassIndex) {
        defineClass(thisClassIndex, objectClassIndex);
    }

    public void defineClass(int thisClassIndex, int superclassIndex) {
        // access flags
        putShort(ACC_PUBLIC);
        // this class index
        putShort(thisClassIndex);
        // super class
        putShort(superclassIndex);
    }

    public void defineDefaultConstructor() {
        defineDefaultConstructor(defaultConstructorMethodIndex);
    }

    public void defineDefaultConstructor(int superIndex) {
        // constructor method entry
        startMethod(defaultConstructorNameIndex, defaultConstructorDescIndex, 1, 1);
        // code
        aload(0);
        putByte(invokespecial);
        putShort(superIndex);
        return_();
        endMethodCode();
        // exceptions
        putShort(0);
        // attribute count
        putShort(0);
        endMethod();
    }

    public void defineField(int nameIndex, int typeIndex) {
        putShort(ACC_PRIVATE);
        putShort(nameIndex);
        putShort(typeIndex);
        // attribute count
        putShort(0);
    }

    public void dump(String path) {
        try (FileOutputStream fos = new FileOutputStream(path)) {
            int p = buf.position();
            int l = buf.limit();
            buf.flip();
            fos.getChannel().write(buf);
            buf.limit(l);
            buf.position(p);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void endMethod() {
        putInt(codeAttributeStart - 4, position() - codeAttributeStart);
    }

    public void endMethodCode() {
        int len = position() - codeStart;
        if (len > 64 * 1024) {
            LOG.error().$("Too much input to generate ").$(host.getName()).$(". Bytecode is too long").$();
            throw BytecodeException.INSTANCE;
        }
        putInt(codeStart - 4, position() - codeStart);
    }

    public void endStackMapTables() {
        putInt(stackMapTableCut, position() - stackMapTableCut - 4);
    }

    public void f2d() {
        putShort(0x8D);
    }

    public void f2i() {
        putShort(0x8B);
    }

    public void f2l() {
        putShort(0x8C);
    }

    public void fieldCount(int count) {
        putShort(count);
    }

    public void finishPool() {
        putShort(O_POOL_COUNT, poolCount);
    }

    public void full_frame(int offset) {
        putByte(0xff);
        putShort(offset);
    }

    public int getCodeStart() {
        return codeStart;
    }

    public void getfield(int index) {
        putByte(0xb4);
        putShort(index);
    }

    public int goto_() {
        return genericGoto(0xa7);
    }

    public void i2b() {
        putShort(0x91);
    }

    public void i2d() {
        putShort(0x87);
    }

    public void i2f() {
        putShort(0x86);
    }

    public void i2l() {
        putShort(0x85);
    }

    public void i2s() {
        putShort(0x93);
    }

    public void iadd() {
        putByte(0x60);
    }

    public void iconst(int v) {
        if (v == -1) {
            putByte(iconst_m1);
        } else if (v > -1 && v < 6) {
            putByte(iconst_0 + v);
        } else if (v < 0) {
            putByte(sipush);
            putShort(v);
        } else if (v < 128) {
            putByte(bipush);
            putByte(v);
        } else {
            putByte(sipush);
            putShort(v);
        }
    }

    public int if_icmpge() {
        return genericGoto(0xa2);
    }

    public int if_icmpne() {
        return genericGoto(0xa0);
    }

    public int ifne() {
        return genericGoto(0x9a);
    }

    public void iinc(int index, int inc) {
        putByte(iinc);
        putByte(index);
        putByte(inc);
    }

    public void iload(int value) {
        optimisedIO(iload_0, iload_1, iload_2, iload_3, iload, value);
    }

    public void ineg() {
        putByte(0x74);
    }

    public void init(Class host) {
        this.host = host;
        this.buf.clear();
        this.poolCount = 1;
        this.utf8Cache.clear();
        this.classCache.clear();
    }

    public void interfaceCount(int count) {
        putShort(count);
    }

    public void invokeInterface(int interfaceIndex, int argCount) {
        putByte(185);
        putShort(interfaceIndex);
        putByte(argCount + 1);
        putByte(0);
    }

    public void invokeStatic(int index) {
        putByte(184);
        putShort(index);
    }

    public void invokeVirtual(int index) {
        putByte(182);
        putShort(index);
    }

    public void irem() {
        putByte(0x70);
    }

    public void ireturn() {
        putByte(0xac);
    }

    public void istore(int value) {
        optimisedIO(istore_0, istore_1, istore_2, istore_3, istore, value);
    }

    public void isub() {
        putByte(0x64);
    }

    public void l2d() {
        putShort(0x8A);
    }

    public void l2f() {
        putShort(0x89);
    }

    public void l2i() {
        putShort(0x88);
    }

    public void lcmp() {
        putByte(0x94);
    }

    public void lconst_0() {
        putByte(0x09);
    }

    public void ldc(int index) {
        putByte(0x12);
        putByte(index);
    }

    public void ldc2_w(int index) {
        putByte(0x14);
        putShort(index);
    }

    public void lload(int value) {
        optimisedIO(lload_0, lload_1, lload_2, lload_3, lload, value);
    }

    public void lmul() {
        putByte(0x69);
    }

    @SuppressWarnings("unchecked")
    public  Class loadClass(Class host) {
        byte[] b = new byte[position()];
        System.arraycopy(buf.array(), 0, b, 0, b.length);
        return (Class) Unsafe.getUnsafe().defineAnonymousClass(host, b, null);
    }

    public void lreturn() {
        putByte(0xad);
    }

    public void lstore(int value) {
        optimisedIO(lstore_0, lstore_1, lstore_2, lstore_3, lstore, value);
    }

    public void methodCount(int count) {
        putShort(count);
    }

    public  T newInstance() {
        Class x = loadClass(host);
        try {
            return x.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            LOG.error().$("could not create an instance of ").$(host.getName()).$(", cause: ").$(e).$();
            throw BytecodeException.INSTANCE;
        }
    }

    public int poolClass(int classIndex) {
        putByte(0x07);
        putShort(classIndex);
        return poolCount++;
    }

    public int poolClass(Class clazz) {
        int index = classCache.keyIndex(clazz);
        if (index > -1) {
            String name = clazz.getName();
            putByte(0x01);
            int n;
            putShort(n = name.length());
            for (int i = 0; i < n; i++) {
                char c = name.charAt(i);
                if (c == '.') {
                    putByte('/');
                } else {
                    putByte(c);
                }
            }
            int result = poolClass(this.poolCount++);
            classCache.putAt(index, clazz, result);
            return result;
        }
        return classCache.valueAt(index);
    }

    public int poolField(int classIndex, int nameAndTypeIndex) {
        return poolRef(0x09, classIndex, nameAndTypeIndex);
    }

    public int poolInterfaceMethod(Class clazz, String name, String sig) {
        return poolInterfaceMethod(poolClass(clazz), poolNameAndType(poolUtf8(name), poolUtf8(sig)));
    }

    public int poolInterfaceMethod(int classIndex, String name, String sig) {
        return poolInterfaceMethod(classIndex, poolNameAndType(poolUtf8(name), poolUtf8(sig)));
    }

    public int poolLongConst(long value) {
        putByte(0x05);
        putLong(value);
        int index = poolCount;
        poolCount += 2;
        return index;
    }

    public int poolMethod(int classIndex, int nameAndTypeIndex) {
        return poolRef(0x0A, classIndex, nameAndTypeIndex);
    }

    public int poolMethod(int classIndex, CharSequence methodName, CharSequence signature) {
        return poolMethod(classIndex, poolNameAndType(poolUtf8(methodName), poolUtf8(signature)));
    }

    public int poolMethod(Class clazz, CharSequence methodName, CharSequence signature) {
        return poolMethod(poolClass(clazz), poolNameAndType(poolUtf8(methodName), poolUtf8(signature)));
    }

    public int poolNameAndType(int nameIndex, int typeIndex) {
        return poolRef(0x0C, nameIndex, typeIndex);
    }

    public int poolStringConst(int utf8Index) {
        putByte(0x8);
        putShort(utf8Index);
        return poolCount++;
    }

    public Utf8Appender poolUtf8() {
        putByte(0x01);
        utf8Appender.lenpos = position();
        utf8Appender.utf8len = 0;
        putShort(0);
        return utf8Appender;
    }

    public int poolUtf8(CharSequence cs) {
        int index = utf8Cache.keyIndex(cs);
        if (index > -1) {
            putByte(0x01);
            int n = cs.length();
            int pos = buf.position();
            putShort(0);
            for (int i = 0; i < n; ) {
                final char c = cs.charAt(i++);
                if (c < 128) {
                    putByte(c);
                } else {
                    if (c < 2048) {
                        putByte((char) (192 | c >> 6));
                        putByte((char) (128 | c & 63));
                    } else if (Character.isSurrogate(c)) {
                        i = encodeSurrogate(c, cs, i, n);
                    } else {
                        putByte((char) (224 | c >> 12));
                        putByte((char) (128 | c >> 6 & 63));
                        putByte((char) (128 | c & 63));
                    }
                }
            }
            buf.putShort(pos, (short) (buf.position() - pos - 2));
            utf8Cache.putAt(index, cs, poolCount);
            return this.poolCount++;
        }

        return utf8Cache.valueAt(index);
    }

    public void pop() {
        putByte(0x57);
    }

    public int position() {
        return buf.position();
    }

    public void putByte(int b) {
        if (buf.remaining() == 0) {
            resize();
        }
        buf.put((byte) b);
    }

    public void putITEM_Integer() {
        putByte(0x01);
    }

    public void putITEM_Long() {
        putByte(0x04);
    }

    public void putITEM_Object(int classIndex) {
        putByte(0x07);
        putShort(classIndex);
    }

    public void putITEM_Top() {
        putByte(0);
    }

    public void putLong(long value) {
        if (buf.remaining() < 4) {
            resize();
        }
        buf.putLong(value);
    }

    public void putShort(int v) {
        putShort((short) v);
    }

    public void putShort(int pos, int v) {
        buf.putShort(pos, (short) v);
    }

    public void putfield(int index) {
        putByte(181);
        putShort(index);
    }

    public void return_() {
        putByte(0xb1);
    }

    public void same_frame(int offset) {
        if (offset < 64) {
            putByte(offset);
        } else {
            putByte(251);
            putShort(offset);
        }
    }

    public void setJmp(int branch, int target) {
        putShort(branch, target - branch + 1);
    }

    public void setupPool() {
        // magic
        putInt(0xCAFEBABE);
        // version
        putInt(0x33);
        // skip pool count, write later when we know the value
        putShort(0);

        // add standard stuff
        objectClassIndex = poolClass(Object.class);
        defaultConstructorMethodIndex = poolMethod(objectClassIndex, poolNameAndType(
                defaultConstructorNameIndex = poolUtf8(""),
                defaultConstructorDescIndex = poolUtf8("()V"))
        );
        codeAttributeIndex = poolUtf8("Code");
    }

    public void startMethod(int nameIndex, int descriptorIndex, int maxStack, int maxLocal) {
        // access flags
        putShort(ACC_PUBLIC);
        // name index
        putShort(nameIndex);
        // descriptor index
        putShort(descriptorIndex);
        // attribute count
        putShort(1);

        // code
        putShort(codeAttributeIndex);

        // attribute len
        putInt(0);
        // come back to this later
        this.codeAttributeStart = position();
        // max stack
        putShort(maxStack);
        // max locals
        putShort(maxLocal);

        // code len
        putInt(0);
        this.codeStart = position();
    }

    public void startStackMapTables(int attributeNameIndex, int frameCount) {
        putShort(attributeNameIndex);
        this.stackMapTableCut = position();
        // length - we will come back here
        putInt(0);
        // number of entries
        putShort(frameCount);
    }

    private int encodeSurrogate(char c, CharSequence in, int pos, int hi) {
        int dword;
        if (Character.isHighSurrogate(c)) {
            if (hi - pos < 1) {
                putByte('?');
                return pos;
            } else {
                char c2 = in.charAt(pos++);
                if (Character.isLowSurrogate(c2)) {
                    dword = Character.toCodePoint(c, c2);
                } else {
                    putByte('?');
                    return pos;
                }
            }
        } else if (Character.isLowSurrogate(c)) {
            putByte('?');
            return pos;
        } else {
            dword = c;
        }
        putByte((char) (240 | dword >> 18));
        putByte((char) (128 | dword >> 12 & 63));
        putByte((char) (128 | dword >> 6 & 63));
        putByte((char) (128 | dword & 63));

        return pos;
    }

    private int genericGoto(int cmd) {
        putByte(cmd);
        int pos = position();
        putShort(0);
        return pos;
    }

    private void optimisedIO(int code0, int code1, int code2, int code3, int code, int value) {
        switch (value) {
            case 0:
                putByte(code0);
                break;
            case 1:
                putByte(code1);
                break;
            case 2:
                putByte(code2);
                break;
            case 3:
                putByte(code3);
                break;
            default:
                putByte(code);
                putByte(value);
                break;
        }
    }

    private int poolInterfaceMethod(int classIndex, int nameAndTypeIndex) {
        return poolRef(0x0B, classIndex, nameAndTypeIndex);
    }

    private int poolRef(int op, int name, int type) {
        putByte(op);
        putShort(name);
        putShort(type);
        return poolCount++;
    }

    private void putInt(int pos, int v) {
        buf.putInt(pos, v);
    }

    private void putInt(int v) {
        if (buf.remaining() < 4) {
            resize();
        }
        buf.putInt(v);
    }

    private void putShort(short v) {
        if (buf.remaining() < 2) {
            resize();
        }
        buf.putShort(v);
    }

    private void resize() {
        ByteBuffer b = ByteBuffer.allocate(buf.capacity() * 2).order(ByteOrder.BIG_ENDIAN);
        System.arraycopy(buf.array(), 0, b.array(), 0, buf.capacity());
        b.position(buf.position());
        buf = b;
    }

    public class Utf8Appender extends AbstractCharSink implements CharSink {
        private int utf8len = 0;
        private int lenpos;

        public int $() {
            putShort(lenpos, utf8len);
            return poolCount++;
        }

        @Override
        public Utf8Appender put(CharSequence cs) {
            int n = cs.length();
            for (int i = 0; i < n; i++) {
                BytecodeAssembler.this.putByte(cs.charAt(i));
            }
            utf8len += n;
            return this;
        }

        @Override
        public Utf8Appender put(char c) {
            BytecodeAssembler.this.putByte(c);
            utf8len++;
            return this;
        }

        @Override
        public CharSink put(char[] chars, int start, int len) {
            for (int i = 0; i < len; i++) {
                BytecodeAssembler.this.putByte(chars[i + start]);
            }
            utf8len += len;
            return this;
        }

        @Override
        public Utf8Appender put(int value) {
            super.put(value);
            return this;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy