This commit was generated by cvs2svn to compensate for changes in r4,

which included commits to RCS files with non-trunk default branches.
This commit is contained in:
hns 2000-12-29 17:58:10 +00:00
parent af35ca5581
commit ee13186158
148 changed files with 34934 additions and 0 deletions

View file

@ -0,0 +1,541 @@
// ClassInfo.java
// FESI Copyright (c) Jean-Marc Lugrin, 1999
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package FESI.Interpreter;
import java.util.Hashtable;
import java.util.Vector;
import java.lang.reflect.*;
import java.beans.*;
import FESI.Data.*;
import FESI.Exceptions.*;
/**
* This class contains the static routines to access the cache of class information
* as well as the ClassInfo (class specific cached information). The information
* is separated between the bean based information and the low level information.
* Even if most classes will have the same information in both cases, they are
* likely to be used in only one way, so there space overhead is ususally small.
* <P>The cache is filled in a lazy manner, only requested information is cached,
* even if this requires more sweeps of the low level information. The rational is
* that for small set of information this is not a major drawback, and for large
* set of information (for example the list of methods of an AWT component), only
* a small subset of it is really used. So the small foorprint is given more importance
* than a faster access for seldom cases.
* <P>The cache is shared by all instance of the evaluator and class loaders (it is
* enough to identify the classes by their Class object). So the access must
* be synchronized and no evaluator specific information must be added.
* <P>The existence of this cache defeats class garbage collections for classes
* accessed via EcmaScript. This should not be too severe a restriction, and can
* be lifted in JDK 1.2.
*/
public class ClassInfo {
/** Cache of information on all classes, using the class object as a key */
static private Hashtable allClassInfo = new Hashtable();
/** Cache of public methods */
private Hashtable publicMethods = null;
/** Cache of bean methods */
private Hashtable beanMethods = null;
/** Cache of bean properties */
private Hashtable beanProperties = null;
/** Cache of BeanInofo */
private BeanInfo beanInfo = null;
/**
* Ensure that ClassInfo objects can only be created via
* the factory.
*/
private ClassInfo() {
}
/**
* Ensure that the specified class has a ClassInfo object in the
* cached. Create an empty one if needed. Must be called synchronized.
*
* @param cls The class for which we look for a ClassInfo
* @return the ClassInfo of cls, added to the cache if needed
*/
private static ClassInfo ensureClassInfo(Class cls) {
boolean debug = ESLoader.isDebugJavaAccess();
ClassInfo classInfo = (ClassInfo) allClassInfo.get(cls);
if (classInfo == null) {
if (debug) System.out.println("** Class info for class '" +
cls + "' not found in cache, created");
classInfo = new ClassInfo();
allClassInfo.put(cls, classInfo);
}
return classInfo;
}
/**
* Get the property descriptor for the specified field of the specified class,
* considered as a bean.
*
* @param fieldName The name of the property
* @param cls The class for which we look for the property.
* @return The PropertyDescriptor or null if not found or in case of error
*/
synchronized public static PropertyDescriptor lookupBeanField(String fieldName, Class cls) {
ClassInfo classInfo = ClassInfo.ensureClassInfo(cls);
return classInfo.cachedBeanFieldLookup(fieldName, cls);
}
/**
* Get the property descriptor for the specified field of the specified class,
* considered as a bean, in the current ClassInfo cache. Add it to the cache
* if needed, after veryfying that the accessors are OK.
*
* @param fieldName The name of the property
* @param cls The class for which we look for the property.
* @return The PropertyDescriptor or null if not found or in case of error
*/
private PropertyDescriptor cachedBeanFieldLookup(String propertyName, Class cls) {
boolean debug = ESLoader.isDebugJavaAccess();
// Check that there is a bean properties cache, chech if the property was cached
if (beanProperties != null) {
if (debug) System.out.println("** Bean properties for class '" +
cls + "' found in cache");
PropertyDescriptor descriptor =
(PropertyDescriptor) beanProperties.get(propertyName);
if (descriptor!= null) {
if (debug) System.out.println("** property descriptor '" + propertyName + "' found in cache");
return descriptor;
}
}
// Not in cache
if (debug) System.out.println("** No property named '" +
propertyName + "' found in cache, lookup started");
// Do we have a cached BeanInfo ? create it if no
if (beanInfo == null) {
try {
beanInfo = Introspector.getBeanInfo(cls);
} catch (IntrospectionException e) {
if (debug) System.out.println(" ** Error getting beaninfo: " + e);
return null;
}
}
// Get the property descriptor by name
PropertyDescriptor [] allProperties = beanInfo.getPropertyDescriptors();
PropertyDescriptor descriptor = null; // none found
for (int i=0; i<allProperties.length; i++) {
PropertyDescriptor property = allProperties[i];
if (debug) System.out.println("** Property examined: " + property.getName());
if (!property.getName().equals(propertyName)) continue;
descriptor = property;
break;
}
// If we found it, test it and add it to the cache
if (descriptor != null) {
// Check that it is valid here (to speed up safe usage later)
Method readMethod = descriptor.getReadMethod();
Method writeMethod = descriptor.getWriteMethod();
Class propCls = descriptor.getPropertyType();
if (descriptor instanceof IndexedPropertyDescriptor) {
// Check indexed access
IndexedPropertyDescriptor iDescriptor = (IndexedPropertyDescriptor) descriptor;
Method indexedReadMethod = iDescriptor.getIndexedReadMethod();
Method indexedWriteMethod = iDescriptor.getIndexedWriteMethod();
Class propIndexedCls = iDescriptor.getIndexedPropertyType();
if (propIndexedCls == null) {
throw new ProgrammingError("getIndexedPropertyType returned null for " + propertyName);
}
if (propIndexedCls == Void.TYPE) {
throw new ProgrammingError("Void indexed property type for '" + propertyName + "' is not allowed");
}
if (indexedReadMethod != null && indexedReadMethod.getParameterTypes().length != 1) {
throw new ProgrammingError("Indexed getter of property ' " + propertyName + "' should have 1 parameter!");
}
if (indexedWriteMethod != null) {
Class [] paramCls = indexedWriteMethod.getParameterTypes();
if (paramCls == null || paramCls.length != 2) {
throw new ProgrammingError("Indexed setter of property ' " + propertyName + "' should have 2 parameter!");
}
if (paramCls[0]!=propIndexedCls) { // SHOULD CHECK INSTANCE
throw new ProgrammingError("Inconstant parameter type for indexed setter of indexed property' " + propertyName + "', type: " + propCls);
}
}
// Check non indexed access
if (propCls != null) {
if (!propCls.isArray()) {
throw new ProgrammingError("Non array type (" + propCls + ") for array access of indexed property '" + propertyName + "'");
}
if (propCls.getComponentType()!=propIndexedCls) {
throw new ProgrammingError("Type missmatch between array and non array access of indexed property '" + propertyName + "'");
}
if (readMethod != null && readMethod.getParameterTypes().length != 0) {
throw new ProgrammingError("Non indexed getter of indxed property ' " + propertyName + "' is not supposed to have a parameter!");
}
if (writeMethod != null) {
Class [] paramCls = writeMethod.getParameterTypes();
if (paramCls == null || paramCls.length != 1) {
throw new ProgrammingError("Non indexed setter of indexed property ' " + propertyName + "' should have 1 parameter!");
}
if (paramCls[0]!=propCls) { // SHOULD CHECK INSTANCE
throw new ProgrammingError("Inconstant parameter type for non indexed setter of indexed property' " + propertyName + "', type: " + propCls);
}
}
} // non indexed access
} else {
if (propCls == null) {
throw new ProgrammingError("getPropertyType returned null for " + propertyName);
}
if (propCls == Void.TYPE) {
throw new ProgrammingError("Void property type for '" + propertyName + "' is not allowed");
}
if (readMethod != null && readMethod.getParameterTypes().length != 0) {
throw new ProgrammingError("Non indexed getter of property ' " + propertyName + "' is not supposed to have a parameter!");
}
if (writeMethod != null) {
Class [] paramCls = writeMethod.getParameterTypes();
if (paramCls == null || paramCls.length != 1) {
throw new ProgrammingError("Non indexed setter of property ' " + propertyName + "' should have 1 parameter!");
}
if (paramCls[0]!=propCls) { // SHOULD CHECK INSTANCE
throw new ProgrammingError("Inconstant parameter type for setter of property' " + propertyName + "', type: " + propCls);
}
}
}
// Add to cache
if (debug) System.out.println("** property '" + propertyName + "' + found, add to cache");
if (beanProperties==null) {
beanProperties = new Hashtable();
}
beanProperties.put(propertyName, descriptor);
} else {
if (debug) System.out.println("** No method named '" + propertyName + "' found");
}
return descriptor;
}
/**
* Get the list of public method in this class or superclass, by name (the
* methods may be static or instance!).
*
* @param functionName The nam of the method being looked up
* @param cls The class of the method being looked up
* @return The method array or null if none found or in case of error
*/
synchronized public static Method[] lookupPublicMethod(String functionName, Class cls) throws EcmaScriptException {
ClassInfo classInfo = ClassInfo.ensureClassInfo(cls);
return classInfo.cachedPublicMethodLookup(functionName, cls);
}
/**
* Look for a method in a list of interface or all superinterfaces
*
* @param functionName The name of the function to search
* @param interfaces The list of interfaces to search
* @param paramTypes The type of parameters of the function
*
* @return The method if found, null otherwise
*/
private Method getInInterfaces(String functionName, Class [] interfaces, Class[] paramTypes) {
boolean debug = ESLoader.isDebugJavaAccess();
if (debug && interfaces.length>0) {
System.out.println("** Looking in " + interfaces.length + " interfaces");
}
SEARCHININTERFACE:
for (int ix=0; ix<interfaces.length; ix++) {
Class theInterface=interfaces[ix];
if (Modifier.isPublic(theInterface.getModifiers())) {
if (debug) {
System.out.println("** Looking in public interface: " + theInterface);
}
try {
Method method = theInterface.getDeclaredMethod(functionName,paramTypes);
if (Modifier.isPublic(method.getModifiers())) {
if (debug) {
System.out.println("** Public method found: " + functionName);
}
return method;
}
} catch (NoSuchMethodException e) {
if (debug) {
System.out.println("** The method has no public declaration in the interface: "+ functionName);
}
// throw new ProgrammingError("The method has no public declaration in a public class: "+ functionName);
} catch (SecurityException e) {
throw new ProgrammingError("Access error inspecting method "+ functionName + ": " + e);
}
} else {
if (debug) {
System.out.println("** Interface " + theInterface + " is not public - not searching for method");
}
}
// Not found, try super interfaces
Class [] superInterfaces = theInterface.getInterfaces();
Method method = getInInterfaces(functionName, superInterfaces, paramTypes);
if (method!=null) {
if (debug) System.out.println("** Method found in super interfaces");
return method;
}
}
if (debug) {
System.out.println("** No method found in interface and super interfaces");
}
return null;
}
/**
* Get a public method list, where method matched in name and class.
* The method list is cached in this ClassInfo object (which must be
* for the specified class).
* <P>Only the public variant of a method is returned.
* <P>If a method was not found, the cache is not modified, and a new
* slow lookup will be done on next search. The case is rare enought
* that it is better to keep the code simpler.
*
* @param functionName The name of the function
* @param cls The class in which the function is defined
* @return The list of methods or null in case of error or if none found.
*/
private Method [] cachedPublicMethodLookup(String functionName, Class cls) throws EcmaScriptException {
boolean debug = ESLoader.isDebugJavaAccess();
if (publicMethods != null) {
if (debug) System.out.println("** Method descriptor for class '" +
cls + "' found in cache");
Method [] methods = (Method []) publicMethods.get(functionName);
if (methods!= null) {
if (debug) System.out.println("** " + methods.length +
" method(s) named '" + functionName + "' found in cache");
return methods;
}
}
// Not in cache, find if any matching the same name can be found
if (debug) System.out.println("** No method named '" +
functionName + "' found in class cache, lookup started");
Method [] allMethods = cls.getMethods();
Vector methodVector = new Vector(allMethods.length);
boolean wasFound = false;
for (int i=0; i<allMethods.length; i++) {
Method method = allMethods[i];
if (debug) System.out.println("** Method examined: " + method.toString());
if (!method.getName().equals(functionName)) continue;
// Method has same name, some closer examination is needed:
// If the class itself is not public, there is an access error if
// the method is invoked on it - the identical method in the
// superclass must be searched, to be used in the invocation.
// I am not too sure of what happens if the method is defined in an
// interface...
if (!Modifier.isStatic(cls.getModifiers()) && !Modifier.isPublic(cls.getModifiers())) {
if (debug) System.out.println("** Class " + cls +
" is not public, examining superclasses and interfaces to find proper method descriptor");
Class[] paramTypes = method.getParameterTypes();
SEARCHPUBLIC:
for (Class theClass=cls;theClass!=null;theClass=theClass.getSuperclass()) {
// Look in interfaces first
Class [] interfaces = cls.getInterfaces();
Method m = getInInterfaces(functionName, interfaces, paramTypes);
if (m != null) {
method = m;
wasFound = true;
break SEARCHPUBLIC;
}
// Look in the class
if (Modifier.isPublic(theClass.getModifiers())) {
if (debug) {
System.out.println("** Looking in public class: " + theClass);
}
try {
m = theClass.getDeclaredMethod(functionName,paramTypes);
if (Modifier.isPublic(method.getModifiers())) {
if (debug) {
System.out.println("** Public method found: " + functionName);
}
method = m;
wasFound = true;
break SEARCHPUBLIC;
}
} catch (NoSuchMethodException e) {
if (debug) {
System.out.println("** The method has no public declaration in the public class: "+ functionName);
}
// throw new ProgrammingError("The method has no public declaration in a public class: "+ functionName);
} catch (SecurityException e) {
throw new ProgrammingError("Access error inspecting method "+ functionName + ": " + e);
}
} else {
if (debug) {
System.out.println("** Class " + theClass + " is not public - not searching for method");
}
}
} // for SEARCHPUBLIC
if (!wasFound) {
throw new EcmaScriptException("The method '" + functionName + "' has no public declaration in a public class or public interface ");
}
} // if class not public
// save it
methodVector.addElement(method);
}
// If we have some, cache them
Method [] methods = null;
int nmbMethods = methodVector.size();
if (nmbMethods>0) {
if (debug) System.out.println("** " + nmbMethods + " methods named: '" + functionName + "' + found, add to class cache");
methods = new Method[nmbMethods];
methodVector.copyInto(methods);
if (publicMethods==null) {
publicMethods = new Hashtable();
}
publicMethods.put(functionName, methods);
} else {
if (debug) System.out.println("** No method named '" + functionName + "' found");
}
return methods;
}
/**
* Get the list of public bean method in this bean, by name (the
* methods may be static or instance!).
*
* @param functionName The nam of the method being looked up
* @param cls The class of the method being looked up
* @return The method array or null if none found or in case of error
*/
synchronized public static Method[] lookupBeanMethod(String functionName, Class cls) {
ClassInfo classInfo = ClassInfo.ensureClassInfo(cls);
return classInfo.cachedBeanMethodLookup(functionName, cls);
}
/**
* Get a bean method list, where method matched in name and class.
* The method lists a cached in this ClassInfo object (which must be
* for the specified class).
* <P>Only the public variant of a method is returned.
* <P>If a method was not found, the cache is not modified, and a new
* slow lookup will be done on next search. The case is rare enought
* that it is better to keep the code simpler.
*
* @param functionName The name of the function
* @param cls The class in which the function is defined
* @return The list of methods or null in case of error or if none found.
*/
private Method [] cachedBeanMethodLookup(String functionName, Class cls) {
boolean debug = ESLoader.isDebugJavaAccess();
if (beanMethods != null) {
if (debug) System.out.println("** Method descriptor for bean '" +
cls + "' found in cache");
Method [] methods = (Method []) beanMethods.get(functionName);
if (methods!= null) {
if (debug) System.out.println("** " + methods.length +
" method(s) named '" + functionName + "' found in cache");
return methods;
}
}
// Not in cache, find if any matching the same name can be found
if (debug) System.out.println("** No method named '" +
functionName + "' found in bean cache, lookup started");
// Do we have a cached BeanInfo ? create it if no
if (beanInfo == null) {
try {
beanInfo = Introspector.getBeanInfo(cls);
} catch (IntrospectionException e) {
if (debug) System.out.println(" ** Error getting beaninfo: " + e);
return null;
}
}
MethodDescriptor [] allDescriptors = beanInfo.getMethodDescriptors();
Vector methodVector = new Vector(allDescriptors.length);
for (int i=0; i<allDescriptors.length; i++) {
Method method = allDescriptors[i].getMethod();
if (debug) System.out.println("** Method examined: " + method.toString());
if (!allDescriptors[i].getName().equals(functionName)) continue;
// Method has same name, some tuning neede:
// If the class itself is not public, there is an access error if
// ther method is invoked on it - the identical method in the
// superclass must be looked at.
// I am not too sure of what happens if the method is defined in an
// interface...
if (!Modifier.isStatic(cls.getModifiers()) && !Modifier.isPublic(cls.getModifiers())) {
if (debug) System.out.println("** Bean class " + cls +
" is not public, examining superclasses to find proper method descriptor");
SEARCHPUBLIC:
for (Class theClass=cls;theClass!=null;theClass=theClass.getSuperclass()) {
if (Modifier.isPublic(theClass.getModifiers())) {
if (debug) {
System.out.println("** Looking in public superlass: " + theClass);
}
try {
Class[] paramTypes = method.getParameterTypes();
Method m = theClass.getDeclaredMethod(functionName,paramTypes);
if (Modifier.isPublic(method.getModifiers())) {
if (debug) {
System.out.println("** Public method found: " + functionName);
}
method = m;
break SEARCHPUBLIC;
}
} catch (NoSuchMethodException e) {
throw new ProgrammingError("Error inspecting method "+ functionName + ": " + e);
} catch (SecurityException e) {
throw new ProgrammingError("Acess error inspecting method "+ functionName + ": " + e);
}
} else {
if (debug) {
System.out.println("** Superlass " + theClass + " is not public");
}
}
} // for
} // if class not public
// save it
methodVector.addElement(method);
}
// If we have some, cache them
Method [] methods = null;
int nmbMethods = methodVector.size();
if (nmbMethods>0) {
if (debug) System.out.println("** " + nmbMethods + " methods named: '"
+ functionName + "' + found, add to bean cache");
methods = new Method[nmbMethods];
methodVector.copyInto(methods);
if (beanMethods==null) {
beanMethods = new Hashtable();
}
beanMethods.put(functionName, methods);
} else {
if (debug) System.out.println("** No bean method named: '" +
functionName + "' + found");
}
return methods;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,298 @@
// EcmaScriptFunctionVisitor.java
// FESI Copyright (c) Jean-Marc Lugrin, 1999
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package FESI.Interpreter;
import FESI.Parser.*;
import FESI.AST.*;
import java.util.Vector;
import FESI.Exceptions.*;
import FESI.Data.*;
/**
* The function visitor use the visitor pattern to iterate the
* parsed code. It examine all function declarations and assign
* them to their global variable.
* <P>The function declarations will be ignored by the evaluation
* visitor (the tree is not modified).
*/
public class EcmaScriptFunctionVisitor
implements EcmaScriptVisitor,
EcmaScriptConstants
{
// The visitor work on behalf on an evaluator
private Evaluator evaluator = null;
private boolean debug = false;
private EvaluationSource currentEvaluationSource = null;
private ESObject currentObject;
/**
* Create a new visitor
* @param evaluator On behalf of this evaluator
*/
public EcmaScriptFunctionVisitor(Evaluator evaluator) {
super();
this.evaluator = evaluator;
}
/**
* Process all function declarations in ths parse tree
* @param node The parse tree
* @evaluationSource A description of the source for error messages
*/
public void processFunctionDeclarations(ASTProgram node,
EvaluationSource evaluationSource ) {
if (debug) System.out.println("processFunctionDeclarations: " + node);
if (currentEvaluationSource!=null) throw new ProgrammingError("illegal recursive function definition");
this.currentEvaluationSource = evaluationSource;
try {
node.jjtAccept(this, null);
} finally {
this.currentEvaluationSource =null;
}
}
public void processFunctionDeclarations(ASTProgram node,
EvaluationSource evaluationSource,
ESObject currentObject ) {
this.currentObject = currentObject;
if (debug) System.out.println("processFunctionDeclarations: " + node);
if (currentEvaluationSource!=null) throw new ProgrammingError("illegal recursive function definition");
this.currentEvaluationSource = evaluationSource;
try {
node.jjtAccept(this, null);
} finally {
this.currentEvaluationSource =null;
}
}
/*--------------------------------------------------------------------
* The following routines implement the walking process
* Irrelevant parts of the tree are skipped
*------------------------------------------------------------------*/
private void badAST() {
throw new ProgrammingError("Bad AST walk in EcmaScriptFunctionVisitor");
}
// The dispatching is by node type - if the specific visitor
// is not implemented, then this routine is called
public Object visit(SimpleNode node, Object data) {
badAST();
return data;
}
public Object visit(ASTProgram node, Object data) {
data = node.childrenAccept(this, data);
return data;
}
public Object visit(ASTStatementList node, Object data) {
badAST();
return data;
}
public Object visit(ASTFunctionDeclaration node, Object data) {
int nChildren = node.jjtGetNumChildren();
if (nChildren!=3) {
throw new ProgrammingError("Bad AST in function declaration");
}
ASTIdentifier idNode = (ASTIdentifier) (node.jjtGetChild(0));
FunctionEvaluationSource fes =
new FunctionEvaluationSource(currentEvaluationSource,
idNode.getName());
ASTFormalParameterList fpl = (ASTFormalParameterList) (node.jjtGetChild(1));
ASTStatementList sl = (ASTStatementList) (node.jjtGetChild(2));
EcmaScriptVariableVisitor varDeclarationVisitor = evaluator.getVarDeclarationVisitor();
Vector variableNames = varDeclarationVisitor.processVariableDeclarations(sl, fes);
if (debug) System.out.println("FUNC DECL: " + idNode.getName());
GlobalObject go = evaluator.getGlobalObject();
try {
ESReference newVar = new ESReference(go, idNode.getName(), idNode.hashCode());
ConstructedFunctionObject func =
ConstructedFunctionObject.makeNewConstructedFunction(
evaluator,
idNode.getName(),
fes,
node.getSourceString(),
fpl.getArguments(),
variableNames,
sl);
if (currentObject != null)
currentObject.putHiddenProperty (idNode.getName(), func);
else
evaluator.putValue(newVar, func);
} catch (EcmaScriptException e) {
e.printStackTrace();
throw new ProgrammingError("Unexpected error registering function" + e.getMessage());
}
return data;
}
public Object visit(ASTFormalParameterList node, Object data) {
badAST();
return data;
}
public Object visit(ASTStatement node, Object data) {
// Ignore statements for function declaration visitors
return data;
}
public Object visit(ASTVariableDeclaration node, Object data) {
badAST();
return data;
}
public Object visit(ASTIfStatement node, Object data) {
badAST();
return data;
}
public Object visit(ASTContinueStatement node, Object data) {
badAST();
return data;
}
public Object visit(ASTWhileStatement node, Object data) {
badAST();
return data;
}
public Object visit(ASTForStatement node, Object data) {
badAST();
return data;
}
public Object visit(ASTForInStatement node, Object data) {
badAST();
return data;
}
public Object visit(ASTForVarStatement node, Object data) {
badAST();
return data;
}
public Object visit(ASTForVarInStatement node, Object data) {
badAST();
return data;
}
public Object visit(ASTBreakStatement node, Object data) {
badAST();
return data;
}
public Object visit(ASTReturnStatement node, Object data) {
badAST();
return data;
}
public Object visit(ASTWithStatement node, Object data) {
badAST();
return data;
}
public Object visit(ASTThisReference node, Object data) {
badAST();
return data;
}
public Object visit(ASTCompositeReference node, Object data) {
badAST();
return data;
}
public Object visit(ASTFunctionCallParameters node, Object data) {
badAST();
return data;
}
public Object visit(ASTPropertyValueReference node, Object data) {
badAST();
return data;
}
public Object visit(ASTPropertyIdentifierReference node, Object data) {
badAST();
return data;
}
public Object visit(ASTAllocationExpression node, Object data) {
badAST();
return data;
}
public Object visit(ASTOperator node, Object data) {
badAST();
return data;
}
public Object visit(ASTPostfixExpression node, Object data) {
badAST();
return data;
}
public Object visit(ASTUnaryExpression node, Object data) {
badAST();
return data;
}
public Object visit(ASTBinaryExpressionSequence node, Object data) {
badAST();
return data;
}
public Object visit(ASTAndExpressionSequence node, Object data) {
badAST();
return data;
}
public Object visit(ASTOrExpressionSequence node, Object data) {
badAST();
return data;
}
public Object visit(ASTConditionalExpression node, Object data) {
badAST();
return data;
}
// Can we really have a cascade ?
public Object visit(ASTAssignmentExpression node, Object data) {
badAST();
return data;
}
public Object visit(ASTExpressionList node, Object data) {
badAST();
return data;
}
public Object visit(ASTEmptyExpression node, Object data) {
badAST();
return data;
}
public Object visit(ASTLiteral node, Object data) {
badAST();
return data;
}
public Object visit(ASTIdentifier node, Object data) {
badAST();
return data;
}
}

View file

@ -0,0 +1,274 @@
// EcmaScriptVariableVisitor.java
// FESI Copyright (c) Jean-Marc Lugrin, 1999
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package FESI.Interpreter;
import FESI.Parser.*;
import FESI.AST.*;
import FESI.Exceptions.*;
import java.util.Vector;
/**
* The variable visitor use the visitor pattern to iterate the
* parsed code. It examine all variable declaration at the current
* level (it does not recurse in functions) and return the
* list of variables as a vector.
* <P>The variable declarations will be ignored by the evaluation
* visitor (the tree is not modified).
*/
public class EcmaScriptVariableVisitor
implements EcmaScriptVisitor,
EcmaScriptConstants
{
// The visitor work on behalf on an evaluator
private Evaluator evaluator = null;
private boolean debug = false;
private Vector variableList = null;
/**
* Create a new visitor
* @param evaluator On behalf of this evaluator
*/
public EcmaScriptVariableVisitor(Evaluator evaluator) {
super();
this.evaluator = evaluator;
}
/**
* Process all variable declarations at the global level
* @param node The parse tree
* @evaluationSource A description of the source for error messages
* @return A vector of variables
*/
public Vector processVariableDeclarations(ASTProgram node,
EvaluationSource evaluationSource) {
if (debug) System.out.println("processVariableDeclarations for program: " + node);
variableList = new Vector();
node.jjtAccept(this, evaluationSource);
return variableList;
}
/**
* Process all variable declarations at the statement list level
* @param node The parse tree
* @evaluationSource A description of the source for error messages
* @return A vector of variables
*/
public Vector processVariableDeclarations(ASTStatementList node,
EvaluationSource evaluationSource) {
if (debug) System.out.println("processVariableDeclarations for function body: " + node);
variableList = new Vector();
node.jjtAccept(this, evaluationSource);
return variableList;
}
/*--------------------------------------------------------------------
* The following routines implement the walking process
* Irrelevant parts of the tree are skipped
*------------------------------------------------------------------*/
private void badAST() {
throw new ProgrammingError("Bad AST walk in EcmaScriptVariableVisitor");
}
// The dispatching is by node type - if the specific visitor
// is not implemented, then this routine is called
public Object visit(SimpleNode node, Object data) {
badAST();
return data;
}
public Object visit(ASTProgram node, Object data) {
data = node.childrenAccept(this, data);
return data;
}
public Object visit(ASTStatementList node, Object data) {
data = node.childrenAccept(this, data);
return data;
}
public Object visit(ASTFunctionDeclaration node, Object data) {
; // ignore function declarations in this mode
return data;
}
public Object visit(ASTFormalParameterList node, Object data) {
badAST();
return data;
}
public Object visit(ASTStatement node, Object data) {
data = node.childrenAccept(this, data);
return data;
}
public Object visit(ASTVariableDeclaration node, Object data) {
int nChildren = node.jjtGetNumChildren();
if (nChildren<1 || nChildren>2) {
throw new ProgrammingError("Bad AST in variable declaration");
}
ASTIdentifier idNode = (ASTIdentifier) (node.jjtGetChild(0));
if (debug) System.out.println("VAR DECL: " + idNode.getName());
variableList.addElement(idNode.getName());
//try {
// evaluator.createVariable(idNode.getName());
//} catch (EcmaScriptException e) {
// e.printStackTrace();
// throw new ProgrammingError(e.getMessage());
//}
return data;
}
public Object visit(ASTIfStatement node, Object data) {
data = node.childrenAccept(this, data);
return data;
}
public Object visit(ASTContinueStatement node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTWhileStatement node, Object data) {
data = node.childrenAccept(this, data);
return data;
}
public Object visit(ASTForStatement node, Object data) {
data = node.childrenAccept(this, data);
return data;
}
public Object visit(ASTForInStatement node, Object data) {
data = node.childrenAccept(this, data);
return data;
}
public Object visit(ASTForVarStatement node, Object data) {
data = node.childrenAccept(this, data);
return data;
}
public Object visit(ASTForVarInStatement node, Object data) {
data = node.childrenAccept(this, data);
return data;
}
public Object visit(ASTBreakStatement node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTReturnStatement node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTWithStatement node, Object data) {
node.setEvaluationSource(data);
data = node.childrenAccept(this, data);
return data;
}
public Object visit(ASTThisReference node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTCompositeReference node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTFunctionCallParameters node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTPropertyValueReference node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTPropertyIdentifierReference node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTAllocationExpression node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTOperator node, Object data) {
badAST();
return data;
}
public Object visit(ASTPostfixExpression node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTUnaryExpression node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTBinaryExpressionSequence node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTAndExpressionSequence node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTOrExpressionSequence node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTConditionalExpression node, Object data) {
; // no internal variable declarations possible
return data;
}
// Can we really have a cascade ?
public Object visit(ASTAssignmentExpression node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTExpressionList node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTEmptyExpression node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTLiteral node, Object data) {
; // no internal variable declarations possible
return data;
}
public Object visit(ASTIdentifier node, Object data) {
; // no internal variable declarations possible
return data;
}
}

View file

@ -0,0 +1,69 @@
// EvaluationSource.java
// FESI Copyright (c) Jean-Marc Lugrin, 1999
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package FESI.Interpreter;
/**
* Abstract declaration of an evaluation source. An evaluation
* source is used to describe the source of a program for
* error messages and debugging purpose.
*/
public abstract class EvaluationSource {
/**
* The end of line string for this machine.
*/
protected String eol = System.getProperty("line.separator", "\n");
/**
* The previous source in case of multi-level evaluation.
*/
protected EvaluationSource previousSource = null;
/**
* Create a evaluation source linked to the previous source
* (which can be null)
*/
public EvaluationSource(EvaluationSource previousSource) {
super();
this.previousSource = previousSource;
}
/**
* Return a description of the evaluation source. Must be
* implemented for each specific evaluation source.
*/
abstract protected String getEvaluationSourceText();
/**
* Display the description of the evaluation source
*/
public String toString() {
return getEvaluationSourceText();
}
/**
* Return an evaluation source number if defined
* @return -1 if not defined
*/
public int getLineNumber() {
if (previousSource != null) {
return previousSource.getLineNumber();
}
return -1; // undefined
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,419 @@
/*
*
* @(#) EncapsulatedEventAdaptor.java 1.4@(#)
*
* Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*
*/
/**
* <p>
* FESI.Interpreter.EventAdaptor
* </p>
*
* @version 1.0
* @author Laurence P. G. Cable.
*/
package FESI.Interpreter;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import FESI.Data.ESWrapper;
import FESI.Exceptions.*;
import java.util.EventObject;
import java.util.EventListener;
import java.beans.BeanInfo;
import java.beans.EventSetDescriptor;
import java.beans.Introspector;
import java.beans.IntrospectionException;
/**
* <p>
* FESI.Interpreter.EventAdaptor is an abstract base
* class designed to support the dynamic generation of java.util.EventListener
* sub-interface adaptor classes.
* </p>
* <p>
* These dynamically generated adaptor classes can be used to take arbitrary
* events and call EcmaScript handlers.
* </p>
* <p>
* Using this dynamic adaptor class, objects that emits events on arbitrary`
* sub-interfaces of java.util.EventListener can be encapsulated and delivered
* onto a single EventListener interface, this allowing late binding of event
* behaviors and filtering applications.
* </p>
*
* <P>see sunw.demo.encapsulatedEvents.EncapsulatedEvent</P>
* <P>see sunw.demo.encapsulatedEvents.EncapsulatedEventListener</P>
* <P>see sunw.demo.encapsulatedEvents.EncapsulatedEventException</P>
* <P>see sunw.demo.encapsulatedEvents.EncapsulatedEventAdaptorGenerator</P>
*/
public abstract class EventAdaptor {
/**
* The Event Source
*/
protected Object source;
/**
* The target Wrapper object
*/
protected ESWrapper wrapper;
/**
* The Event Source's add<T>Listener method
*/
protected Method addAdaptorMethod;
/**
* The Event Source's remove<T>Listener method
*/
protected Method removeAdaptorMethod;
/**
* Generate an Adaptor instance for the Listener Interface and Event Source
*
* @param lc The java.lang.Class object for the listener to adapt.
* @param s The source object that the adaptor will listen to.
* @param w The wrapper object that the adaptor will listen to.
*
* @return The newly instantiated dynamic adaptor
*
* @exception ClassNotFoundException If class cannot be loaded
* @exception IntrospectionException If error in introspection
* @exception InstantiationException If class found but cannot be instantiated
* @exception IllegalAccessException If operationis not allowed
*/
public static EventAdaptor getEventAdaptor(Class lc, Object s, ESWrapper w)
throws ClassNotFoundException, InstantiationException,
IllegalAccessException, IntrospectionException {
Class eeac = null;
EventAdaptor eea = null;
BeanInfo sbi = Introspector.getBeanInfo(s.getClass());
EventSetDescriptor[] sesd = sbi.getEventSetDescriptors();
/*
* first validate that the event source emits event on the interface
* specified.
*/
if (validateEventSource(sesd, lc) == -1) {
throw new IllegalArgumentException("Object: " +
s +
" does not source: " +
lc.getName()
);
}
// generate the adaptor class
eeac = EventAdaptor.getEventAdaptorClass(lc);
// instantiate an instance of it ...
eea = (EventAdaptor)eeac.newInstance();
eea.setWrapper(w);
// and register the adaptor with the event source
try {
eea.setSource(s);
} catch (Exception e) {
throw new RuntimeException("Failed to register with source");
}
return eea;
}
/**
*
* @param lc The java.lang.Class object for the listener to adapt.
*
* @exception ClassNotFoundException If class cannot be loaded
*
* @return The Class object for the newly generated dynamic adaptor class.
*/
public static Class getEventAdaptorClass(Class lc) throws ClassNotFoundException {
if (!java.util.EventListener.class.isAssignableFrom(lc)) {
throw new IllegalArgumentException("Class is not a subinterface of java.util.EventListenerEventListener");
}
return EventAdaptorGenerator.getAdaptorClassForListenerClass(lc);
}
/**
* <p> default constructor.
*/
protected EventAdaptor() { super(); }
/**
* @return the java.lang.Class object for the java.util.EventListener subinterface this instance adapts.
*/
abstract public Class getListenerClass();
/**
* @return the name of the java.util.EventListener subinterface this instance adapts.
*/
public String getListenerClassName() {
return getListenerClass().getName();
}
/**
* @return the event source object that this instance is adapting.
*/
public synchronized Object getSource() { return source; }
public synchronized ESWrapper getWarepper() { return wrapper; }
public synchronized void setWrapper(ESWrapper w) { wrapper = w; }
/**
* @param sesd the EventSetDescriptor[] from the prospective event source.
* @param lc the java.lang.Class for the EventListener we are adapting.
* @return the index of the matching EventSetDescriptor or -1.
*/
private static int validateEventSource(EventSetDescriptor[] sesd, Class lc) {
for (int i = 0; i < sesd.length; i++)
if (lc.equals(sesd[i].getListenerType())) return i;
return -1;
}
/**
* <p>
* setSource sets the adaptor instance to listen to the specified
* source. This operation will fail if the source does not emit events
* on the EventListener subinterface this adaptor implements.
* </p>
*
* @param s the prospective event source.
*
* @exception IntrospectionException If any error with introspection
* @exception Exception If any other error
*/
public synchronized void setSource(Object s)
throws IntrospectionException, Exception {
if (source != null && s != null && source.equals(s)) return;
if (source != null) removeAdaptorFromSource(); // unregister current
if (s == null) {
source = null;
addAdaptorMethod = null;
removeAdaptorMethod = null;
return;
}
BeanInfo sbi = Introspector.getBeanInfo(s.getClass());
EventSetDescriptor[] sesd = sbi.getEventSetDescriptors();
int i;
if ((i = validateEventSource(sesd, getListenerClass())) == -1) {
throw new IllegalArgumentException("Object: " +
s +
" does not source: " +
getListenerClassName()
);
}
// update state
Object olds = source;
Method oldam = addAdaptorMethod;
Method oldrm = removeAdaptorMethod;
source = s;
addAdaptorMethod = sesd[i].getAddListenerMethod();
removeAdaptorMethod = sesd[i].getRemoveListenerMethod();
try {
addAdaptorToSource(); // register with new source
} catch (Exception e) {
// something went wrong ... restore previous state.
source = olds;
addAdaptorMethod = oldam;
removeAdaptorMethod = oldrm;
if (source != null) addAdaptorToSource();
throw e; // reraise problem
}
}
/**
*
* <p>
* This method is called from simple EventListener method stubs of
* dynamic subclasses in order to create and EncapsulatedEvent and
* deliver it to the registered listeners.
* </p>
*
* @param e The EventObject being encapsulated
* @param lm The jav.lang.reflect.Method describing the listener Method.
*
* @exception Exception If any error occured in dispatch event
*/
protected void fire(EventObject e, Method lm) throws Exception {
Object [] a = new Object [] {e};
// System.out.println("FIRE: " + lm.getName() + " for " + getListenerClass().getName());
wrapper.dispatchEvent(a, getListenerClass(), lm);
}
/**
* <p>
* This method is called from cracked EventListener method stubs of
* dynamic subclasses in order to create and EncapsulatedEvent and
* deliver it to the registered listeners.
* </p>
*
* @param a The cracked Event arguments being encapsulated
* @param lm The jav.lang.reflect.Method describing the listener Method.
*
* @exception Exception If any error occured in dispatch event
*/
protected void fire(Object[] a, Method lm) throws Exception {
//System.out.println("FIRE: " + lm.getName() + " for " + getListenerClass().getName());
wrapper.dispatchEvent(a, getListenerClass(), lm);
}
/**
* <p>
* EncapsulatedEventListener's may raise exceptions to the originating
* event source through an adaptor by throwing the actual exception within
* an instance of EncapsulatedEventException.
* </p>
* <p>
* This method is called to verify that it is allowable to re-raise the
* exception from the adaptor to the source, by checking that the listener
* method on the adaptor that the source delivered the original event to
* is permitted to throw such an exception, otherwise a RuntimeException
* is raised.
* </p>
*
* @param ex the exception thrown by the listener
* @param eel the listener instance that threw it
* @param lm the adaptors listener method that must re-raise it
*
*/
/*
protected final void handleEventException(EncapsulatedEventException ex, EncapsulatedEventListener eel, Method lm) throws Exception {
Class ec = ex.getExceptionClass();
Class[] ext = lm.getExceptionTypes();
// let's see if the Exception encapsulated is one the source is expecting
// if it is then throw it.
for (int i = 0; i < ext.length; i++) {
if (ext[i].isAssignableFrom(ec)) throw ex.getException();
}
// if we get here then the Exception the listener is trying
// to throw isnt one the source is expecting ... so throw it as a RTE
throw new RuntimeException("Event Source [" +
source +
"] is not prepared to catch [" +
ex.getException() +
"] from listener [" +
eel
);
}
*/
/**
* Adds this Adaptor to the Event Source
*/
protected void addAdaptorToSource() {
try {
Object[] args = new Object[1];
args[0] = this;
addAdaptorMethod.invoke(source, args);
} catch (InvocationTargetException ite) {
throw new ProgrammingError("cannot add adaptor [" +
this +
"] to source [" +
source +
"] InvocationTargetException: " +
ite.getMessage()
);
} catch (IllegalAccessException iae) {
throw new ProgrammingError("cannot add adaptor [" +
this +
"] to source [" +
source +
"] IllegalAccessException: " +
iae.getMessage()
);
}
}
/**
* Removes this Adaptor instance from the Event Source
*/
protected void removeAdaptorFromSource() {
try {
Object[] args = new Object[1];
args[0] = this;
removeAdaptorMethod.invoke(source, args);
} catch (InvocationTargetException ite) {
System.err.println("cannot remove adaptor [" +
this +
"] from source [" +
source +
"] InvocationTargetException: " +
ite.getMessage()
);
} catch (IllegalAccessException iae) {
System.err.println("cannot remove adaptor [" +
this +
"] from source [" +
source +
"] IllegalAccessException: " +
iae.getMessage()
);
}
}
}

View file

@ -0,0 +1,262 @@
/*
*
* @(#) EventAdaptorGenerator.java 1.3@(#)
*
* Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*
*/
/**
* <p>
* FESI.Interpreter.EventAdaptorGenerator
* </p>
*
* @version 1.0
* @author Laurence P. G. Cable
*/
package FESI.Interpreter;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.ClassLoader;
import java.lang.ClassFormatError;
import java.util.Hashtable;
import FESI.ClassFile.EventAdaptorClassFile;
/**
* <p>
* The EventAdaptorGenerator is the class responsible for
* dynamically generating classes that adapt arbitrary sub-interfaces of
* java.util.EventListener to FESI.ClassFile.EventListener.
* <p>
* It is largely derived of EventAdaptorGenerator.java(1.3) of Sun,
* delivered as an example of for the bean development tools.
* </p>
* <p>
* The dynamically generated adaptor class is implemented as a subclass of
* FESI.ClassFile and implements the specified
* sub-interface of java.util.EventListener. Each listener method of the
* sub-interface calls into FESI.ClassFile.EncapsulatedEvent to
* deliver the event to the proper EcmaScript event processing routine.
* </p>
*/
public final class EventAdaptorGenerator extends ClassLoader {
private static String packagePrefix = "FESI.ClassFile";
private static String adaptorInfix = ".DYN_EE_ADAPTOR.";
private static String adaptorClassNamePrefix = packagePrefix + adaptorInfix;
/**
* @return debug status
*/
private boolean debug() { return false; }
/*
* one instance of the Generator ...
*/
private static EventAdaptorGenerator generator =
new EventAdaptorGenerator();
/**
* constructs a generator
*/
private EventAdaptorGenerator() { super(); }
/**
* invokes the ClassLoader to load the named class
*
* @param className the name of the class to load
*
* @return the newly loaded Class
*
* @exception ClassNotFoundException if the class cannnot be loaded
*
* @see java.lang.ClassLoader
*/
private static Class loadClassNamed(String className)
throws ClassNotFoundException {
return generator.loadClass(className);
}
/**
* invoke the Class Loader to generate and load a dynamically generated
* adaptor subclass of EventAdaptor to adapt to the specified
* sub-interface of java.util.EventListener.
*
* @param lc The java.lang.Class object for the sub-interface to adapt
*
* @return the newly loaded dynamic adaptor class.
*
* @throws ClassNotFoundException
*/
static Class getAdaptorClassForListenerClass(Class lc)
throws ClassNotFoundException {
// we assume that lc is a subinterface of java.util.EventListener
return loadClassNamed(mapListenerNameToAdaptorName(lc.getName()));
}
/**
* invoke the Class Loader to generate and load a dynamically generated
* adaptor subclass of EncapsulatedEventAdaptor.
*
* @return the newly loaded class
*
* @throw ClassNotFoundException
*/
static Class getAdaptorClassForListenerClass(String lcn)
throws ClassNotFoundException {
// we assume that lc is a subinterface of java.util.EventListener
return loadClassNamed(mapListenerNameToAdaptorName(lcn));
}
/**
* loadClass will lookup classes with its Class Loader or via the
* system, unless the class requested is a dynamic adaptor class,
* then we invoke the code generator to create the adaptor class
* on the fly.
*
* @return the newly loaded dynamic adaptor class.
*
* @exception ClassNotFoundException If the class cannot be found
*/
protected Class loadClass(String className, boolean resolve)
throws ClassNotFoundException {
Class c = findLoadedClass(className); // check the cache
if (debug()) System.err.println("loadClass(" + className + ")");
if (c == null) { // not in cache
if (isAdaptorClassName(className)) { // is this an adaptor?
// generate an adaptor to this sub-interface
c = generateAdaptorClass(className);
} else try { // regular class
ClassLoader mycl = this.getClass().getClassLoader();
// look for the requeseted class elsewhere ...
if (mycl != null) {
c = mycl.loadClass(className); // try the CL that loaded me
}
if (c == null) {
c = findSystemClass(className); // try system for class
}
} catch (NoClassDefFoundError ncdfe) {
throw new ClassNotFoundException(ncdfe.getMessage());
}
}
if (c != null) { // class found?
if (resolve) resolveClass(c);
} else
throw new ClassNotFoundException(className);
if (debug()) System.err.println("loaded: " + c.getName());
return c;
}
/**
* map the EventListener subclass name to the synthetic name for a
* dynamically generated adaptor class.
*
* @return the synthesised adaptor class name.
*/
private static String mapListenerNameToAdaptorName(String listenerName) {
return adaptorClassNamePrefix + listenerName;
}
/**
* Checks to determine if class name is that of a dynamically generated
* EncapsulatedEventAdaptor Class as created by this Generator.
*
* This is defined to be so, iff the class prefix matches the one
* we generate.
*
* @return is this the "name" of an adaptor class?
*/
private static boolean isAdaptorClassName(String className) {
return className.startsWith(adaptorClassNamePrefix);
}
/**
* @return the name of the sub-interface we are adapting from the name of the adaptor class.
*/
public static String getBaseNameFromAdaptorName(String className) {
return ((className != null && isAdaptorClassName(className))
? className.substring(adaptorClassNamePrefix.length())
: null
);
}
/**
* generates the dynamic Adaptor class to bridge the EventListener
* interface to the EncapsulatedListener interface.
*
* @return the newly generated adaptor class.
*
*/
private Class generateAdaptorClass(String className) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
byte[] cimpl = null;
Class clazz = null;
if (debug()) System.err.println("generateAdaptorClass(" + className + ")");
try { // generate the classfile into the stream
new EventAdaptorClassFile(className, (OutputStream)baos);
} catch (IOException ioe) {
return null;
} catch (ClassNotFoundException cnfe) {
return null;
}
cimpl = baos.toByteArray(); // get the classfile as an array of bytes
// now "try to" intern it into the runtime ...
try {
clazz = defineClass(className, cimpl, 0, cimpl.length);
} catch (ClassFormatError ex) {
System.err.println("Failed to define adaptor for " + className);
System.err.println("Caught: " + ex);
ex.printStackTrace();
}
return clazz;
}
}

View file

@ -0,0 +1,78 @@
// FESIResourceConnection.java
// FESI Copyright (c) Jean-Marc Lugrin, 1999
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package FESI.Interpreter;
import FESI.Data.ESLoader;
import java.net.*;
import java.io.*;
public class FESIResourceConnection extends URLConnection {
private static boolean debug = false;
private Object resource; // the resource we are fetching
private String cookie; // identification of the loader instance to use
private String name; // name of the resource
private final String prefix = LocalClassLoader.urlPrefix;
private final int prefixLength = prefix.length();
public FESIResourceConnection (URL url)
throws MalformedURLException, IOException
{
super(url);
if (ESLoader.isDebugLoader()) System.out.println(" ** new FESIResourceConnection('"+url+"')");
String file = url.getFile();
if (file.startsWith("/")) {
file = file.substring(1);
}
if (! file.startsWith(prefix)) {
throw new MalformedURLException("FESIResource file should start with /" + prefix);
}
cookie = file.substring(prefixLength, file.indexOf("/+/"));
name = file.substring(file.indexOf("/+/")+3);
if (ESLoader.isDebugLoader()) System.out.println(" ** cookie: " + cookie + ", name: " + name);
}
public void connect() throws IOException {
if (ESLoader.isDebugLoader()) System.out.println(" ** Connect: cookie: " + cookie + ", name: " + name);
Object o = LocalClassLoader.getLocalResource(cookie, name);
if (o == null) {
resource = null;
return;
} else {
resource = o;
}
}
public Object getContent() throws IOException {
if (!connected) {
connect();
}
return resource;
}
public InputStream getInputStream() throws IOException {
if (!connected) {
connect();
}
if (resource instanceof InputStream) {
return (InputStream) resource;
}
return LocalClassLoader.getLocalResourceAsStream(cookie, name);
}
}

View file

@ -0,0 +1,512 @@
/*
* FesiHashtable, adapted from:
* @(#)Hashtable.java 1.41 97/01/28
*
* Copyright (c) 1995, 1996 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*
* CopyrightVersion 1.1_beta
*
* Adapted by JM Lugrin for FESI
* Synchronization removed (speed up, evaluator already synchronized)
* Data type specific for object property list.
* Externally provided hash code (to avoid recalculating the hash code.
* Support the hidden and readonly properties (not enforced by this class).
* Optimize compare for interned strings and poor hash function.
*
*/
package FESI.Interpreter;
import FESI.Data.ESValue;
import java.util.Enumeration;
/**
* Hashtable collision list.
*/
class HashtableEntry {
int hash;
String key;
ESValue value;
HashtableEntry next;
boolean hidden;
boolean readonly;
protected Object clone() {
HashtableEntry entry = new HashtableEntry();
entry.hash = hash;
entry.key = key;
entry.value = value;
entry.hidden = hidden;
entry.readonly = readonly;
entry.next = (next != null) ? (HashtableEntry)next.clone() : null;
return entry;
}
}
/**
* This class implements a hashtable, which maps keys to values. Any
* non-<code>null</code> object can be used as a key or as a value.
* <p>
* To successfully store and retrieve objects from a hashtable, the
* objects used as keys must implement the <code>hashCode</code>
* method and the <code>equals</code> method.
* <p>
* An instance of <code>Hashtable</code> has two parameters that
* affect its efficiency: its <i>capacity</i> and its <i>load
* factor</i>. The load factor should be between 0.0 and 1.0. When
* the number of entries in the hashtable exceeds the product of the
* load factor and the current capacity, the capacity is increased by
* calling the <code>rehash</code> method. Larger load factors use
* memory more efficiently, at the expense of larger expected time
* per lookup.
* <p>
* If many entries are to be made into a <code>Hashtable</code>,
* creating it with a sufficiently large capacity may allow the
* entries to be inserted more efficiently than letting it perform
* automatic rehashing as needed to grow the table.
* <p>
* This example creates a hashtable of numbers. It uses the names of
* the numbers as keys:
* <p><blockquote><pre>
* Hashtable numbers = new Hashtable();
* numbers.put("one", new Integer(1));
* numbers.put("two", new Integer(2));
* numbers.put("three", new Integer(3));
* </pre></blockquote>
* <p>
* To retrieve a number, use the following code:
* <p><blockquote><pre>
* Integer n = (Integer)numbers.get("two");
* if (n != null) {
* System.out.println("two = " + n);
* }
* </pre></blockquote>
*
* @author Arthur van Hoff
* @version 1.41, 01/28/97
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.Object#hashCode()
* @see java.util.Hashtable#rehash()
* @since JDK1.0
*/
public
class FesiHashtable implements Cloneable {
/**
* The hash table data.
*/
private transient HashtableEntry table[];
/**
* The total number of entries in the hash table.
*/
private transient int count;
/**
* Rehashes the table when count exceeds this threshold.
*/
private int threshold;
/**
* The load factor for the hashtable.
*/
private float loadFactor;
/**
* Constructs a new, empty hashtable with the specified initial
* capacity and the specified load factor.
*
* @param initialCapacity the initial capacity of the hashtable.
* @param loadFactor a number between 0.0 and 1.0.
* @exception IllegalArgumentException if the initial capacity is less
* than or equal to zero, or if the load factor is less than
* or equal to zero.
* @since JDK1.0
*/
public FesiHashtable(int initialCapacity, float loadFactor) {
if ((initialCapacity <= 0) || (loadFactor <= 0.0)) {
throw new IllegalArgumentException();
}
this.loadFactor = loadFactor;
table = new HashtableEntry[initialCapacity];
threshold = (int)(initialCapacity * loadFactor);
}
/**
* Constructs a new, empty hashtable with the specified initial capacity
* and default load factor.
*
* @param initialCapacity the initial capacity of the hashtable.
* @since JDK1.0
*/
public FesiHashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
/**
* Constructs a new, empty hashtable with a default capacity and load
* factor.
*
* @since JDK1.0
*/
public FesiHashtable() {
this(27, 0.75f); // a smaller prime than the original 27
}
/**
* Returns the number of keys in this hashtable.
*
* @return the number of keys in this hashtable.
* @since JDK1.0
*/
public int size() {
return count;
}
/**
* Tests if this hashtable maps no keys to values.
*
* @return <code>true</code> if this hashtable maps no keys to values;
* <code>false</code> otherwise.
* @since JDK1.0
*/
public boolean isEmpty() {
return count == 0;
}
/**
* Returns an enumeration of the keys in this hashtable.
*
* @return an enumeration of the keys in this hashtable.
* @see java.util.Enumeration
* @see java.util.Hashtable#elements()
* @since JDK1.0
*/
public Enumeration keys() {
return new HashtableEnumerator(table,true);
}
/**
* Returns an enumeration of the values in this hashtable.
* Use the Enumeration methods on the returned object to fetch the elements
* sequentially.
*
* @return an enumeration of the values in this hashtable.
* @see java.util.Enumeration
* @see java.util.Hashtable#keys()
* @since JDK1.0
*/
public Enumeration elements() {
return new HashtableEnumerator(table, false);
}
/**
* Tests if the specified object is a key in this hashtable.
*
* @param key possible key.
* @return <code>true</code> if the specified object is a key in this
* hashtable; <code>false</code> otherwise.
*/
public boolean containsKey(String key, int hash) {
HashtableEntry tab[] = table;
int index = (hash & 0x7FFFFFFF) % tab.length;
for (HashtableEntry e = tab[index] ; e != null ; e = e.next) {
if ((e.key==key ) || ((e.hash == hash) && e.key.equals(key))) {
return true;
}
}
return false;
}
/**
* Returns the value to which the specified key is mapped in this hashtable.
*
* @param key a key in the hashtable.
* @param hash the key hashtable.
* @return the value to which the key is mapped in this hashtable;
* <code>null</code> if the key is not mapped to any value in
* this hashtable.
*/
public ESValue get(String key, int hash) {
HashtableEntry tab[] = table;
int index = (hash & 0x7FFFFFFF) % tab.length;
for (HashtableEntry e = tab[index] ; e != null ; e = e.next) {
if ((e.key==key ) || ((e.hash == hash) && e.key.equals(key))) {
return e.value;
}
}
return null;
}
/**
* Check if a property is hidden (return false if not present).
*
* @param key a key in the hashtable.
* @param hash the key hashtable.
* @return true if hidden.
*/
public boolean isHidden(String key, int hash) {
HashtableEntry tab[] = table;
int index = (hash & 0x7FFFFFFF) % tab.length;
for (HashtableEntry e = tab[index] ; e != null ; e = e.next) {
if ((e.key==key ) || ((e.hash == hash) && e.key.equals(key))) {
return e.hidden;
}
}
return false;
}
/**
* Check if a property is readonly (return false if not present).
*
* @param key a key in the hashtable.
* @param hash the key hashtable.
* @return true if hidden.
*/
public boolean isReadonly(String key, int hash) {
HashtableEntry tab[] = table;
int index = (hash & 0x7FFFFFFF) % tab.length;
for (HashtableEntry e = tab[index] ; e != null ; e = e.next) {
if ((e.key==key ) || ((e.hash == hash) && e.key.equals(key))) {
return e.readonly;
}
}
return false;
}
/**
* Rehashes the contents of the hashtable into a hashtable with a
* larger capacity. This method is called automatically when the
* number of keys in the hashtable exceeds this hashtable's capacity
* and load factor.
*
* @since JDK1.0
*/
protected void rehash() {
int oldCapacity = table.length;
HashtableEntry oldTable[] = table;
int newCapacity = oldCapacity * 2 + 1;
if (newCapacity<101) newCapacity = 101; // Ensure a prime
HashtableEntry newTable[] = new HashtableEntry[newCapacity];
threshold = (int)(newCapacity * loadFactor);
table = newTable;
//System.out.println("rehash old=" + oldCapacity + ", new=" + newCapacity + ", thresh=" + threshold + ", count=" + count);
for (int i = oldCapacity ; i-- > 0 ;) {
for (HashtableEntry old = oldTable[i] ; old != null ; ) {
HashtableEntry e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = newTable[index];
newTable[index] = e;
}
}
}
/**
* Maps the specified <code>key</code> to the specified
* <code>value</code> in this hashtable. Neither the key nor the
* value can be <code>null</code>.
* <p>
* The value can be retrieved by calling the <code>get</code> method
* with a key that is equal to the original key.
*
* @param key the hashtable key.
* @param hash the hash value.
* @param hidden true if the entry must not be enumerated.
* @param readonly true if the entry must not be deleted.
* @param value the value.
* @return the previous value of the specified key in this hashtable,
* or <code>null</code> if it did not have one.
* @exception NullPointerException if the key or value is
* <code>null</code>.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.util.Hashtable#get(java.lang.Object)
* @since JDK1.0
*/
public ESValue put(String key, int hash, boolean hidden, boolean readonly, ESValue value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
HashtableEntry tab[] = table;
int index = (hash & 0x7FFFFFFF) % tab.length;
for (HashtableEntry e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
ESValue old = e.value;
e.value = value;
return old;
}
}
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
return put(key, hash, hidden, readonly, value);
}
// Creates the new entry.
HashtableEntry e = new HashtableEntry();
e.hash = hash;
e.key = key;
e.value = value;
e.hidden = hidden;
e.readonly = readonly;
e.next = tab[index];
tab[index] = e;
count++;
return null;
}
/**
* Removes the key (and its corresponding value) from this
* hashtable. This method does nothing if the key is not in the hashtable.
*
* @param key the key that needs to be removed.
* @return the value to which the key had been mapped in this hashtable,
* or <code>null</code> if the key did not have a mapping.
* @since JDK1.0
*/
public ESValue remove(String key, int hash) {
HashtableEntry tab[] = table;
int index = (hash & 0x7FFFFFFF) % tab.length;
for (HashtableEntry e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
if (prev != null) {
prev.next = e.next;
} else {
tab[index] = e.next;
}
count--;
return e.value;
}
}
return null;
}
/**
* Clears this hashtable so that it contains no keys.
*
* @since JDK1.0
*/
public void clear() {
HashtableEntry tab[] = table;
for (int index = tab.length; --index >= 0; )
tab[index] = null;
count = 0;
}
/**
* Creates a shallow copy of this hashtable. The keys and values
* themselves are not cloned.
* This is a relatively expensive operation.
*
* @return a clone of the hashtable.
* @since JDK1.0
*/
public Object clone() {
try {
FesiHashtable t = (FesiHashtable)super.clone();
t.table = new HashtableEntry[table.length];
for (int i = table.length ; i-- > 0 ; ) {
t.table[i] = (table[i] != null)
? (HashtableEntry)table[i].clone() : null;
}
return t;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
/**
* Returns a rather long string representation of this hashtable.
*
* @return a string representation of this hashtable.
* @since JDK1.0
*/
public String toString() {
int max = size() - 1;
StringBuffer buf = new StringBuffer();
Enumeration k = keys();
Enumeration e = elements();
buf.append("{");
for (int i = 0; i <= max; i++) {
String s1 = k.nextElement().toString();
String s2 = e.nextElement().toString();
buf.append(s1 + "=" + s2);
if (i < max) {
buf.append(", ");
}
}
buf.append("}");
return buf.toString();
}
}
/**
* A hashtable enumerator class. This class should remain opaque
* to the client. It will use the Enumeration interface.
*/
class HashtableEnumerator implements Enumeration {
boolean keys;
int index;
HashtableEntry table[];
HashtableEntry entry;
HashtableEnumerator(HashtableEntry table[], boolean keys) {
this.table = table;
this.keys = keys;
this.index = table.length;
}
public boolean hasMoreElements() {
if (entry != null) {
return true;
}
while (index-- > 0) {
if ((entry = table[index]) != null) {
return true;
}
}
return false;
}
public Object nextElement() {
if (entry == null) {
while ((index-- > 0) && ((entry = table[index]) == null));
}
if (entry != null) {
HashtableEntry e = entry;
entry = e.next;
return keys ? ((Object) e.key) : ((Object) e.value);
}
throw new java.util.NoSuchElementException("FesiHashtableEnumerator");
}
}

View file

@ -0,0 +1,39 @@
// FileEvaluationSource.java
// FESI Copyright (c) Jean-Marc Lugrin, 1999
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package FESI.Interpreter;
/**
* Describe a file used as a source.
*/
public class FileEvaluationSource extends EvaluationSource {
private String theFileName;
/**
* Create a file source description
* @param theFileName Describe the source file name
* @param previousSource Describe the calling source
*/
public FileEvaluationSource(String theFileName, EvaluationSource previousSource) {
super(previousSource);
this.theFileName = theFileName;
}
protected String getEvaluationSourceText() {
return "in file: '" + theFileName + "'";
}
}

View file

@ -0,0 +1,43 @@
// FunctionEvaluationSource.java
// FESI Copyright (c) Jean-Marc Lugrin, 1999
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package FESI.Interpreter;
/**
* Describe a function used as a source.
*/
public class FunctionEvaluationSource extends EvaluationSource {
private String theFunctionName;
/**
* Create a function source description
* @param evaluationSource Describe the function name
* @param previousSource Describe the calling source
*/
public FunctionEvaluationSource(
EvaluationSource evaluationSource,
String theFunctionName) {
super(evaluationSource);
this.theFunctionName = theFunctionName;
}
protected String getEvaluationSourceText() {
return "of function '" + theFunctionName +"' " +
previousSource.getEvaluationSourceText();
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,44 @@
// JarEvaluationSource.java
// FESI Copyright (c) Jean-Marc Lugrin, 1999
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package FESI.Interpreter;
/**
* Describe a jar entry used as a source.
*/
public class JarEvaluationSource extends EvaluationSource {
private String theJarName;
private String theEntryName;
/**
* Create a jar source description
* @param theJarName Describe the source jar
* @param theEntryName Describe the source entry in the jar
* @param previousSource Describe the calling source
*/
public JarEvaluationSource(String theJarName, String theEntryName, EvaluationSource previousSource) {
super(previousSource);
this.theJarName = theJarName;
this.theEntryName = theEntryName;
}
protected String getEvaluationSourceText() {
return "in entry: '" + theEntryName + "' of jar: '" + theJarName + "'";
}
}

View file

@ -0,0 +1,52 @@
// LineEvaluationSource.java
// FESI Copyright (c) Jean-Marc Lugrin, 1999
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package FESI.Interpreter;
/**
* Add line number to an evaluation source
*/
public class LineEvaluationSource extends EvaluationSource {
private int theLineNumber;
/**
* Create a source description being the line number of a previous description
* @param theLineNumber Describe the line number
* @param previousSource Describe the calling source
*/
public LineEvaluationSource(int theLineNumber, EvaluationSource previousSource) {
super(previousSource);
this.theLineNumber = theLineNumber;
}
/**
* Return the string describing the line number
*/
protected String getEvaluationSourceText() {
return "at line " + theLineNumber + " " + previousSource.getEvaluationSourceText();
}
/**
* Get the line number of the error if possible
*/
public int getLineNumber() {
return theLineNumber;
}
}

View file

@ -0,0 +1,375 @@
// LocalClassLoaderjava
// FESI Copyright (c) Jean-Marc Lugrin, 1999
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package FESI.Interpreter;
import java.io.*;
import java.util.Hashtable;
import java.util.Properties;
import java.util.zip.*;
import java.net.*;
import java.awt.*;
// import sun.awt.image.*; // byte array image source
import FESI.Data.ESLoader;
import FESI.Exceptions.*;
/**
* This code is largely inspired of Java Examples in a Nutshell.
* The loaders are shared by all instance of the evaluator, they
* are therefore synchronized when needed.
**/
public class LocalClassLoader extends ClassLoader {
public final static String urlPrefix = "FESI";
public final static String resourceName = "fesiresource"; // must match class name
private static Hashtable loadersByCookie = new Hashtable(); // loaders by cookie
private static Hashtable loadersByFilename = new Hashtable(); // loaders by file name
private static final String protocolPathProp = "java.protocol.handler.pkgs";
private static int cookieCount = 1;
private static char fileSep = System.getProperty("file.separator", "/").charAt(0);
/** This is the directory from which the classes will be loaded */
private boolean asJar;
private String myCookie;
private ZipFile zipFile = null;
private File directoryFile = null;
/** The constructor. Just initialize the directory */
private LocalClassLoader(File dir) {
this.directoryFile = dir;
this.asJar = false;
myCookie = "fcl"+cookieCount++;
loadersByCookie.put(myCookie, this);
if (ESLoader.isDebugLoader()) System.out.println(" ** New class loader: " + this);
}
private LocalClassLoader(ZipFile zipFile) {
this.asJar = true;
this.zipFile = zipFile;
myCookie = "fcl"+cookieCount++;
loadersByCookie.put(myCookie, this);
if (ESLoader.isDebugLoader()) System.out.println(" ** New class loader: " + this);
}
/** The factory. Make a loader if none exist for the same source */
synchronized public static LocalClassLoader makeLocalClassLoader(String filename) throws EcmaScriptException{
LocalClassLoader loader = null;
boolean asJar = false;
File file = new File(filename);
String fullname = null;
if (file.isFile()) {
try {
fullname = file.getCanonicalPath();
} catch (IOException e) {
fullname = file.getAbsolutePath(); // Hope this work
}
loader = (LocalClassLoader) loadersByFilename.get(fullname);
if (loader == null) {
ZipFile zipFile = null;
try {
zipFile = new ZipFile(fullname);
} catch (IOException e) {
throw new EcmaScriptException("IO Error opening zip file '" + fullname + "' : " + e);
}
loader = new LocalClassLoader(zipFile);
loadersByFilename.put(fullname, loader);
} else {
if (ESLoader.isDebugLoader()) System.out.println(" ** loader in cache: " + loader);
}
} else if (file.isDirectory()) {
try {
fullname = file.getCanonicalPath();
} catch (IOException e) {
fullname = file.getAbsolutePath(); // Hope this work
}
loader = (LocalClassLoader) loadersByFilename.get(fullname);
if (loader == null) {
loader = new LocalClassLoader(file);
loadersByFilename.put(fullname, loader);
} else {
if (ESLoader.isDebugLoader()) System.out.println(" ** loader in cache: " + loader);
}
} else {
throw new EcmaScriptException("No file or directory '" + filename + "' found");
}
return loader;
}
/**
* A convenience method that calls the 2-argument form of this method
*
* @param name The name of the class
* @return the loaded class
* @exception ClassNotFoundException If class cannot be loaded
*/
public Class loadClass(String name) throws ClassNotFoundException {
return loadClass(name, true);
}
/**
* This is one abstract method of ClassLoader that all subclasses must
* define. Its job is to load an array of bytes from somewhere and to
* pass them to defineClass(). If the resolve argument is true, it must
* also call resolveClass(), which will do things like verify the presence
* of the superclass. Because of this second step, this method may be
* called to load superclasses that are system classes, and it must take
* this into account.
* @param classname The name of the class to load
* @param resolve True if class must be resolved
* @return the loaded class
* @exception ClassNotFoundException If class cannot be loaded
**/
public Class loadClass(String classname, boolean resolve)
throws ClassNotFoundException {
// if (ESLoader.isDebugLoader()) System.out.println(" ** loadClass: " + classname);
try {
// Our ClassLoader superclass has a built-in cache of classes it has
// already loaded. So, first check the cache.
Class c = findLoadedClass(classname);
// After this method loads a class, it will be called again to
// load the superclasses. Since these may be system classes, we've
// got to be able to load those too. So try to load the class as
// a system class (i.e. from the CLASSPATH) and ignore any errors
if (c == null) {
try { c = findSystemClass(classname); }
catch (Exception e) {}
}
// If the class wasn't found by either of the above attempts, then
// try to load it from a file in (or beneath) the directory
// specified when this ClassLoader object was created. Form the
// filename by replacing all dots in the class name with
// (platform-independent) file separators and by adding the
// ".class" extension.
// Alternatively try to load it from the jar file which was
// specified.
if (c == null) {
//String asEntryName = (asJar ? (classname.replace('.','/') + ".class") : (classname + ".class"));
String asEntryName = classname.replace('.',(asJar ? '/' : fileSep)) + ".class";
byte classbytes[] = getResourceBuffer(asEntryName);
if (classbytes == null) {
if (ESLoader.isDebugLoader()) System.out.println(" ** class '" + classname + "' not loaded");
throw new ClassNotFoundException("Class '" + classname + "' not foud by " + this);
} else {
if (ESLoader.isDebugLoader()) System.out.println(" ** class '" + classname + "' loaded");
// Now call an inherited method to convert those bytes into a Class
c = defineClass(classname, classbytes, 0, classbytes.length);
}
}
// If the resolve argument is true, call the inherited resolveClass
// method.
if (resolve) resolveClass(c);
// And we're done. Return the Class object we've loaded.
return c;
}
// If anything goes wrong, throw a ClassNotFoundException error
catch (Exception e) {
if (ESLoader.isDebugLoader()) System.out.println(" ** Error loading '" + classname + "' by loader: " + this + ", " + e);
throw new ClassNotFoundException(e.toString()); }
}
public URL getResource(String name) {
if (ESLoader.isDebugLoader()) System.out.println(" ** getResource: '" + name + "' asked to: " + this);
URL url = getSystemResource(name);
if (url != null) {
if (ESLoader.isDebugLoader()) System.out.println(" ** URL found in system as: " + url);
return url;
}
try {
url = new URL(resourceName, null, "/" + urlPrefix + myCookie + "/+/" + name);
if (ESLoader.isDebugLoader())System.out.println(" ** URL found as " + url);
} catch (MalformedURLException e) {
if (ESLoader.isDebugLoader()) System.out.println(" ** Bad URL " + url + " " + e);
}
return url;
}
public InputStream getResourceAsStream(String name) {
if (ESLoader.isDebugLoader()) System.out.println(" ** getResourceAsStream: '" + name + "' asked to: " + this);
InputStream back = getSystemResourceAsStream(name);
if (back != null) {
if (ESLoader.isDebugLoader()) System.out.println(" ** getResourceAsStream("+name+") is a system resource");
return back;
}
return getLocalResourceAsStream(name);
}
private byte [] getResourceBuffer(String name) {
byte buf[] = null;
if (ESLoader.isDebugLoader()) System.out.println(" ** getResourceBuffer, resource '" + name + "'");
if (asJar) {
ZipEntry zipEntry = zipFile.getEntry(name);
if (zipEntry == null) {
if (ESLoader.isDebugLoader()) System.out.println(" ** Resource '" + name + "'not found in jar by: " + this);
return null;
}
try {
InputStream inputStream = zipFile.getInputStream(zipEntry);
int limit = (int)zipEntry.getSize();
buf = new byte[limit];
int total = 0;
while (total < limit)
{
int ct = inputStream.read(buf,total,limit-total);
total = total + ct;
if (ct == 0) {
if (ESLoader.isDebugLoader()) {
System.out.println(" ** Error read entry '" + name + "' in jar, loader: " + this);
System.out.println("Only " +
total + " bytes out of " + limit + " read from entry '" +
name + "' in jar '" + zipFile.getName() +"'");
}
throw new IOException ("Only " +
total + " bytes out of " + limit + " read from entry '" +
name + "' in jar '" + zipFile.getName() +"'");
}
}
inputStream.close();
} catch (IOException e) {
if (ESLoader.isDebugLoader()) System.out.println(" ** Error reading jar: " + e);
return null;
}
} else {
try {
// Create a File object. Interpret the filename relative to the
// directory specified for this ClassLoader.
File f = new File(directoryFile, name);
// Read from file
// Get the length of the class file, allocate an array of bytes for
// it, and read it in all at once.
int length = (int) f.length();
buf = new byte[length];
DataInputStream in = new DataInputStream(new FileInputStream(f));
in.readFully(buf);
in.close();
} catch (IOException e) {
if (ESLoader.isDebugLoader()) System.out.println(" ** Error reading file: " + e);
return null;
}
}
return buf;
}
private Object getLocalResource(String name) {
if (ESLoader.isDebugLoader()) System.out.println(" ** getLocalResource, resource '" + name + "' asked to: " + this);
byte buf[] = getResourceBuffer(name);
if (buf==null) return null;
if (name.endsWith(".gif") || name.endsWith(".jpeg")) {
Image image = Toolkit.getDefaultToolkit().createImage(buf);
if (ESLoader.isDebugLoader()) System.out.println(" ** Returning image resource: " + image);
return image;
// return new ByteArrayImageSource(buf); // SUN specific method
} else {
ByteArrayInputStream s = new ByteArrayInputStream(buf);
if (ESLoader.isDebugLoader()) System.out.println(" ** Returning stream resource: " + s);
return s;
}
}
private InputStream getLocalResourceAsStream(String name) {
if (ESLoader.isDebugLoader()) System.out.println(" ** getLocalResourceAsStream, resource '" + name + "' asked to: " + this);
if (asJar) {
int limit;
try
{
ZipEntry zipEntry = zipFile.getEntry(name);
if (zipEntry != null) {
InputStream inputStream = zipFile.getInputStream(zipEntry);
if (ESLoader.isDebugLoader()) System.out.println(" ** Resource found, returned as stream: " + inputStream);
return inputStream;
}
} catch (Exception e) {
if (ESLoader.isDebugLoader()) System.out.println(" ** Exception when loading resource: " + name +
": " + e);
}
} else {
// Create a File object. Interpret the filename relative to the
// directory specified for this ClassLoader.
File f = new File(directoryFile, name);
// Get the stream of this file.
try {
InputStream inputStream = new FileInputStream(f);
if (ESLoader.isDebugLoader()) System.out.println(" ** Resource found, returned as stream: " + inputStream);
return inputStream;
} catch (IOException e) {
if (ESLoader.isDebugLoader()) System.out.println(" ** Exception when loading resource: " + name +
": " + e);
}
}
if (ESLoader.isDebugLoader()) System.out.println(" ** Resource not found: " + name);
return null;
}
public static InputStream getLocalResourceAsStream(String cookie,
String name) {
if (ESLoader.isDebugLoader()) System.out.println(" ** static getLocalResourceAsStream, cookie: " + cookie + ", resource: " + name);
LocalClassLoader cl = (LocalClassLoader) loadersByCookie.get(cookie);
if (cl == null) {
if (ESLoader.isDebugLoader()) System.err.println(" @@ LocalClassLoader cookie: " + cookie + " NOT FOUND !");
return null;
}
if (ESLoader.isDebugLoader()) System.out.println(" ** Classloader found: " + cl);
return cl.getLocalResourceAsStream(name);
}
public static Object getLocalResource(String cookie,
String name) {
if (ESLoader.isDebugLoader()) System.out.println(" ** static getLocalResource, cookie: " + cookie + ", resource: " + name);
LocalClassLoader cl = (LocalClassLoader) loadersByCookie.get(cookie);
if (cl == null) {
if (ESLoader.isDebugLoader()) System.out.println(" @@ LocalClassLoader cookie: " + cookie + " NOT FOUND !");
return null;
}
if (ESLoader.isDebugLoader()) System.out.println(" ** Classloader found: " + cl);
return cl.getLocalResource(name);
}
public String toString() {
return "LocalClassLoader["+myCookie+"]:" +
(asJar ? ("JAR='" + zipFile.getName()): ("DIR='" + directoryFile)) +"'";
}
static {
// Add this protocol type to the http properties
Properties newP = new Properties(System.getProperties());
newP.put(protocolPathProp,
newP.getProperty(protocolPathProp)+"|FESI.Interpreter");
System.setProperties(newP);
}
}

View file

@ -0,0 +1,74 @@
// ParsedProgram.java
// FESI Copyright (c) Jean-Marc Lugrin, 1999
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package FESI.Interpreter;
import FESI.Parser.*;
import FESI.AST.*;
import FESI.Data.*;
import java.util.Vector;
/**
* Represent a parsed program or function
*/
public class ParsedProgram {
// The parsed tree of the function
private ASTProgram programNode = null;
// The list of declared variables
private Vector variableNames = null;
// The source of the parsed program
private EvaluationSource evaluationSource = null;
/**
* Create a parsed program representation from the abstract tree and list of variables
* @param programNode the parsed program
* @param variableNames The variables declared by var
* @param evaluationSource the source of the parsed tree
*/
protected ParsedProgram(ASTProgram programNode,
Vector variableNames,
EvaluationSource evaluationSource) {
this.programNode = programNode;
this.variableNames = variableNames;
this.evaluationSource = evaluationSource;
}
/**
* Get the program node
* @return the program node
*/
protected ASTProgram getProgramNode() {
return programNode;
}
/**
* Get the variable list
* @return the variable list
*/
protected Vector getVariableNames() {
return variableNames;
}
/**
* Get the evaluation souce
* @return the evaluation source
*/
protected EvaluationSource getEvaluationSource() {
return evaluationSource;
}
}

View file

@ -0,0 +1,147 @@
// ScopeChain.java
// FESI Copyright (c) Jean-Marc Lugrin, 1999
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package FESI.Interpreter;
import FESI.Exceptions.*;
import FESI.Data.*;
/**
* The scope chaine has established by WITH statements. Used to
* lookup values by name.
*/
public class ScopeChain {
private ScopeChain previousElement;
private ESObject thisElement;
/**
* CReate a new scope chain linked to a previous one (which
* is null only for the topmost chain)
* @param thisElement Object to look at at this level
* @param previousElement previous object in scope chain
*/
ScopeChain(ESObject thisElement, ScopeChain previousElement) {
this.previousElement = previousElement;
this.thisElement = thisElement;
}
/**
* Return the previous element in scope chain
* @return The previous element
*/
ScopeChain previousScope() {
return previousElement;
}
/**
* Return a reference to an object in the scope chain, so that the value
* can be accessed or modified.
*
* @param identifier The name of the object
* @return an ESReference object
* @exception EcmaScriptException Not thrown
*/
public ESReference getReference(String identifier) throws EcmaScriptException {
return getReference(identifier, identifier.hashCode());
}
/**
* Return a reference to an object in the scope chain, so that the value
* can be accessed or modified.
*
* @param identifier The name of the object
* @param hash The hashCode of this identifier
* @return an ESReference object
* @exception EcmaScriptException Not thrown
*/
public ESReference getReference(String identifier, int hash) throws EcmaScriptException {
ScopeChain theChain = this;
do {
if (theChain.thisElement.hasProperty(identifier, hash)) {
return new ESReference(theChain.thisElement, identifier, hash);
}
theChain = theChain.previousElement;
} while (theChain != null);
return new ESReference(null, identifier, hash);
}
/**
* Return a value for an object in the scope chain.
* return ESUndefined for undefined properties of existing objects,
* but generate an error for unexistent object.
* <P>A variant of getProperty is used, which will call recursively
* this routine and the getProperty in a previous scope until all
* scopes have been examined or a value is returned. This avoid the
* call of hasProperty followed by a second call to getProperty to
* get the property value.
*
* @param identifier The name of the object
* @return The value of the object, possibly ESUndefined
* @exception EcmaScriptException if no global object has that value
*/
public ESValue getValue(String identifier) throws EcmaScriptException {
return getValue(identifier, identifier.hashCode());
}
/**
* Return a value for an object in the scope chain.
* return ESUndefined for undefined properties of existing objects,
* but generate an error for unexistent object.
* <P>A variant of getProperty is used, which will call recursively
* this routine and the getProperty in a previous scope until all
* scopes have been examined or a value is returned. This avoid the
* call of hasProperty followed by a second call to getProperty to
* get the property value.
*
* @param identifier The name of the object
* @param hash The hash code of the element (for optimization)
* @return The value of the object, possibly ESUndefined
* @exception EcmaScriptException if no global object has that value
*/
public ESValue getValue(String identifier, int hash) throws EcmaScriptException {
return thisElement.getPropertyInScope(identifier, previousElement, hash);
}
/**
* Call a function defined by name in the scope chain.
* <P>A variant of doCall is used, which will call recursively
* this routine and the doCall in a previous scope until all
* scopes have been examined or a value is returned. This avoid the
* call of hasProperty followed by a second call to getProperty to
* get the property value.
*
* @param evaluator The evaluator doing the call
* @param thisObject The 'this' object of the call
* @param functionName The name of the function to call
* @param hash The hash code of the function name (for optimization)
* @param arguments The parameters of the call
* @return The result of the call, possibly NULL
* @exception EcmaScriptException for any error
*/
public ESValue doIndirectCall(Evaluator evaluator,
ESObject thisObject,
String functionName,
int hash,
ESValue[] arguments) throws EcmaScriptException {
return thisElement.doIndirectCallInScope(evaluator, previousElement, thisObject, functionName, hash, arguments);
}
}

View file

@ -0,0 +1,66 @@
// StringEvaluationSource.java
// FESI Copyright (c) Jean-Marc Lugrin, 1999
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package FESI.Interpreter;
import java.util.StringTokenizer;
/**
* Describe a string used as a source. The string is trimmed
* to its first significant characters during display.
*/
public class StringEvaluationSource extends EvaluationSource {
private String theString;
/**
* Create a string source description
* @param theString Describe the source
* @param previousSource Describe the calling source
*/
public StringEvaluationSource(String theString, EvaluationSource previousSource) {
super(previousSource);
this.theString = theString;
}
protected String getEvaluationSourceText() {
String displayString = new String("");
// All this to print just first line of string...
boolean firstLineFound = false;
boolean moreLinesFound = false;
StringTokenizer t = new StringTokenizer(theString, "\n\r");
while (t.hasMoreTokens()) {
String theLine = t.nextToken();
if (theLine.equals("\n") || theLine.equals("\r")) continue;
if (theLine.trim().length()>0) { // Skip any leading empty lines
if (!firstLineFound) {
displayString = theLine;
firstLineFound = true;
continue;
}
moreLinesFound = true;
break;
}
}
if (moreLinesFound) {
return "in string starting with: '" + displayString + "'...";
} else {
return "in string: '" + displayString + "'";
}
}
}

View file

@ -0,0 +1,40 @@
// UserEvaluationSource.java
// FESI Copyright (c) Jean-Marc Lugrin, 1999
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package FESI.Interpreter;
/**
* Describe an arbitrary source provided by a user program.
*/
public class UserEvaluationSource extends EvaluationSource {
private String theDescription;
/**
* Create a user described source
* @param theDescription Describe the source
* @param previousSource Describe the calling source
*/
public UserEvaluationSource(String theDescription, EvaluationSource previousSource) {
super(previousSource);
this.theDescription = theDescription;
}
protected String getEvaluationSourceText() {
return "in: '" + theDescription + "'";
}
}