/*
* $Id: ByteCompiler.java,v 1.14 2002/09/16 08:05:03 jkl Exp $
*
* Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
*
* Use is subject to license terms, as defined in
* Anvil Sofware License, Version 1.1. See LICENSE
* file, or http://njet.org/license-1.1.txt
*/
package anvil.script.compiler;
import anvil.core.Any;
import anvil.core.AnyPattern;
import anvil.core.AnyRange;
import anvil.core.Array;
import anvil.core.Register;
import anvil.core.reflect.Reflection;
import anvil.Location;
import anvil.Log;
import anvil.doc.Doc;
import anvil.server.Address;
import anvil.server.ZoneClassLoader;
import anvil.server.Zone;
import anvil.server.CompilerPreferences;
import anvil.script.CompilableFunction;
import anvil.script.ClassType;
import anvil.script.Type;
import anvil.script.expression.Node;
import anvil.script.Smith;
import anvil.script.Module;
import anvil.script.ModuleEnvelope;
import anvil.script.expression.Node;
import anvil.script.statements.FunctionStatement;
import anvil.script.statements.SwitchStatement;
import anvil.java.util.BindingEnumeration;
import anvil.util.Conversions;
import anvil.java.util.Hashlist;
import anvil.java.util.HashlistIterator;
import java.io.File;
import java.io.FileOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Stack;
import anvil.codec.Code;
import anvil.codec.ClassRoom;
import anvil.codec.Method;
import anvil.codec.Field;
import anvil.codec.CodecConstants;
import anvil.codec.ConstantPool;
import anvil.codec.Source;
/**
* class ByteCompiler
*
* @author: Jani Lehtim�ki
*/
public class ByteCompiler implements CodecConstants
{
public static final int L_THIS = 0;
public static final int L_CONTEXT = 0;
public static final int L_FRAME = 0;
public String TYPE_MODULE;
public static final String TYPE_OBJECT = "java/lang/Object";
public static final String TYPE_ANY = "anvil/core/Any";
public static final String TYPE_ANY_OP = "anvil/core/Any$Op";
public static final String TYPE_ANY_ARRAY = "[Lanvil/core/Any;";
public static final String TYPE_CONTEXT = "anvil/script/Context";
public static final String TYPE_SYMBOLS = "anvil/core/Symbols";
public static final String TYPE_FUNCTION = "anvil/script/Function";
public static final String TYPE_COMPILED_SCRIPT = "anvil/script/compiler/CompiledModule";
public static final String TYPE_STACKFRAME = "anvil/script/StackFrame";
public static final int GET = 0;
public static final int GET_BOOLEAN = 1;
public static final int SET = 2;
public static final int CHECK = 3;
public static final int DELETE = 4;
private Smith _smith;
private ModuleEnvelope _envelope;
private CompilerPreferences _prefs;
private boolean _debug;
private Writer _writer;
private Hashlist _constants = new Hashlist();
private Hashlist _symbols = new Hashlist();
private Hashlist _texts = new Hashlist();
private Hashlist _reflections = new Hashlist();
private Vector _switches = new Vector();
private StringBuffer _text = new StringBuffer();
private ClassRoom _scriptclass;
private ClassRoom _class;
private ConstantPool _pool;
private Code _code;
private Stack _classes = new Stack();
private Stack _codes = new Stack();
public Field _f_const;
public Field _f_text;
public Field _f_switch;
public ByteCompiler(Smith smith, ModuleEnvelope envelope)
{
_smith = smith;
_envelope = envelope;
Zone zone = envelope.getAddress().getZone();
_prefs = zone.getCompilerPreferences();
_debug = zone.getDebug();
}
public String getClassname()
{
return _envelope.getClassname();
}
public Module getModule()
{
return _envelope.getModule();
}
public ClassRoom getModuleClass()
{
return _scriptclass;
}
public ClassRoom getClassRoom()
{
return _class;
}
public ConstantPool getPool()
{
return _pool;
}
public void pushClass(ClassRoom clazz)
{
if (_scriptclass == null) {
_scriptclass = clazz;
}
_classes.push(clazz);
_class = clazz;
_pool = clazz.getPool();
}
public void popClass()
{
_classes.pop();
if (!_classes.isEmpty()) {
_class = (ClassRoom)_classes.peek();
_pool = _class.getPool();
} else {
_class = null;
_pool = null;
}
}
public Method getMethod()
{
return getCode().getMethod();
}
public Code getCode()
{
return _code;
}
public void pushCode(Code code)
{
_codes.push(code);
_code = code;
}
public void popCode()
{
_codes.pop();
if (!_codes.isEmpty()) {
_code = (Code)_codes.peek();
}
}
private int addText(String text)
{
Integer slot = (Integer)_texts.get(text);
if (slot != null) {
return slot.intValue();
} else {
int ctr = _texts.size();
_texts.put(text, new Integer(ctr));
return ctr;
}
}
public ByteCompiler text(Code code, String text, boolean newline)
{
if (text.length()>0) {
code.aload_first();
code.getstatic(code.getPool().addFieldRef(TYPE_MODULE, "_text", "[[B"));
code.iconst(addText(text));
code.aaload();
code.invokevirtual(code.getPool().addMethodRef("anvil/script/Context",
newline?"println":"print", "([B)V"));
}
return this;
}
public ByteCompiler symbol(String name)
{
return symbol(Register.register(name));
}
public ByteCompiler symbol(int index)
{
Code code = getCode();
String name = Register.getNameOf(index);
code.getstatic(getPool().addFieldRef(TYPE_MODULE, "r_"+name, "I"));
_symbols.put(new Integer(index), name);
return this;
}
private int addConstant(Any value)
{
Integer slot = (Integer)_constants.get(value);
if (slot != null) {
return slot.intValue();
} else {
int ctr = _constants.size();
_constants.put(value, new Integer(ctr));
return ctr;
}
}
public ByteCompiler constant(Any value, boolean canchange)
{
Code code = getCode();
int slot = addConstant(value);
code.getstatic(code.getPool().addFieldRef(TYPE_MODULE, "_const", "[Lanvil/core/Any;"));
code.iconst(slot);
code.aaload();
if (canchange && value.isMutable()) {
code.invokevirtual(getPool().addMethodRef("anvil/core/Any", "copy", "()Lanvil/core/Any;"));
}
return this;
}
public int addSwitch(Enumeration keys)
{
_switches.addElement(keys);
return _switches.size() - 1;
}
public ByteCompiler reflect(String classname)
{
Integer slot = (Integer)_reflections.get(classname);
if (slot == null) {
slot = new Integer(_reflections.size());
_reflections.put(classname, slot);
}
Code code = getCode();
ConstantPool pool = getPool();
int javafield = pool.addFieldRef(TYPE_MODULE, "java$"+slot,
"Lanvil/core/reflect/Reflection;");
int contextclass = pool.addClass(TYPE_CONTEXT);
code.getstatic(javafield);
Source isnull = code.if_null();
code.getstatic(javafield);
Source notnull = code.go_to();
isnull.bind();
code.aload_first();
code.astring(classname);
code.invokevirtual(pool.addMethodRef(contextclass, "reflect", "(Ljava/lang/String;)Lanvil/core/reflect/Reflection;"));
code.dup();
code.putstatic(javafield);
notnull.bind();
code.popop();
return this;
}
public ByteCompiler location(Location location)
{
if (_debug && location != null) {
Code code = getCode();
Method method = code.getMethod();
if (method.getClassRoom().getStatic() != method) {
code.aload_first();
code.iconst(location.getLine());
code.invokevirtual(getPool().addMethodRef("anvil/script/StackFrameStack",
"setLine", "(I)V"));
}
}
return this;
}
public boolean compile()
{
Code code;
String classname = _envelope.getClassname();
Address address = _envelope.getAddress();
String pathinfo = address.getPathinfo();
String classsig = _envelope.getDescriptor();
TYPE_MODULE = classsig;
ClassRoom clazz = new ClassRoom();
pushClass(clazz);
clazz.setClassname(classsig, null);
clazz.setSuperClassname("anvil/script/compiler/CompiledModule");
clazz.setAccessFlags(ACC_SUPER|ACC_PUBLIC);
ConstantPool pool = clazz.getPool();
_f_const = clazz.createField("_const", "[Lanvil/core/Any;", ACC_PUBLIC|ACC_STATIC|ACC_FINAL);
_f_text = clazz.createField("_text", "[[B", ACC_PUBLIC|ACC_STATIC|ACC_FINAL);
_f_switch = clazz.createField("_switch", "[Lanvil/java/util/Hashlist;", ACC_PUBLIC|ACC_STATIC|ACC_FINAL);
Method _m_const = clazz.createMethod("_const", "()V", ACC_PUBLIC|ACC_STATIC);
Method _m_text = clazz.createMethod("_text", "()V", ACC_PUBLIC|ACC_STATIC);
Method _m_switch = clazz.createMethod("_switch", "()V", ACC_PUBLIC|ACC_STATIC);
Method _m_symbols = clazz.createMethod("_symbols", "()V", ACC_PUBLIC|ACC_STATIC);
//Method _m_imports = clazz.createMethod("_imports", "()V", ACC_PUBLIC|ACC_STATIC);
code = clazz.getStatic().getCode();
//code.println("MODULE-START:"+classsig);
code.invokestatic(_m_const.getIndex());
code.invokestatic(_m_text.getIndex());
code.invokestatic(_m_switch.getIndex());
code.invokestatic(_m_symbols.getIndex());
((anvil.script.statements.ModuleStatement)getModule()).compile(this);
//code.println("MODULE-END:"+classsig);
code.vreturn();
/* constants */
code = _m_const.getCode();
if (_constants.size() > 0) {
code.iconst(_constants.size());
code.anewarray(TYPE_ANY);
Enumeration e = _constants.keys();
for(int i=0; e.hasMoreElements(); i++) {
code.dup();
code.iconst(i);
((Any)e.nextElement()).toCode(code);
code.aastore();
}
code.putstatic(_f_const);
}
code.vreturn();
/* texts */
code = _m_text.getCode();
int getBytes = pool.addMethodRef("anvil/util/Conversions", "getBytes", "(Ljava/lang/String;)[B");
if (_texts.size() > 0) {
int size = _texts.size();
code.iconst(size);
code.anewarray("[[B", 1);
Enumeration e = _texts.keys();
for(int i=0; e.hasMoreElements(); i++) {
code.dup();
code.iconst(i);
code.astring((String)e.nextElement());
code.invokestatic(getBytes);
code.aastore();
}
code.putstatic(_f_text);
}
code.vreturn();
/* switchtables */
code = _m_switch.getCode();
int n = _switches.size();
if (_switches.size() > 0 ) {
int hashlistclazz = pool.addClass("anvil/java/util/Hashlist");
int hashlistctor = pool.addMethodRef(hashlistclazz, "<init>", "()V");
int hashlistadd = pool.addMethodRef(hashlistclazz, "add",
"(Ljava/lang/Object;Ljava/lang/Object;)Lanvil/java/util/Hashlist;");
int intclazz = pool.addClass("java/lang/Integer");
int intclazzctor = pool.addMethodRef(intclazz, "<init>", "(I)V");
code.iconst(n);
code.anewarray(hashlistclazz);
for(int i=0; i<n; i++) {
code.dup();
code.iconst(i);
code.anew(hashlistclazz);
code.dup();
code.invokespecial(hashlistctor);
int c = 0;
Enumeration keys = (Enumeration)_switches.elementAt(i);
while(keys.hasMoreElements()) {
Any key = (Any)keys.nextElement();
if (key != SwitchStatement.DEFAULT_MARKER) {
key.toCode(code);
code.anew(intclazz);
code.dup();
code.iconst(c++);
code.invokespecial(intclazzctor);
code.invokevirtual(hashlistadd);
}
}
code.aastore();
}
code.putstatic(_f_switch);
}
code.vreturn();
/* methodindices */
code = _m_symbols.getCode();
if (_symbols.size() > 0) {
int register = pool.addMethodRef(pool.addClass("anvil/core/Register"),
"register", "(Ljava/lang/String;)I");
BindingEnumeration e = _symbols.keysAndElements();
while(e.hasMoreElements()) {
int index = ((Integer)e.nextKey()).intValue();
String name = (String)e.nextElement();
Field field = clazz.createField("r_"+name, "I", ACC_PUBLIC|ACC_STATIC|ACC_FINAL);
code.astring(name);
code.invokestatic(register);
code.putstatic(field);
}
}
code.vreturn();
/* reflectionfields */
if (_reflections.size() > 0) {
BindingEnumeration e = _reflections.keysAndElements();
while(e.hasMoreElements()) {
int index = ((Integer)e.nextElement()).intValue();
clazz.createField("java$"+index, "Lanvil/core/reflect/Reflection;", ACC_PUBLIC|ACC_STATIC);
}
}
popClass();
try {
clazz.write(_smith);
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
public void compileArgumentList(CompilableFunction function, Node[] parameters)
{
compileArgumentList(function, parameters, -1);
}
public void compileArgumentList(CompilableFunction function, Node[] parameters, int contextindex)
{
Code code = getCode();
ConstantPool pool = getPool();
int max = (parameters != null) ? parameters.length : 0;
int c = 0;
int n = function.getParameterCount();
for(int i=0; i<n; i++) {
Any defaultValue = function.getParameterDefault(i);
switch(function.getParameterType(i)) {
case CompilableFunction.PARAMETER_ANY:
if (c<max) {
parameters[c++].compile(this, Node.GET);
} else {
if (defaultValue != null) {
constant(defaultValue, true);
} else {
code.aconst_null();
}
}
break;
case CompilableFunction.PARAMETER_OBJECT:
if (c<max) {
parameters[c++].compile(this, Node.GET);
code.invokevirtual(pool.addMethodRef(TYPE_ANY,
"toObject", "()Ljava/lang/Object;"));
} else {
if (defaultValue != null) {
constant(defaultValue, true);
code.invokevirtual(pool.addMethodRef(TYPE_ANY,
"toObject", "()Ljava/lang/Object;"));
} else {
code.aconst_null();
}
}
break;
case CompilableFunction.PARAMETER_BOOLEAN:
if (c<max) {
Node node = parameters[c++];
if (node.isConstant()) {
code.iconst(node.eval().toBoolean());
} else {
node.compile(this, Node.GET_BOOLEAN);
}
} else {
if (defaultValue != null) {
code.iconst(defaultValue.toBoolean());
} else {
code.iconst(false);
}
}
break;
case CompilableFunction.PARAMETER_INT:
if (c<max) {
Node node = parameters[c++];
if (node.isConstant()) {
code.iconst(node.eval().toInt());
} else {
node.compile(this, Node.GET);
code.invokevirtual(pool.addMethodRef(TYPE_ANY,
"toInt", "()I"));
}
} else {
if (defaultValue != null) {
code.iconst(defaultValue.toInt());
} else {
code.iconst(0);
}
}
break;
case CompilableFunction.PARAMETER_LONG:
if (c<max) {
Node node = parameters[c++];
if (node.isConstant()) {
code.lconst(node.eval().toLong());
} else {
node.compile(this, Node.GET);
code.invokevirtual(pool.addMethodRef(TYPE_ANY,
"toLong", "()J"));
}
} else {
if (defaultValue != null) {
code.lconst(defaultValue.toLong());
} else {
code.lconst(0);
}
}
break;
case CompilableFunction.PARAMETER_DOUBLE:
if (c<max) {
Node node = parameters[c++];
if (node.isConstant()) {
code.dconst(node.eval().toDouble());
} else {
node.compile(this, Node.GET);
code.invokevirtual(pool.addMethodRef(TYPE_ANY,
"toDouble", "()D"));
}
} else {
if (defaultValue != null) {
code.dconst(defaultValue.toDouble());
} else {
code.dconst(0.0);
}
}
break;
case CompilableFunction.PARAMETER_STRING:
if (c<max) {
Node node = parameters[c++];
if (node.isConstant()) {
code.astring(node.eval().toString());
} else {
node.compile(this, Node.GET);
code.invokevirtual(pool.addMethodRef(TYPE_OBJECT,
"toString", "()Ljava/lang/String;"));
}
} else {
if (defaultValue != null) {
code.astring(defaultValue.toString());
} else {
code.aconst_null();
}
}
break;
case CompilableFunction.PARAMETER_ARRAY:
{
int arrayclazz = pool.addClass("anvil/core/Array");
int appendmethod = pool.addMethodRef(arrayclazz, "append",
"(Lanvil/core/Any;)Lanvil/core/Array;");
code.anew(arrayclazz);
code.dup();
code.invokespecial(pool.addMethodRef(arrayclazz, "<init>", "()V"));
for(; c < max; c++) {
code.dup();
parameters[c].compile(this, Node.GET);
code.invokevirtual(appendmethod);
}
}
break;
case CompilableFunction.PARAMETER_ANYLIST:
case CompilableFunction.PARAMETER_REST:
if (c >= max) {
code.getstatic(pool.addFieldRef(TYPE_ANY, "EMPTY_TUPLE",
"Lanvil/core/AnyTuple;"));
} else {
int tupleclazz = pool.addClass("anvil/core/AnyTuple");
int appendmethod = pool.addMethodRef(tupleclazz, "append",
"(Lanvil/core/Any;)Lanvil/core/Array;");
code.anew(tupleclazz);
code.dup();
code.iconst(max - c);
code.anewarray(TYPE_ANY);
int index = 0;
for(; c < max; c++) {
code.dup();
code.iconst(index++);
parameters[c].compile(this, Node.GET);
code.aastore();
}
code.invokespecial(pool.addMethodRef(tupleclazz, "<init>",
"([Lanvil/core/Any;)V"));
}
break;
case CompilableFunction.PARAMETER_LIST:
int len = max - c;
if (len <= 0) {
code.getstatic(pool.addFieldRef(TYPE_ANY, "ARRAY0",
"[Lanvil/core/Any;"));
} else {
code.iconst(len);
code.anewarray(TYPE_ANY);
int index = 0;
for(; c < max; c++) {
code.dup();
code.iconst(index++);
parameters[c].compile(this, Node.GET);
code.aastore();
}
}
break;
case CompilableFunction.PARAMETER_CONTEXT:
if (contextindex == -1) {
code.aload_first();
} else {
code.aload(contextindex);
}
break;
}
}
}
public void compileArgumentList(Node[] parameters)
{
Code code = getCode();
ConstantPool pool = getPool();
int n = (parameters != null) ? parameters.length : 0;
if (n == 0) {
code.getstatic(pool.addFieldRef(TYPE_ANY, "ARRAY0",
"[Lanvil/core/Any;"));
return;
}
boolean hasSplices = false;
for(int i=0; i<n; i++) {
if (parameters[i].typeOf() == Node.EXPR_SPLICE) {
hasSplices = true;
break;
}
}
if (hasSplices) {
int clazz = pool.addClass("anvil/script/ParameterList");
int splicemethod = pool.addMethodRef(clazz, "splice",
"(Lanvil/core/Any;)Lanvil/script/ParameterList;");
int addmethod = pool.addMethodRef(clazz, "add",
"(Lanvil/core/Any;)Lanvil/script/ParameterList;");
code.anew(clazz);
code.dup();
code.iconst(n);
code.invokespecial(pool.addMethodRef(clazz, "<init>", "(I)V"));
for(int i=0; i<n; i++) {
parameters[i].compile(this, Node.GET);
if (parameters[i].typeOf() == Node.EXPR_SPLICE) {
code.invokevirtual(splicemethod);
} else {
code.invokevirtual(addmethod);
}
}
code.invokevirtual(pool.addMethodRef(clazz, "toArray",
"()[Lanvil/core/Any;"));
} else {
code.iconst(n);
code.anewarray(TYPE_ANY);
for(int i=0; i<n; i++) {
code.dup();
code.iconst(i);
parameters[i].compile(this, Node.GET);
code.aastore();
}
}
}
public void compileCall(CompilableFunction function, Node[] parameters)
{
compileArgumentList(function, parameters);
getCode().invokestatic(function.getTypeRef(getPool()));
}
public static String getSignature(CompilableFunction function)
{
StringBuffer sig = new StringBuffer();
int n = function.getParameterCount();
sig.append('(');
for(int i=0; i<n; i++) {
sig.append(CompilableFunction.SIGNATURES[function.getParameterType(i)]);
}
sig.append(')');
sig.append(CompilableFunction.SIGNATURES[CompilableFunction.PARAMETER_ANY]);
return sig.toString();
}
public void any2boolean()
{
Code code = getCode();
code.invokevirtual(code.getPool().addMethodRef("anvil/core/Any", "toBoolean", "()Z"));
}
public void boolean2any()
{
Code code = getCode();
code.invokestatic(code.getPool().addMethodRef(
"anvil/script/compiler/CompiledModule", "b2a", "(Z)Lanvil/core/Any;"));
}
public void accessInstance(ClassType context, ClassType target)
{
Code code = getCode();
if (context == null || context == target) {
code.self();
return;
}
ConstantPool pool = code.getPool();
ClassType[] parents = context.getEnclosingClasses();
int n = parents.length;
for(int i=0; i<n; i++) {
ClassType parent = parents[i];
if (parent == target) {
code.self();
code.getfield(pool.addFieldRef(context.getTypeRef(pool), "this$"+i,
'L'+parent.getDescriptor()+';'));
return;
}
}
code.self();
}
}