/*
* $Id: FunctionStatement.java,v 1.18 2002/09/16 08:05:06 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.statements;
import anvil.core.Any;
import anvil.core.AnyClass;
import anvil.core.AnyTuple;
import anvil.codec.ExceptionHandler;
import anvil.codec.ClassRoom;
import anvil.codec.Field;
import anvil.codec.Method;
import anvil.codec.Code;
import anvil.codec.CodecConstants;
import anvil.codec.ConstantPool;
import anvil.codec.Target;
import anvil.codec.Source;
import anvil.codec.Switch;
import anvil.Location;
import anvil.doc.DocParser;
import anvil.doc.Doc;
import anvil.parser.Tag;
import anvil.ErrorListener;
import anvil.script.CompilableFunction;
import anvil.script.compiler.ByteCompiler;
import anvil.script.Context;
import anvil.script.Type;
import anvil.script.FunctionDispatcher;
import anvil.script.expression.Node;
import anvil.script.expression.Expression;
import anvil.script.expression.VariableNode;
import anvil.script.expression.ConstantNode;
import anvil.script.ParameterListDeclaration;
import anvil.script.parser.ExpressionParser;
import anvil.script.parser.TemplateParser;
import anvil.script.Module;
import anvil.script.StackFrame;
import anvil.script.Grammar;
import anvil.java.util.Hashlist;
import java.io.IOException;
import java.util.Enumeration;
/**
* class FunctionStatement
*
* @author: Jani Lehtim�ki
*/
public class FunctionStatement extends DefinitionStatement
implements CompilableFunction, CodecConstants
{
protected FunctionStatement _context;
protected boolean _synchronized = false;
protected ParameterListDeclaration _parameters = null;
protected BlockStatement _block;
protected ExceptionHandler _handler;
protected Switch _switch;
protected boolean _escaped = false;
protected int _localcount = 0;
protected int _yieldcount = 0;
protected String _signature;
protected int _local_frame;
protected VariableNode _return_var = null;
public FunctionStatement(DefinitionStatement parent, Location location)
{
super(parent, location);
_block = new ImplicitBlockStatement(this, location);
}
public FunctionStatement(
Location location,
DefinitionStatement parent,
FunctionStatement context,
boolean sync,
String name,
String document,
ParameterListDeclaration parameters)
{
super(
parent,
location, (context != null) ? context.getName() + '$' + name : name,
DocParser.parseFunction(name, document));
_context = context;
_synchronized = sync;
_parameters = parameters;
_parameters.declareTo(this);
_parameters.importDocuments(_document);
_block = new ImplicitBlockStatement(this, location);
}
public String toString()
{
StringBuffer buffer = new StringBuffer(40);
buffer.append(super.toString());
buffer.append('(');
if (_parameters != null) {
_parameters.toString(buffer);
} else {
buffer.append("<unparsed>");
}
buffer.append(')');
return buffer.toString();
}
public int typeOf()
{
return Statement.ST_FUNCTION;
}
public int getNextLocalSlot()
{
return _localcount++;
}
public Type lookupDeclaration(String name)
{
Type type = (Type)_types.get(name);
if (type == null) {
if (_context != null) {
type = _context.lookupDeclaration(name);
}
if (type == null) {
return super.lookupDeclaration(name);
}
}
return type;
}
public LocalVariableStatement declare(String name)
{
LocalVariableStatement var = new LocalVariableStatement(getLocation(), this, name);
declare(var);
return var;
}
public ParameterStatement declareParameter(String name)
{
ParameterStatement param = new ParameterStatement(getLocation(), this, name);
declare(param);
return param;
}
public boolean isStaticRegion()
{
return true;
}
public boolean isInnerFunction()
{
return _context != null;
}
public void parse(TemplateParser parser, Tag tag)
{
super.parse(parser, tag);
String name = tag.getValue("name");
if (name != null) {
name = name.trim();
if (!Grammar.isValidIdentifier(name)) {
parser.error(getLocation(), "invalid function name: '"+name+"'");
} else {
_name = name;
}
_document = DocParser.parseFunction(_name, parser.getDocument());
} else {
parser.error(getLocation(), "function attribute 'name' not given");
}
_synchronized = tag.contains("synchronized");
_parameters = parseParameters(parser, tag);
_parameters.declareTo(this);
}
public boolean onTag(TemplateParser parser, int type, Tag tag)
{
switch(type) {
case ST_ENDFUNCTION:
parser.pop();
return true;
default:
return super.onTag(parser, type, tag);
}
}
public ParameterListDeclaration parseParameters(TemplateParser parser, Tag tag)
{
ParameterListDeclaration parameters = new ParameterListDeclaration();
String params = tag.getValue("params");
if (params != null) {
ExpressionParser p = new ExpressionParser(parser, parser.getLocation(), params);
p.parseParameterListDeclaration(parameters);
} else {
parameters.open();
int n = tag.getLength();
for(int i=0; i<n; i++) {
String name = tag.getName(i);
if (name.equalsIgnoreCase("param")) {
String param = tag.getValue(i);
Expression defaultexpr = null;
if ((i<n-1) && tag.getName(i+1).equalsIgnoreCase("default")) {
String def = tag.getValue(i+1);
defaultexpr = Grammar.parseExpression(def, getLocation(), parser);
i++;
} else {
if (parameters.hasDefaultValues()) {
parser.error(getLocation(), "Parameters with default values must appear last");
defaultexpr = null;
}
}
if (Grammar.isValidIdentifier(param)) {
if (parameters.isDeclared(param)) {
parser.error(getLocation(), "Entity '"+param+"' is already declared");
} else {
parameters.add(param, defaultexpr);
}
} else {
parser.error(getLocation(), "Parameter name '"+param+"' is invalid");
}
}
}
String rest = tag.getValue("rest");
if (rest != null) {
rest = rest.trim();
if (!Grammar.isValidIdentifier(rest)) {
parser.error(getLocation(), "Parameter '" + rest + "' is invalid");
} else {
if (parameters.isDeclared(rest)) {
parser.error(getLocation(), "Entity '"+rest+"' is already declared");
} else {
parameters.add(PARAMETER_REST, rest, Any.EMPTY_TUPLE, null);
}
}
}
parameters.close();
}
return parameters;
}
public void check(ErrorListener context)
{
_parameters.check(context);
_block.check(context);
_block.eliminate(context);
}
public void markEscaped()
{
_escaped = true;
}
public boolean isEscaped()
{
return _escaped;
}
public int addYieldState()
{
return ++_yieldcount;
}
public boolean isGenerator()
{
return _yieldcount > 0;
}
public String name()
{
return "function";
}
public String getName()
{
return _name;
}
public String getDescriptor()
{
return "f_" + _name;
}
public int getType()
{
return FUNCTION;
}
public FunctionStatement getContext()
{
return _context;
}
public Doc getDocument()
{
return _document;
}
public Statement getChildStatement()
{
return _block;
}
public int getMinimumParameterCount()
{
return _parameters.minSize();
}
public int getParameterCount()
{
return _parameters.size();
}
public String getParameterName(int index)
{
return _parameters.getName(index);
}
public int getParameterType(int index)
{
return _parameters.getType(index);
}
public Any getParameterDefault(int index)
{
return _parameters.getDefault(index);
}
public Doc getParameterDoc(int index)
{
return _parameters.getDoc(index);
}
public Any getAttribute(String name)
{
return null;
}
public FunctionDispatcher getDispatcher(Context context)
{
return null;
}
public Any execute(Context context, Any[] parameters)
{
return execute(context, null, parameters);
}
public Any execute(Context context, Any self, Any[] parameters)
{
return null;
}
public Any execute(Context context, Any self)
{
return null;
}
public Any execute(Context context, Any self, Any param1)
{
return null;
}
public Any execute(Context context, Any self, Any param1, Any param2)
{
return null;
}
public Any execute(Context context, Any self, Any param1, Any param2, Any param3)
{
return null;
}
public Any execute(Context context, Any self, Any param1, Any param2, Any param3, Any param4)
{
return null;
}
public int getFrameIndex()
{
return _local_frame;
}
public String getSignature()
{
if (_signature == null) {
StringBuffer buffer = new StringBuffer();
buffer.append('(');
buffer.append("Lanvil/script/Context;");
int n = _parameters.size();
for(int i=1; i<n; i++) {
buffer.append("Lanvil/core/Any;");
}
buffer.append(")Lanvil/core/Any;");
_signature = buffer.toString();
}
return _signature;
}
public void compileDescriptor(ByteCompiler context)
{
_parameters.compileDescriptor(context);
}
public void compile(ByteCompiler context)
{
ClassRoom clazz = context.getClassRoom();
Field typefield = clazz.createField("f_"+_name, "Lanvil/script/Function;", ACC_PUBLIC|ACC_STATIC);
clazz.createField("F_"+_name, "Lanvil/core/Any;", ACC_PUBLIC|ACC_STATIC);
Method method = clazz.createMethod("f_"+_name, getSignature(), ACC_PUBLIC|ACC_FINAL|ACC_STATIC|(_synchronized?ACC_SYNCHRONIZED:0));
compileBody(context, method, typefield);
}
public void compileBody(ByteCompiler context, Method method, Field typefield)
{
ClassRoom clazz = context.getClassRoom();
ConstantPool pool = clazz.getPool();
Code code = method.getCode();
boolean is_method = (getType() != FUNCTION);
boolean is_generator = (_yieldcount > 0);
context.pushCode(code);
int l_context = code.addLocal();
Enumeration enum = _types.elements();
while(enum.hasMoreElements()) {
Object obj = enum.nextElement();
if (obj instanceof ParameterStatement) {
((ParameterStatement)obj).compileInit(context);
}
}
if (is_generator) {
enum = _types.elements();
while(enum.hasMoreElements()) {
Object obj = enum.nextElement();
if (obj instanceof LocalVariableStatement) {
((LocalVariableStatement)obj).markEscaped();
}
}
int generatorclazz = pool.addClass("anvil/script/Generator");
_local_frame = code.addLocal();
code.anew(generatorclazz);
code.dup();
code.aload_first();
code.getstatic(pool.addFieldRef(context.TYPE_MODULE, "_module", "Lanvil/script/compiler/CompiledModule;"));
if (is_method) {
code.self();
} else {
code.aconst_null();
}
code.getstatic(typefield);
code.iconst(_localcount);
code.iconst(getLocation().getLine());
code.invokespecial(pool.addMethodRef(generatorclazz, "<init>",
"(Lanvil/script/Context;Lanvil/script/Module;Lanvil/core/AnyClass;Lanvil/script/Function;II)V"));
code.astore(_local_frame);
enum = _types.elements();
enum = _types.elements();
while(enum.hasMoreElements()) {
Object obj = enum.nextElement();
if (obj instanceof LocalVariableStatement) {
((LocalVariableStatement)obj).compile(context);
}
}
code.aload(_local_frame);
code.invokevirtual(pool.addMethodRef(generatorclazz, "getWrapper", "()Lanvil/core/Any;"));
code.areturn();
context.popCode();
Method method2;
if (is_method) {
method2 = clazz.createMethod("h_"+_name, "(Lanvil/script/Context;Lanvil/script/Generator;)Lanvil/core/Any;", ACC_PUBLIC|(_synchronized?ACC_SYNCHRONIZED:0));
} else {
method2 = clazz.createMethod("h_"+_name, "(Lanvil/script/Context;Lanvil/script/Generator;)Lanvil/core/Any;", ACC_PUBLIC|ACC_FINAL|ACC_STATIC|(_synchronized?ACC_SYNCHRONIZED:0));
}
code = method2.getCode();
code.addLocal();
_local_frame = code.addLocal();
code.addLocals(2);
context.pushCode(code);
code.aload(_local_frame);
code.invokevirtual(pool.addMethodRef("anvil/script/Generator", "getState", "()I"));
_switch = code.select();
for(int i=0; i<=_yieldcount; i++) {
_switch.addCase(i);
}
_switch.end();
_switch.bindCase(0);
_block.compile(context);
_switch.bindDefault();
code.aload(_local_frame);
code.invokevirtual(pool.addMethodRef("anvil/script/Generator", "setClosedState", "()V"));
code.getstatic(pool.addFieldRef(context.TYPE_ANY, "UNDEFINED", "Lanvil/core/Any;"));
code.areturn();
} else {
_local_frame = code.addLocal();
_return_var = new VariableNode(declare("return$"+hashCode()));
//_return_var.compile(context, ConstantNode.UNDEFINED);
//code.pop();
code.aload(l_context);
code.getstatic(pool.addFieldRef(context.TYPE_MODULE, "_module", "Lanvil/script/compiler/CompiledModule;"));
if (is_method) {
code.self();
}
code.getstatic(typefield);
code.iconst(_escaped ? _localcount : 0);
code.iconst(getLocation().getLine());
code.iconst(_escaped);
code.invokevirtual(pool.addMethodRef("anvil/script/StackFrameStack", "push",
is_method ?
"(Lanvil/script/Module;Lanvil/core/AnyClass;Lanvil/script/Function;IIZ)Lanvil/script/StackFrame;" :
"(Lanvil/script/Module;Lanvil/script/Function;IIZ)Lanvil/script/StackFrame;"
));
code.astore(_local_frame);
ExceptionHandler handler = code.startExceptionHandler(true);
_handler = handler;
{
enum = _types.elements();
while(enum.hasMoreElements()) {
Object obj = enum.nextElement();
if (obj instanceof LocalVariableStatement) {
((LocalVariableStatement)obj).compile(context);
}
}
_block.compile(context);
handler.callFinally();
if (getType() == CONSTRUCTOR) {
code.self();
} else {
_return_var.compile(context, Node.GET);
//code.aload(_return_var);
//code.getstatic(pool.addFieldRef(context.TYPE_ANY, "UNDEFINED", "Lanvil/core/Any;"));
}
code.areturn();
}
handler.endTry();
handler.endProtectedRegion();
handler.startCatch(0);
{
int l_thrown = code.addLocal();
code.astore(l_thrown);
handler.callFinally();
code.aload(l_thrown);
code.athrow();
}
handler.endCatches();
handler.startFinally();
{
int l_ret = code.addLocal();
code.astore(l_ret);
code.aload(l_context);
code.invokevirtual(pool.addMethodRef("anvil/script/StackFrameStack", "pop", "()V"));
code.ret(l_ret);
}
handler.endFinally();
handler.end();
}
context.popCode();
}
public int getTypeRef(ConstantPool pool)
{
return pool.addMethodRef(_parent.getTypeRef(pool),
getDescriptor(), getSignature());
}
public void bindYieldState(int state)
{
if (_switch != null) {
_switch.bindCase(state);
}
}
public boolean callFinalizer()
{
if (_handler != null) {
_handler.callFinally();
}
return false;
}
public VariableNode getReturnVariable()
{
return _return_var;
}
}