// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz)
// All rights reserved.
//
// This software may be modified and distributed under the terms
// of the BSD license. See the LICENSE file for details.
package wyil.io;
import java.io.*;
import java.math.BigInteger;
import java.util.*;
import wybs.lang.Attribute;
import wybs.lang.NameID;
import wycc.util.Pair;
import wyfs.io.BinaryInputStream;
import wyfs.lang.Path;
import wyfs.util.Trie;
import wyil.lang.*;
import wyil.lang.Bytecode.Expr;
import wyil.lang.SyntaxTree.Location;
import wyil.util.AbstractBytecode;
/**
* Read a binary WYIL file from a byte stream and convert into the corresponding
* WyilFile object.
*
* @author David J. Pearce
*
*/
public final class WyilFileReader {
private static final char[] magic = { 'W', 'Y', 'I', 'L', 'F', 'I', 'L', 'E' };
private final Path.Entry<WyilFile> entry;
private final BinaryInputStream input;
private String[] stringPool;
private Path.ID[] pathPool;
private NameID[] namePool;
private Constant[] constantPool;
private Type[] typePool;
public WyilFileReader(Path.Entry<WyilFile> entry) throws IOException {
this.entry = entry;
this.input = new BinaryInputStream(entry.inputStream());
}
/**
* Construct a WyilFileReader to read a WyilFile in headless mode. That is,
* where the file is not associated with a Path.Entry.
*
* @param input
*/
public WyilFileReader(InputStream input) throws IOException {
this.entry = null;
this.input = new BinaryInputStream(input);
}
public void close() throws IOException {
input.close();
}
public WyilFile read() throws IOException {
for (int i = 0; i != 8; ++i) {
char c = (char) input.read_u8();
if (magic[i] != c) {
throw new IllegalArgumentException("invalid magic number");
}
}
// head blocker
int kind = input.read_uv();
int size = input.read_uv();
input.pad_u8();
if (kind != WyilFileWriter.BLOCK_Header) {
throw new IllegalArgumentException("header block must come first");
}
int majorVersion = input.read_uv();
int minorVersion = input.read_uv();
int stringPoolCount = input.read_uv();
int pathPoolCount = input.read_uv();
int namePoolCount = input.read_uv();
int typePoolCount = input.read_uv();
int constantPoolCount = input.read_uv();
int numBlocks = input.read_uv();
readStringPool(stringPoolCount);
readPathPool(pathPoolCount);
readNamePool(namePoolCount);
readTypePool(typePoolCount);
readConstantPool(constantPoolCount);
input.pad_u8();
return readModule();
}
/**
* Read the list of strings which constitute the string pool. Each entry is
* formated like so:
*
* <pre>
* +-----------------+
* | uv : len |
* +-----------------+
* | u8[len] : bytes |
* +-----------------+
* </pre>
*
* The encoding for each string item is UTF-8.
*
* @param count
* @throws IOException
*/
private void readStringPool(int count) throws IOException {
final String[] myStringPool = new String[count];
for (int i = 0; i != count; ++i) {
int length = input.read_uv();
try {
byte[] data = new byte[length];
input.read(data);
String str = new String(data, 0, length, "UTF-8");
myStringPool[i] = str;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 Charset not supported?");
}
}
stringPool = myStringPool;
}
/**
* Read the list of paths which constitute the path pool. Each entry is
* formated like so:
*
* <pre>
* +-----------------+
* | uv : parent |
* +-----------------+
* | uv : stringIdx |
* +-----------------+
* </pre>
*
* Each entry is a child of some parent entry, with index zero being
* automatically designated the "root". The <code>stringIdx</code>
* corresponds to an index in the string pool.
*
* @param count
* @throws IOException
*/
private void readPathPool(int count) throws IOException {
final Path.ID[] myPathPool = new Path.ID[count];
myPathPool[0] = Trie.ROOT;
for (int i = 1; i != count; ++i) {
int parent = input.read_uv();
int stringIndex = input.read_uv();
Path.ID id;
id = myPathPool[parent];
id = id.append(stringPool[stringIndex]);
myPathPool[i] = id;
}
pathPool = myPathPool;
}
/**
* Read the list of names which constitute the name pool. Each entry is
* formated like so:
*
* <pre>
* +-----------------+
* | uv : pathIdx |
* +-----------------+
* | uv : stringIdx |
* +-----------------+
* </pre>
*
* Each entry consists of a path component and a name component, both of
* which index the path and string pools (respectively).
*
* @param count
* @throws IOException
*/
private void readNamePool(int count) throws IOException {
final NameID[] myNamePool = new NameID[count];
for (int i = 0; i != count; ++i) {
// int kind = input.read_uv();
int pathIndex = input.read_uv();
int nameIndex = input.read_uv();
Path.ID id = pathPool[pathIndex];
String name = stringPool[nameIndex];
myNamePool[i] = new NameID(id, name);
}
namePool = myNamePool;
}
/**
* Read the list of constants which constitute the constant pool. Each entry
* is formated like so:
*
* <pre>
* +-----------------+
* | uv : code |
* +-----------------+
* | ... payload ... |
* +-----------------+
* </pre>
*
* Here, the size of the payload is determined by the constant code. In some
* cases, there is no payload (e.g. for the constant NULL). In other case,
* there can be numerous bytes contained in the payload (e.g. for an Integer
* constant).
*
* @param count
* @throws IOException
*/
private void readConstantPool(int count) throws IOException {
final Constant[] myConstantPool = new Constant[count];
for (int i = 0; i != count; ++i) {
int code = input.read_uv();
Constant constant;
switch (code) {
case WyilFileWriter.CONSTANT_Null:
constant = Constant.Null;
break;
case WyilFileWriter.CONSTANT_False:
constant = Constant.False;
break;
case WyilFileWriter.CONSTANT_True:
constant = Constant.True;
break;
case WyilFileWriter.CONSTANT_Byte: {
byte val = (byte) input.read_u8();
constant = new Constant.Byte(val);
break;
}
case WyilFileWriter.CONSTANT_Int: {
int len = input.read_uv();
byte[] bytes = new byte[len];
input.read(bytes);
BigInteger bi = new BigInteger(bytes);
constant = new Constant.Integer(bi);
break;
}
case WyilFileWriter.CONSTANT_Array: {
int len = input.read_uv();
ArrayList<Constant> values = new ArrayList<>();
for (int j = 0; j != len; ++j) {
int index = input.read_uv();
values.add(myConstantPool[index]);
}
constant = new Constant.Array(values);
break;
}
case WyilFileWriter.CONSTANT_Record: {
int len = input.read_uv();
HashMap<String, Constant> tvs = new HashMap<>();
for (int j = 0; j != len; ++j) {
int fieldIndex = input.read_uv();
int constantIndex = input.read_uv();
String str = stringPool[fieldIndex];
tvs.put(str, myConstantPool[constantIndex]);
}
constant = new Constant.Record(tvs);
break;
}
case WyilFileWriter.CONSTANT_Function:
case WyilFileWriter.CONSTANT_Method: {
int typeIndex = input.read_uv();
int nameIndex = input.read_uv();
Type.FunctionOrMethod t = (Type.FunctionOrMethod) typePool[typeIndex];
NameID name = namePool[nameIndex];
constant = new Constant.FunctionOrMethod(name, t);
break;
}
case WyilFileWriter.CONSTANT_Type: {
int typeIndex = input.read_uv();
constant = new Constant.Type(typePool[typeIndex]);
break;
}
default:
throw new RuntimeException("Unknown constant encountered: " + code);
}
myConstantPool[i] = constant;
}
constantPool = myConstantPool;
}
/**
* <p>
* Read the list of types which constitute the type pool. Each entry is
* currently formated using the binary representation of automata.
* </p>
*
* <b>NOTE:</b> Eventually, automata need to be properly integrated with the
* WyIL file format to avoid duplication.
*
* @param count
* @throws IOException
*/
private void readTypePool(int count) throws IOException {
final Type[] myTypePool = new Type[count];
for (int i = 0; i != count; ++i) {
int code = input.read_uv();
Type type;
switch (code) {
case WyilFileWriter.TYPE_Any:
type = Type.T_ANY;
break;
case WyilFileWriter.TYPE_Void:
type = Type.T_VOID;
break;
case WyilFileWriter.TYPE_Null:
type = Type.T_NULL;
break;
case WyilFileWriter.TYPE_Bool:
type = Type.T_BOOL;
break;
case WyilFileWriter.TYPE_Byte:
type = Type.T_BYTE;
break;
case WyilFileWriter.TYPE_Int:
type = Type.T_INT;
break;
case WyilFileWriter.TYPE_Type:
type = Type.T_META;
break;
case WyilFileWriter.TYPE_Nominal: {
int nameIndex = input.read_uv();
NameID name = namePool[nameIndex];
type = Type.Nominal(name);
break;
}
case WyilFileWriter.TYPE_Reference: {
int typeIndex = input.read_uv();
int lifetimeIndex = input.read_uv();
Type element = myTypePool[typeIndex];
String lifetime = stringPool[lifetimeIndex];
type = Type.Reference(lifetime,element);
break;
}
case WyilFileWriter.TYPE_Array: {
int typeIndex = input.read_uv();
Type element = myTypePool[typeIndex];
type = Type.Array(element);
break;
}
case WyilFileWriter.TYPE_Record: {
int numFields = input.read_uv();
boolean isOpen = input.read_bit();
Pair<Type,String>[] fields = new Pair[numFields];
for(int j=0;j!=numFields;++j) {
int stringIndex = input.read_uv();
int typeIndex = input.read_uv();
String fieldName = stringPool[stringIndex];
Type fieldType = myTypePool[typeIndex];
fields[j] = new Pair<>(fieldType,fieldName);
}
type = Type.Record(isOpen,fields);
break;
}
case WyilFileWriter.TYPE_Property: {
Type[] parameters = readTypes(myTypePool);
type = Type.Property(parameters);
break;
}
case WyilFileWriter.TYPE_Function: {
Type[] parameters = readTypes(myTypePool);
Type[] returns = readTypes(myTypePool);
type = Type.Function(parameters, returns);
break;
}
case WyilFileWriter.TYPE_Method: {
String[] lifetimeParameters = readStrings();
String[] contextLifetimes = readStrings();
Type[] parameters = readTypes(myTypePool);
Type[] returns = readTypes(myTypePool);
type = Type.Method(lifetimeParameters, contextLifetimes, parameters, returns);
break;
}
case WyilFileWriter.TYPE_Union: {
Type[] elements = readTypes(myTypePool);
type = Type.Union(elements);
break;
}
case WyilFileWriter.TYPE_Intersection: {
Type[] elements = readTypes(myTypePool);
type = Type.Intersection(elements);
break;
}
case WyilFileWriter.TYPE_Negation: {
int typeIndex = input.read_uv();
Type element = myTypePool[typeIndex];
type = Type.Negation(element);
break;
}
default:
throw new RuntimeException("Unknown type encountered: " + code);
}
myTypePool[i] = type;
}
typePool = myTypePool;
}
private Type[] readTypes(Type[] myTypePool) throws IOException {
int count = input.read_uv();
Type[] types = new Type[count];
for(int i=0;i!=count;++i) {
int typeIndex = input.read_uv();
Type type = myTypePool[typeIndex];
types[i] = type;
}
return types;
}
private String[] readStrings() throws IOException {
int count = input.read_uv();
String[] strings = new String[count];
for(int i=0;i!=count;++i) {
int stringIndex = input.read_uv();
String string = stringPool[stringIndex];
strings[i] = string;
}
return strings;
}
/**
* Read a module contained within a given WyIL file. The format is:
*
* <pre>
* +-----------------+
* | uv : kind |
* +-----------------+
* | uv : size |
* +-----------------+
* ~~~~~~~~ u8 ~~~~~~~
* +-----------------+
* | uv : pathIDX |
* +-----------------+
* | uv : nModifiers |
* +-----------------+
* | uv : nBlocks |
* +-----------------+
* ~~~~~~~~ u8 ~~~~~~~
* +-----------------+
* | Block[nBlocks] |
* +-----------------+
* </pre>
*
* Here, the <code>pathIDX</code> gives the path identifiers for the module
* in question.
*
* @throws IOException
*/
private WyilFile readModule() throws IOException {
int kind = input.read_uv(); // block identifier
int size = input.read_uv();
input.pad_u8();
int pathIdx = input.read_uv();
int numModifiers = input.read_uv(); // unused
int numBlocks = input.read_uv();
input.pad_u8();
WyilFile wyilFile = new WyilFile(entry);
for (int i = 0; i != numBlocks; ++i) {
readModuleBlock(wyilFile);
}
return wyilFile;
}
private void readModuleBlock(WyilFile parent) throws IOException {
int kind = input.read_uv();
int size = input.read_uv();
input.pad_u8();
switch (kind) {
case WyilFileWriter.BLOCK_Constant:
readConstantBlock(parent);
break;
case WyilFileWriter.BLOCK_Type:
readTypeBlock(parent);
break;
case WyilFileWriter.BLOCK_Property:
readPropertyBlock(parent);
break;
case WyilFileWriter.BLOCK_Function:
case WyilFileWriter.BLOCK_Method:
readFunctionOrMethodBlock(parent);
break;
default:
throw new RuntimeException("unknown module block encountered (" + kind + ")");
}
input.pad_u8();
}
/**
* Read a BLOCK_Constant, that is a WyIL module block representing a
* constant declaration. The format is:
*
* <pre>
* +-----------------+
* | uv : nameIdx |
* +-----------------+
* | uv : Modifiers |
* +-----------------+
* | uv : constIdx |
* +-----------------+
* ~~~~~~~ u8 ~~~~~~~~
* </pre>
*
* The <code>nameIdx</code> is an index into the <code>stringPool</code>
* representing the declaration's name, whilst <code>constIdx</code> is an
* index into the <code>constantPool</code> representing the constant value
* itself.
*
* @throws IOException
*/
private void readConstantBlock(WyilFile parent) throws IOException {
int nameIdx = input.read_uv();
int modifiers = input.read_uv();
int constantIdx = input.read_uv();
WyilFile.Block block = new WyilFile.Constant(parent, generateModifiers(modifiers), stringPool[nameIdx],
constantPool[constantIdx]);
parent.blocks().add(block);
}
/**
* Read a BLOCK_Type, that is a WyIL module block representing a type
* declaration. The format is:
*
* <pre>
* +------------------------+
* | uv : nameIdx |
* +------------------------+
* | uv : Modifiers |
* +------------------------+
* | uv : typeIdx |
* +------------------------+
* | uv : nInvariants |
* +------------------------+
* | uv[nInvariants] |
* +------------------------+
* | SyntaxTree |
* +------------------------+
* ~~~~~~~~~~ u8 ~~~~~~~~~~~~
* </pre>
*
* The <code>nameIdx</code> is an index into the <code>stringPool</code>
* representing the declaration's name, whilst <code>typeIdx</code> is an
* index into the <code>typePool</code> representing the type itself.
*
* @throws IOException
*/
private void readTypeBlock(WyilFile parent) throws IOException {
int nameIdx = input.read_uv();
int modifiers = input.read_uv();
int typeIdx = input.read_uv();
int nInvariants = input.read_uv();
//
Collection<Modifier> mods = generateModifiers(modifiers);
String name = stringPool[nameIdx];
Type type = typePool[typeIdx];
//
int[] invariant = new int[nInvariants];
for (int i = 0; i != nInvariants; ++i) {
invariant[i] = input.read_uv();
}
WyilFile.Type decl = new WyilFile.Type(parent, mods, name, type);
SyntaxTree tree = readSyntaxTree(decl);
//
for (int i = 0; i != nInvariants;++i) {
Location<Bytecode.Expr> expr = (Location<Expr>) tree.getLocation(invariant[i]);
decl.getInvariant().add(expr);
}
parent.blocks().add(decl);
}
/**
* Read a BLOCK_Function or BLOCK_Method, that is a WyIL module block
* representing a function or method declaration. The format is:
*
* <pre>
* +------------------------+
* | uv : nameIdx |
* +------------------------+
* | uv : Modifiers |
* +------------------------+
* | uv : typeIdx |
* +------------------------+
* | uv : nPreconditions |
* +------------------------+
* | uv : nPostconditions |
* +------------------------+
* | uv[nPreconditions] |
* +------------------------+
* | uv[nPostconditions] |
* +------------------------+
* | uv : body |
* +------------------------+
* | SyntaTree |
* +------------------------+
* ~~~~~~~~~~ u8 ~~~~~~~~~~~~
* </pre>
*
* The <code>nameIdx</code> is an index into the <code>stringPool</code>
* representing the declaration's name, whilst <code>typeIdx</code> is an
* index into the <code>typePool</code> representing the function or method
* type itself.
*
* @throws IOException
*/
private void readFunctionOrMethodBlock(WyilFile parent) throws IOException {
int nameIdx = input.read_uv();
int modifiers = input.read_uv();
int typeIdx = input.read_uv();
int nPreconditions = input.read_uv();
int nPostconditions = input.read_uv();
//
Collection<Modifier> mods = generateModifiers(modifiers);
String name = stringPool[nameIdx];
Type.FunctionOrMethod type = (Type.FunctionOrMethod) typePool[typeIdx];
//
WyilFile.FunctionOrMethod decl = new WyilFile.FunctionOrMethod(parent, mods, name, type);
int[] precondition = new int[nPreconditions];
for (int i = 0; i != nPreconditions; ++i) {
precondition[i] = input.read_uv();
}
int[] postcondition = new int[nPostconditions];
for (int i = 0; i != nPostconditions; ++i) {
postcondition[i] = input.read_uv();
}
//
int body = input.read_uv();
//
readSyntaxTree(decl);
SyntaxTree tree = decl.getTree();
//
for (int i = 0; i != nPreconditions; ++i) {
Location<Bytecode.Expr> expr = (Location<Expr>) tree.getLocation(precondition[i]);
decl.getPrecondition().add(expr);
}
//
for (int i = 0; i != nPostconditions; ++i) {
Location<Bytecode.Expr> expr = (Location<Expr>) tree.getLocation(postcondition[i]);
decl.getPostcondition().add(expr);
}
//
Location<Bytecode.Block> loc = (Location<Bytecode.Block>) tree.getLocation(body);
decl.setBody(loc);
//
parent.blocks().add(decl);
}
private void readPropertyBlock(WyilFile parent) throws IOException {
int nameIdx = input.read_uv();
int modifiers = input.read_uv();
int typeIdx = input.read_uv();
int nPreconditions = input.read_uv();
//
Collection<Modifier> mods = generateModifiers(modifiers);
String name = stringPool[nameIdx];
Type.Property type = (Type.Property) typePool[typeIdx];
//
WyilFile.Property decl = new WyilFile.Property(parent, mods, name, type);
int[] precondition = new int[nPreconditions];
for (int i = 0; i != nPreconditions; ++i) {
precondition[i] = input.read_uv();
}
//
readSyntaxTree(decl);
SyntaxTree tree = decl.getTree();
//
for (int i = 0; i != nPreconditions; ++i) {
Location<Bytecode.Expr> expr = (Location<Expr>) tree.getLocation(precondition[i]);
decl.getPrecondition().add(expr);
}
//
parent.blocks().add(decl);
}
/**
* Convert an bit pattern representing various modifiers into instances of
* <code>Modifier</code>.
*
* @param modifiers
* @return
*/
private Collection<Modifier> generateModifiers(int modifiers) {
ArrayList<Modifier> mods = new ArrayList<>();
// first, protection modifiers
switch (modifiers & WyilFileWriter.MODIFIER_PROTECTION_MASK) {
case WyilFileWriter.MODIFIER_Public:
mods.add(Modifier.PUBLIC);
break;
case WyilFileWriter.MODIFIER_Private:
mods.add(Modifier.PRIVATE);
break;
default:
throw new RuntimeException("Unknown modifier");
}
// second, mangle modifiers
switch (modifiers & WyilFileWriter.MODIFIER_MANGLE_MASK) {
case WyilFileWriter.MODIFIER_Native:
mods.add(Modifier.NATIVE);
break;
case WyilFileWriter.MODIFIER_Export:
mods.add(Modifier.EXPORT);
break;
}
return mods;
}
/**
* Read a syntax tree from the output stream. The format
* of a syntax tree is one of the following:
*
* <pre>
* +-------------------+
* | uv : nLocs |
* +-------------------+
* | Locations[nLocs] |
* +-------------------+
* </pre>
*
*
* @param parent
* @return
* @throws IOException
*/
private SyntaxTree readSyntaxTree(WyilFile.Declaration parent) throws IOException {
SyntaxTree tree = parent.getTree();
int nLocs = input.read_uv();
for(int i=0;i!=nLocs;++i) {
tree.getLocations().add(readLocation(tree));
}
return tree;
}
/**
* Read details of a Location from the input stream. The format of a
* location is:
*
* <pre>
* +-------------------+
* | uv : nTypes |
* +-------------------+
* | uv[] : typeIdxs |
* +-------------------+
* | uv : nAttrs |
* +-------------------+
* | Bytecode |
* +-------------------+
* | Attribute[nAttrs] |
* +-------------------+
* </pre>
*
* @param output
* @throws IOException
*/
private SyntaxTree.Location<?> readLocation(SyntaxTree tree) throws IOException {
int nTypes = input.read_uv();
Type[] types = new Type[nTypes];
for (int i = 0; i != types.length; ++i) {
int typeIdx = input.read_uv();
types[i] = typePool[typeIdx];
}
int nAttrs = input.read_uv();
Bytecode bytecode = readBytecode();
//
List<Attribute> attributes = new ArrayList<>();
//
return new SyntaxTree.Location<>(tree, types, bytecode, attributes);
}
/**
* <p>
* REad a given bytecode whose format is currently given as follows:
* </p>
*
* <pre>
* +-------------------+
* | u8 : opcode |
* +-------------------+
* | uv : nAttrs |
* +-------------------+
* | Attribute[nAttrs] |
* +-------------------+
* ...
* </pre>
*
* <p>
* <b>NOTE:</b> The intention is to support a range of different bytecode
* formats in order to optimise the common cases. For example, when there
* are no targets, no operands, no types, etc. Furthermore, when the size of
* items can be reduced from uv to u4, etc.
* </p>
*/
private Bytecode readBytecode() throws IOException {
int opcode = input.read_u8();
int nAttrs = input.read_uv();
// FIXME: read attributes!
Bytecode.Schema schema = AbstractBytecode.schemas[opcode];
// First, read and validate all operands, groups and blocks
int[] operands = readOperands(schema);
int[][] groups = readOperandGroups(schema);
int[] blocks = readBlocks(schema);
// Second, read all extras
Object[] extras = readExtras(schema);
// Finally, create the bytecode
return schema.construct(opcode,operands,groups,blocks,extras);
}
private int[] readOperands(Bytecode.Schema schema) throws IOException {
switch(schema.getOperands()) {
case ZERO:
// do nout
return null;
case ONE:
int o = input.read_uv();
return new int[] { o };
case TWO:
int o1 = input.read_uv();
int o2 = input.read_uv();
return new int[] { o1, o2 };
case MANY:
default:
return readUnboundArray();
}
}
private int[][] readOperandGroups(Bytecode.Schema schema) throws IOException {
switch(schema.getOperandGroups()) {
case ZERO:
// do nout
return null;
case ONE:
int[] o = readUnboundArray();
return new int[][] { o };
case TWO:
int[] o1 = readUnboundArray();
int[] o2 = readUnboundArray();
return new int[][] { o1, o2 };
case MANY:
default:
int size = input.read_uv();
int[][] os = new int[size][];
for(int i=0;i!=size;++i) {
os[i] = readUnboundArray();
}
return os;
}
}
private int[] readBlocks(Bytecode.Schema schema) throws IOException {
switch(schema.getBlocks()) {
case ZERO:
// do nout
return null;
case ONE:
int o = input.read_uv();
return new int[] { o };
case TWO:
int o1 = input.read_uv();
int o2 = input.read_uv();
return new int[] { o1, o2 };
case MANY:
default:
return readUnboundArray();
}
}
/**
* Read the list of extra components defined by a given bytecode schema.
* Each extra is interpreted in a slightly different fashion.
*
* @param schema
* @param labels
* @return
* @throws IOException
*/
private Object[] readExtras(Bytecode.Schema schema)
throws IOException {
Bytecode.Extras[] extras = schema.extras();
Object[] results = new Object[extras.length];
for(int i=0;i!=extras.length;++i) {
switch(extras[i]) {
case CONSTANT: {
int constIdx = input.read_uv();
results[i] = constantPool[constIdx];
break;
}
case STRING: {
int stringIdx = input.read_uv();
results[i] = stringPool[stringIdx];
break;
}
case NAME: {
int nameIdx = input.read_uv();
results[i] = namePool[nameIdx];
break;
}
case TYPE: {
int typeIdx = input.read_uv();
results[i] = typePool[typeIdx];
break;
}
case STRING_ARRAY: {
int nStrings = input.read_uv();
String[] strings = new String[nStrings];
for(int j=0;j!=nStrings;++j) {
int stringIdx = input.read_uv();
strings[j] = stringPool[stringIdx];
}
results[i] = strings;
break;
}
case SWITCH_ARRAY: {
// This is basically a special case just for the switch
// statement.
int nPairs = input.read_uv();
Bytecode.Case[] pairs = new Bytecode.Case[nPairs];
for(int j=0;j!=nPairs;++j) {
int block = input.read_uv();
int nConstants = input.read_uv();
Constant[] constants = new Constant[nConstants];
for(int k=0;k!=nConstants;++k) {
int constIdx = input.read_uv();
constants[k] = constantPool[constIdx];
}
pairs[j] = new Bytecode.Case(block,constants);
}
results[i] = pairs;
break;
}
default:
throw new RuntimeException("unknown bytecode extra encountered: " + extras[i]);
}
}
return results;
}
private int[] readUnboundArray() throws IOException {
int size = input.read_uv();
int[] array = new int[size];
for(int i=0;i!=size;++i) {
array[i] = input.read_uv();
}
return array;
}
private int[] readRegisters(int nRegisters) throws IOException {
int[] bs = new int[nRegisters];
for (int i = 0; i != nRegisters; ++i) {
bs[i] = input.read_uv();
}
return bs;
}
private Type[] readTypes(int nTypes) throws IOException {
Type[] types = new Type[nTypes];
for (int i = 0; i != nTypes; ++i) {
int typeIndex = input.read_uv();
types[i] = typePool[typeIndex];
}
return types;
}
}