// 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.lang; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.*; import wybs.lang.Attribute; import wybs.lang.CompilationUnit; import wybs.lang.SyntacticElement; import wybs.util.AbstractCompilationUnit; import wyfs.lang.Content; import wyfs.lang.Path; import wyil.io.*; import wyil.lang.Bytecode.Expr; import wyil.lang.SyntaxTree.Location; /** * <p> * Provides an in-memory representation of a binary WyIL file. This is an * Intermediate Representation of Whiley (and potentially other) files, where * all aspects of name resolution and type checking are already resolved. * Furthermore, the Whiley Intermediate Language (WyIL) is a low-level, * register-based bytecode format where control-flow constructs are flattened * into unstructured control flow using conditional and unconditional branching. * </p> * <p> * The purpose of the WyIL file format is to simply the construction of * back-ends for the Whiley compiler, as well as simplifying the process of name * and type resolution in libraries. The format achieves a similar goal to that * Java ClassFile format, except that it is geared towards Whiley rather than * Java. * </p> * * @author David J. Pearce * */ public final class WyilFile extends AbstractCompilationUnit { // ========================================================================= // Content Type // ========================================================================= /** * Responsible for identifying and reading/writing WyilFiles. The normal * extension is ".wyil" for WyilFiles. */ public static final Content.Type<WyilFile> ContentType = new Content.Type<WyilFile>() { public Path.Entry<WyilFile> accept(Path.Entry<?> e) { if (e.contentType() == this) { return (Path.Entry<WyilFile>) e; } return null; } @Override public WyilFile read(Path.Entry<WyilFile> e, InputStream input) throws IOException { WyilFileReader reader = new WyilFileReader(e); WyilFile mi = reader.read(); return mi; } @Override public void write(OutputStream output, WyilFile module) throws IOException { WyilFileWriter writer = new WyilFileWriter(output); writer.write(module); } @Override public String toString() { return "Content-Type: wyil"; } @Override public String getSuffix() { return "wyil"; } }; // ========================================================================= // State // ========================================================================= /** * The list of blocks in this WyiFile. */ private final ArrayList<Block> blocks; // ========================================================================= // Constructors // ========================================================================= /** * Construct a WyilFile objects with a given identifier, originating * filename and list of declarations. * * @param mid * @param filename * @param declarations */ public WyilFile(Path.Entry<? extends CompilationUnit> entry) { super(entry); this.blocks = new ArrayList<>(); } // ========================================================================= // Accessors // ========================================================================= /** * Determines whether a declaration exists with the given name. * * @param name * @return */ public boolean hasName(String name) { for (Block d : blocks) { if(d instanceof Declaration) { Declaration nd = (Declaration) d; if(nd.name().equals(name)) { return true; } } } return false; } /** * Returns all declarations declared in this WyilFile. This list is * modifiable, and one can add new declarations to this WyilFile by adding * them to the returned list. * * @return */ public List<WyilFile.Block> blocks() { return blocks; } /** * Looks up a type declaration in this WyilFile with the given name; if none * exists, returns null. * * @param name * @return */ public Type type(String name) { for (Block d : blocks) { if(d instanceof Type) { Type td = (Type) d; if(td.name().equals(name)) { return td; } } } return null; } /** * Returns all type declarations in this WyilFile. Note that the returned * list is not modifiable. * * @param name * @return */ public Collection<WyilFile.Type> types() { ArrayList<Type> r = new ArrayList<>(); for (Block d : blocks) { if(d instanceof Type) { r.add((Type)d); } } return Collections.unmodifiableList(r); } /** * Looks up a constant declaration in this WyilFile with the given name; if none * exists, returns null. * * @param name * @return */ public Constant constant(String name) { for (Block d : blocks) { if(d instanceof Constant) { Constant cd = (Constant) d; if(cd.name().equals(name)) { return cd; } } } return null; } /** * Returns all constant declarations in this WyilFile. Note that the * returned list is not modifiable. * * @param name * @return */ public Collection<WyilFile.Constant> constants() { ArrayList<Constant> r = new ArrayList<>(); for (Block d : blocks) { if(d instanceof Constant) { r.add((Constant)d); } } return Collections.unmodifiableList(r); } /** * Returns all function or method declarations in this WyilFile with the * given name. Note that the returned list is not modifiable. * * @param name * @return */ public List<FunctionOrMethodOrProperty> functionOrMethodOrProperty(String name) { ArrayList<FunctionOrMethodOrProperty> r = new ArrayList<>(); for (Block d : blocks) { if (d instanceof FunctionOrMethodOrProperty) { FunctionOrMethodOrProperty m = (FunctionOrMethodOrProperty) d; if (m.name().equals(name)) { r.add(m); } } } return Collections.unmodifiableList(r); } /** * Looks up a function or method declaration in this WyilFile with the given * name and type; if none exists, returns null. * * @param name * @return */ public FunctionOrMethodOrProperty functionOrMethodOrProperty(String name, wyil.lang.Type.FunctionOrMethod ft) { for (Block d : blocks) { if (d instanceof FunctionOrMethodOrProperty) { FunctionOrMethodOrProperty md = (FunctionOrMethodOrProperty) d; if (md.name().equals(name) && md.type().equals(ft)) { return md; } } } return null; } /** * Returns all function or method declarations in this WyilFile. Note that * the returned list is not modifiable. * * @param name * @return */ public Collection<WyilFile.FunctionOrMethod> functionOrMethods() { ArrayList<FunctionOrMethod> r = new ArrayList<>(); for (Block d : blocks) { if(d instanceof FunctionOrMethod) { r.add((FunctionOrMethod)d); } } return Collections.unmodifiableList(r); } // ========================================================================= // Mutators // ========================================================================= public void replace(WyilFile.Block old, WyilFile.Block nuw) { for(int i=0;i!=blocks.size();++i) { if(blocks.get(i) == old) { blocks.set(i,nuw); return; } } } // ========================================================================= // Types // ========================================================================= /** * <p> * A block is an chunk of information within a WyIL file. For example, it * might be a declaration for a type, constant, function or method. However, * other kinds of block are possible, such as for storing documentation, * debug information, etc. * </p> * <p> * A block may have zero or more "attributes" associated with it. Attributes * provide meta-information about the block which, although not strictly * necessary for execution, are typically helpful. Such information includes * additional type information, variable naming information, etc. * </p> * * @author David J. Pearce * */ public static abstract class Block implements SyntacticElement { private final WyilFile parent; private final List<Attribute> attributes; public Block(WyilFile parent, Collection<Attribute> attributes) { this.parent = parent; this.attributes = new ArrayList<>(attributes); } public Block(WyilFile parent, Attribute[] attributes) { this.parent = parent; this.attributes = new ArrayList<>(Arrays.asList(attributes)); } /** * Get the WyIL file enclosing this block * * @return */ public WyilFile parent() { return parent; } @Override public List<Attribute> attributes() { return attributes; } @Override public <T extends Attribute> T attribute(Class<T> type) { for (Attribute a : attributes) { if (type.isInstance(a)) { return (T) a; } } return null; } } /** * A declaration is a named entity within a WyIL file, and is either a type, * constant, function or method declaration. * * @author David J. Pearce * */ public static abstract class Declaration extends Block { private String name; private List<Modifier> modifiers; private SyntaxTree tree; public Declaration(WyilFile parent, String name, Collection<Modifier> modifiers, Attribute... attributes) { super(parent, attributes); this.name = name; this.modifiers = new ArrayList<>(modifiers); this.tree = new SyntaxTree(this); } public Declaration(WyilFile parent, String name, Collection<Modifier> modifiers, Collection<Attribute> attributes) { super(parent, attributes); this.name = name; this.modifiers = new ArrayList<>(modifiers); this.tree = new SyntaxTree(this); } public String name() { return name; } public List<Modifier> modifiers() { return modifiers; } public boolean hasModifier(Modifier modifier) { return modifiers.contains(modifier); } public SyntaxTree getTree() { return tree; } } /** * A type declaration is a top-level block within a WyilFile that associates * a name with a given type. These names can be used within types, * constants, functions and methods in other WyIL files. Every type has an * optional invariant as well, which must hold true for all values of that * type. * * @author David J. Pearce * */ public static final class Type extends Declaration { private final wyil.lang.Type type; private final List<Location<Expr>> invariant; public Type(WyilFile parent, Collection<Modifier> modifiers, String name, wyil.lang.Type type, Attribute... attributes) { super(parent, name, modifiers, attributes); this.type = type; this.invariant = new ArrayList<>(); } public Type(WyilFile parent, Collection<Modifier> modifiers, String name, wyil.lang.Type type, Collection<Attribute> attributes) { super(parent, name, modifiers, attributes); this.type = type; this.invariant = new ArrayList<>(); } public wyil.lang.Type type() { return type; } /** * Get the list of expressions that make up the invariant of this * type. This list maybe empty, but it cannot be null. * * @return */ public List<Location<Expr>> getInvariant() { return invariant; } } /** * A constant declaration is a top-level block within a WyilFile that * associates a name with a given constant value. These names can be used * within expressions found in constants and functions and methods in other * WyIL files. Note that constants may not have cyclic dependencies (i.e. * they're value may not depend on itself). * * @author David J. Pearce * */ public static final class Constant extends Declaration { private final wyil.lang.Constant constant; public Constant(WyilFile parent, Collection<Modifier> modifiers, String name, wyil.lang.Constant constant, Attribute... attributes) { super(parent, name, modifiers, attributes); this.constant = constant; } public Constant(WyilFile parent, Collection<Modifier> modifiers, String name, wyil.lang.Constant constant, Collection<Attribute> attributes) { super(parent, name, modifiers, attributes); this.constant = constant; } public wyil.lang.Constant constant() { return constant; } } public static class FunctionOrMethodOrProperty extends Declaration { private final wyil.lang.Type.FunctionOrMethod type; /** * Expressions making up clauses of precondition */ private final List<SyntaxTree.Location<Bytecode.Expr>> precondition; public FunctionOrMethodOrProperty(WyilFile parent, Collection<Modifier> modifiers, String name, wyil.lang.Type.FunctionOrMethod type, Attribute... attributes) { super(parent, name, modifiers, attributes); this.type = type; this.precondition = new ArrayList<>(); } public FunctionOrMethodOrProperty(WyilFile parent, Collection<Modifier> modifiers, String name, wyil.lang.Type.FunctionOrMethod type, Collection<Attribute> attributes) { super(parent, name, modifiers, attributes); this.type = type; this.precondition = new ArrayList<>(); } public wyil.lang.Type.FunctionOrMethod type() { return type; } /** * Get the list of expressions that make up the precondition of this * function/method. This list maybe empty, but it cannot be null. * * @return */ public List<SyntaxTree.Location<Bytecode.Expr>> getPrecondition() { return precondition; } /** * Check whether this represents a function declaration or not. * * @return */ public boolean isFunction() { return type instanceof wyil.lang.Type.Function; } /** * Check whether this represents a method declaration or not. * * @return */ public boolean isMethod() { return type instanceof wyil.lang.Type.Method; } } public static final class Property extends FunctionOrMethodOrProperty { public Property(WyilFile parent, Collection<Modifier> modifiers, String name, wyil.lang.Type.Property type, Attribute... attributes) { super(parent, modifiers, name, type, attributes); } } public static final class FunctionOrMethod extends FunctionOrMethodOrProperty { /** * Expressions making up clauses of postcondition */ private final List<SyntaxTree.Location<Bytecode.Expr>> postcondition; /** * The function or method body (which can be null) */ private SyntaxTree.Location<Bytecode.Block> body; public FunctionOrMethod(WyilFile parent, Collection<Modifier> modifiers, String name, wyil.lang.Type.FunctionOrMethod type, Attribute... attributes) { super(parent, modifiers, name, type, attributes); this.postcondition = new ArrayList<>(); } public FunctionOrMethod(WyilFile parent, Collection<Modifier> modifiers, String name, wyil.lang.Type.FunctionOrMethod type, Collection<Attribute> attributes) { super(parent, modifiers, name, type, attributes); this.postcondition = new ArrayList<>(); } /** * Get the list of expressions that make up the postcondition of this * function/method. This list maybe empty, but it cannot be null. * * @return */ public List<SyntaxTree.Location<Bytecode.Expr>> getPostcondition() { return postcondition; } /** * Get the body of this function or method * * @return */ public SyntaxTree.Location<Bytecode.Block> getBody() { return body; } public void setBody(SyntaxTree.Location<Bytecode.Block> body) { this.body = body; } } }