org.wso2.ballerinalang.compiler.bir.writer.BIRBinaryWriter.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.ballerinalang.compiler.bir.writer.BIRBinaryWriter.java

Source

/*
 *  Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. 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.wso2.ballerinalang.compiler.bir.writer;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.ballerinalang.compiler.BLangCompilerException;
import org.wso2.ballerinalang.compiler.bir.model.BIRNode;
import org.wso2.ballerinalang.compiler.bir.model.BIRNode.BIRBasicBlock;
import org.wso2.ballerinalang.compiler.bir.writer.CPEntry.IntegerCPEntry;
import org.wso2.ballerinalang.compiler.bir.writer.CPEntry.PackageCPEntry;
import org.wso2.ballerinalang.compiler.bir.writer.CPEntry.StringCPEntry;
import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType;
import org.wso2.ballerinalang.compiler.semantics.model.types.BType;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * Serialize BIR into a binary format.
 *
 * @since 0.980.0
 */
public class BIRBinaryWriter {
    private static final byte[] BIR_MAGIC = { (byte) 0xba, (byte) 0x10, (byte) 0xc0, (byte) 0xde };
    private static final int BIR_VERSION = 1;

    private ConstantPool cp = new ConstantPool();

    public byte[] write(BIRNode.BIRPackage birPackage) {
        ByteBuf birbuf = Unpooled.buffer();

        // Write the package details in the form of constant pool entry
        int orgCPIndex = addStringCPEntry(birPackage.org.value);
        int nameCPIndex = addStringCPEntry(birPackage.name.value);
        int versionCPIndex = addStringCPEntry(birPackage.version.value);
        int pkgIndex = cp.addCPEntry(new PackageCPEntry(orgCPIndex, nameCPIndex, versionCPIndex));
        birbuf.writeInt(pkgIndex);

        // Write functions
        writeFunctions(birbuf, birPackage.functions);

        // Write the constant pool entries.
        // TODO Only one constant pool is available for now. This will change in future releases
        // TODO e.g., strtab, shstrtab, rodata.
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (DataOutputStream dataOut = new DataOutputStream(baos)) {
            dataOut.write(BIR_MAGIC);
            dataOut.writeInt(BIR_VERSION);
            writeCP(dataOut);
            dataOut.write(birbuf.nioBuffer().array(), 0, birbuf.nioBuffer().limit());
            return baos.toByteArray();
        } catch (IOException e) {
            throw new BLangCompilerException("failed to serialize the bir", e);
        }
    }

    // private methods

    private void writeFunctions(ByteBuf buf, List<BIRNode.BIRFunction> birFunctionList) {
        buf.writeInt(birFunctionList.size());
        birFunctionList.forEach(func -> writeFunction(buf, func));
    }

    private void writeFunction(ByteBuf buf, BIRNode.BIRFunction birFunction) {
        // Function name CP Index
        buf.writeInt(addStringCPEntry(birFunction.name.value));
        // Function definition or a declaration
        // Non-zero value means this is a function declaration e.g. extern function
        buf.writeByte(birFunction.isDeclaration ? 1 : 0);
        // Visibility
        buf.writeByte(birFunction.visibility.value());

        // Function type as a CP Index
        buf.writeInt(addFuncSignature(birFunction.type));
        // Arg count
        buf.writeInt(birFunction.argsCount);
        // Local variables
        buf.writeInt(birFunction.localVars.size());
        for (BIRNode.BIRVariableDcl localVar : birFunction.localVars) {
            buf.writeByte(localVar.kind.getValue());
            buf.writeInt(addStringCPEntry(localVar.type.getDesc()));
            buf.writeInt(addStringCPEntry(localVar.name.value));
        }

        // Write basic blocks
        writeBasicBlocks(buf, birFunction.basicBlocks);
    }

    private void writeBasicBlocks(ByteBuf buf, List<BIRBasicBlock> birBBList) {
        BIRInstructionWriter insWriter = new BIRInstructionWriter(buf, cp);
        insWriter.writeBBs(birBBList);
    }

    private int addStringCPEntry(String value) {
        return cp.addCPEntry(new StringCPEntry(value));
    }

    private void writeCP(DataOutputStream buf) throws IOException {
        CPEntry[] cpEntries = cp.getConstPoolEntries();
        // TODO The length should be available in the const-pool section header.
        buf.writeInt(cpEntries.length);
        for (CPEntry cpEntry : cpEntries) {
            buf.writeByte(cpEntry.entryType.value);
            switch (cpEntry.entryType) {
            case CP_ENTRY_INTEGER:
                buf.writeLong(((IntegerCPEntry) cpEntry).value);
                break;
            case CP_ENTRY_BOOLEAN:
                buf.writeBoolean(((CPEntry.BooleanCPEntry) cpEntry).value);
                break;
            case CP_ENTRY_STRING:
                StringCPEntry stringCPEntry = (StringCPEntry) cpEntry;
                byte[] strBytes = stringCPEntry.value.getBytes(StandardCharsets.UTF_8);
                buf.writeInt(strBytes.length);
                buf.write(strBytes);
                break;
            case CP_ENTRY_PACKAGE:
                PackageCPEntry pkgCPEntry = (PackageCPEntry) cpEntry;
                buf.writeInt(pkgCPEntry.orgNameCPIndex);
                buf.writeInt(pkgCPEntry.pkgNameCPIndex);
                buf.writeInt(pkgCPEntry.versionCPIndex);
                break;
            default:
                throw new IllegalStateException(
                        "unsupported constant pool entry type: " + cpEntry.entryType.name());
            }
        }
    }

    private int addFuncSignature(BInvokableType funcType) {
        String funcSig = generateFunctionSig(funcType.paramTypes, funcType.retType);
        return this.cp.addCPEntry(new StringCPEntry(funcSig));
    }

    private String generateSig(List<BType> types) {
        StringBuilder builder = new StringBuilder();
        types.forEach(t -> builder.append(t.getDesc()));
        return builder.toString();
    }

    private String generateFunctionSig(List<BType> paramTypes, BType retType) {
        return "(" + generateSig(paramTypes) + ")(" + retType.getDesc() + ")";
    }
}