io.questdb.std.BytecodeAssembler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core Show documentation
Show all versions of core Show documentation
QuestDB is High Performance Time Series Database
The newest version!
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* 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().$("Failed to 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;
}
}
}