// 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;
}
}
}