NameDescriptor nd;
public NameDescriptor(NameDescriptor nd, String id) {
super(nd.toString()+"."+id);
- identifier=id;
+ identifier=getPathFromRootToHere(id);
this.nd=nd;
}
public NameDescriptor(String id) {
super(id);
- identifier=id;
+ identifier=getPathFromRootToHere(id);
nd=null;
}
else
return nd.getRoot();
}
-
+
+ public String getPathFromRootToHere(String id) {
+ String path = id;
+ NameDescriptor temp = this.nd;
+ while(temp!=null) {
+ path = temp.identifier + "___________" + path;
+ temp = temp.nd;
+ }
+
+ return path;
+ }
+
public String toString() {
if (nd==null)
return identifier;
TypeDescriptor td=new TypeDescriptor(n);
return td;
}
+
+ public static TypeDescriptor getTypeDescriptor(String n) {
+ TypeDescriptor td=new TypeDescriptor(n);
+ return td;
+ }
public void addClass(ClassDescriptor tdn) {
if (classes.contains(tdn.getSymbol()))
}
}
- Vector singleimports;
+ Hashtable singleimports;
Vector multiimports;
NameDescriptor packages;
/** Parse the classes in this file */
public void parseFile(ParseNode pn, Set toanalyze,String sourcefile) {
- singleimports=new Vector();
+ singleimports= new Hashtable();
multiimports=new Vector();
-
ParseNode ipn=pn.getChild("imports").getChild("import_decls_list");
if (ipn!=null) {
ParseNodeVector pnv=ipn.getChildren();
NameDescriptor nd=parseName(pnimport.getChild("name"));
// TODO need to implement
if (isNode(pnimport,"import_single"))
- singleimports.add(nd);
+ if(!singleimports.containsKey(nd.getIdentifier())) {
+ //map name to full name (includes package/directory
+ singleimports.put(nd.getIdentifier(), nd.getPathFromRootToHere(nd.getIdentifier()));
+ } else {
+ throw new Error("Error: class " + nd.getIdentifier() + " is already more than once in a single type import inside "+sourcefile);
+ }
else
+ //TODO MULTI-IMPORTS!
multiimports.add(nd);
}
}
+
+
+
ParseNode ppn=pn.getChild("packages").getChild("package");
+ String packageName = null;
if (ppn!=null) {
- // TODO need to implement
- packages=parseName(ppn.getChild("name"));
+ packageName = ppn.getChild("name").getChild("identifier").getTerminal();
}
ParseNode tpn=pn.getChild("type_declaration_list");
- if (tpn!=null) {
- ParseNodeVector pnv=tpn.getChildren();
- for(int i=0; i<pnv.size(); i++) {
- ParseNode type_pn=pnv.elementAt(i);
- if (isEmpty(type_pn)) /* Skip the semicolon */
- continue;
- if (isNode(type_pn,"class_declaration")) {
- ClassDescriptor cn=parseTypeDecl(type_pn);
- cn.setSourceFileName(sourcefile);
- parseInitializers(cn);
- if (toanalyze!=null)
- toanalyze.add(cn);
- state.addClass(cn);
- // for inner classes/enum
- HashSet tovisit = new HashSet();
- Iterator it_icds = cn.getInnerClasses();
- while(it_icds.hasNext()) {
- tovisit.add(it_icds.next());
- }
-
- while(!tovisit.isEmpty()) {
- ClassDescriptor cd = (ClassDescriptor)tovisit.iterator().next();
- tovisit.remove(cd);
- parseInitializers(cd);
- if(toanalyze != null) {
- toanalyze.add(cd);
- }
- cd.setSourceFileName(sourcefile);
- state.addClass(cd);
-
- Iterator it_ics = cd.getInnerClasses();
- while(it_ics.hasNext()) {
- tovisit.add(it_ics.next());
- }
-
- Iterator it_ienums = cd.getEnum();
- while(it_ienums.hasNext()) {
- ClassDescriptor iecd = (ClassDescriptor)it_ienums.next();
- if(toanalyze != null) {
- toanalyze.add(iecd);
- }
- iecd.setSourceFileName(sourcefile);
- state.addClass(iecd);
- }
- }
-
- Iterator it_enums = cn.getEnum();
- while(it_enums.hasNext()) {
- ClassDescriptor ecd = (ClassDescriptor)it_enums.next();
- if(toanalyze != null) {
- toanalyze.add(ecd);
- }
- ecd.setSourceFileName(sourcefile);
- state.addClass(ecd);
- }
- } else if (isNode(type_pn,"task_declaration")) {
- TaskDescriptor td=parseTaskDecl(type_pn);
- if (toanalyze!=null)
- toanalyze.add(td);
- state.addTask(td);
- } else if (isNode(type_pn,"interface_declaration")) {
- // TODO add version for normal Java later
- ClassDescriptor cn = parseInterfaceDecl(type_pn);
- if (toanalyze!=null)
- toanalyze.add(cn);
- cn.setSourceFileName(sourcefile);
- state.addClass(cn);
-
- // for enum
- Iterator it_enums = cn.getEnum();
- while(it_enums.hasNext()) {
- ClassDescriptor ecd = (ClassDescriptor)it_enums.next();
- if(toanalyze != null) {
- toanalyze.add(ecd);
- }
- ecd.setSourceFileName(sourcefile);
- state.addClass(ecd);
- }
- } else if (isNode(type_pn,"enum_declaration")) {
- // TODO add version for normal Java later
- ClassDescriptor cn = parseEnumDecl(null, type_pn);
- if (toanalyze!=null)
- toanalyze.add(cn);
- cn.setSourceFileName(sourcefile);
- state.addClass(cn);
- } else {
- throw new Error(type_pn.getLabel());
- }
+ if (tpn != null) {
+ ParseNodeVector pnv = tpn.getChildren();
+ for (int i = 0; i < pnv.size(); i++) {
+ ParseNode type_pn = pnv.elementAt(i);
+ if (isEmpty(type_pn)) /* Skip the semicolon */
+ continue;
+ if (isNode(type_pn, "class_declaration")) {
+ ClassDescriptor cn = parseTypeDecl(type_pn, packageName);
+ cn.setImports(singleimports, multiimports);
+ cn.setSourceFileName(sourcefile);
+ parseInitializers(cn);
+ if (toanalyze != null)
+ toanalyze.add(cn);
+ state.addClass(cn);
+ // for inner classes/enum
+ HashSet tovisit = new HashSet();
+ Iterator it_icds = cn.getInnerClasses();
+ while (it_icds.hasNext()) {
+ tovisit.add(it_icds.next());
+ }
+
+ while (!tovisit.isEmpty()) {
+ ClassDescriptor cd = (ClassDescriptor) tovisit.iterator().next();
+ tovisit.remove(cd);
+ parseInitializers(cd);
+ if (toanalyze != null) {
+ toanalyze.add(cd);
+ }
+ cd.setSourceFileName(sourcefile);
+ state.addClass(cd);
+
+ Iterator it_ics = cd.getInnerClasses();
+ while (it_ics.hasNext()) {
+ tovisit.add(it_ics.next());
+ }
+
+ Iterator it_ienums = cd.getEnum();
+ while (it_ienums.hasNext()) {
+ ClassDescriptor iecd = (ClassDescriptor) it_ienums.next();
+ if (toanalyze != null) {
+ toanalyze.add(iecd);
+ }
+ iecd.setSourceFileName(sourcefile);
+ state.addClass(iecd);
+ }
+ }
+
+ Iterator it_enums = cn.getEnum();
+ while (it_enums.hasNext()) {
+ ClassDescriptor ecd = (ClassDescriptor) it_enums.next();
+ if (toanalyze != null) {
+ toanalyze.add(ecd);
+ }
+ ecd.setSourceFileName(sourcefile);
+ state.addClass(ecd);
+ }
+ } else if (isNode(type_pn, "task_declaration")) {
+ TaskDescriptor td = parseTaskDecl(type_pn);
+ if (toanalyze != null)
+ toanalyze.add(td);
+ state.addTask(td);
+ } else if (isNode(type_pn, "interface_declaration")) {
+ // TODO add version for normal Java later
+ ClassDescriptor cn = parseInterfaceDecl(type_pn);
+ if (toanalyze != null)
+ toanalyze.add(cn);
+ cn.setSourceFileName(sourcefile);
+ state.addClass(cn);
+
+ // for enum
+ Iterator it_enums = cn.getEnum();
+ while (it_enums.hasNext()) {
+ ClassDescriptor ecd = (ClassDescriptor) it_enums.next();
+ if (toanalyze != null) {
+ toanalyze.add(ecd);
+ }
+ ecd.setSourceFileName(sourcefile);
+ state.addClass(ecd);
+ }
+ } else if (isNode(type_pn, "enum_declaration")) {
+ // TODO add version for normal Java later
+ ClassDescriptor cn = parseEnumDecl(null, type_pn);
+ if (toanalyze != null)
+ toanalyze.add(cn);
+ cn.setSourceFileName(sourcefile);
+ state.addClass(cn);
+ } else {
+ throw new Error(type_pn.getLabel());
+ }
}
}
}
return tel;
}
- public ClassDescriptor parseTypeDecl(ParseNode pn) {
- ClassDescriptor cn=new ClassDescriptor(pn.getChild("name").getTerminal(), false);
+ public ClassDescriptor parseTypeDecl(ParseNode pn, String packageName) {
+ ClassDescriptor cn;
+ // if is in no package, then create a class descriptor with just the name.
+ // else add the package on and replace "." with "___________"
+ if(packageName == null) {
+ cn=new ClassDescriptor(pn.getChild("name").getTerminal(), false);
+ } else {
+ String newClassname = packageName + "___________" + pn.getChild("name").getTerminal();
+ cn= new ClassDescriptor(packageName, newClassname.replaceAll("\\.", "___________") , false);
+ }
if (!isEmpty(pn.getChild("super").getTerminal())) {
/* parse superclass name */
ParseNode snn=pn.getChild("super").getChild("type").getChild("class").getChild("name");
private NameDescriptor parseName(ParseNode nn) {
ParseNode base=nn.getChild("base");
ParseNode id=nn.getChild("identifier");
- if (base==null)
+ //TODO check for bugs with naming things like classes...
+ String terminal = (String) (singleimports.containsKey(id.getTerminal())?singleimports.get(id.getTerminal()):id.getTerminal());
+ if (base==null) {
+ return new NameDescriptor(terminal);
+ }
+ return new NameDescriptor(parseName(base.getChild("name")),terminal);
+ }
+
+ //only function difference between this and parseName() is that this
+ //does not look for a import mapping.
+ private NameDescriptor parseNameRaw(ParseNode nn) {
+ ParseNode base=nn.getChild("base");
+ ParseNode id=nn.getChild("identifier");
+ if (base==null) {
return new NameDescriptor(id.getTerminal());
+ }
return new NameDescriptor(parseName(base.getChild("name")),id.getTerminal());
-
}
private void parseFlagDecl(ClassDescriptor cn,ParseNode pn) {
LiteralNode ln=new LiteralNode(literaltype, literal_obj);
ln.setNumLine(pn.getLine());
return ln;
- } else if (isNode(pn,"createobject")) {
- TypeDescriptor td=parseTypeDescriptor(pn);
-
- Vector args=parseArgumentList(pn);
- boolean isglobal=pn.getChild("global")!=null||
- pn.getChild("scratch")!=null;
- String disjointId=null;
- if( pn.getChild("disjoint") != null) {
- disjointId = pn.getChild("disjoint").getTerminal();
+ } else if (isNode(pn, "createobject")) {
+ TypeDescriptor td = parseTypeDescriptor(pn);
+
+ Vector args = parseArgumentList(pn);
+ boolean isglobal = pn.getChild("global") != null || pn.getChild("scratch") != null;
+ String disjointId = null;
+ if (pn.getChild("disjoint") != null) {
+ disjointId = pn.getChild("disjoint").getTerminal();
}
- CreateObjectNode con=new CreateObjectNode(td, isglobal, disjointId);
+ CreateObjectNode con = new CreateObjectNode(td, isglobal, disjointId);
con.setNumLine(pn.getLine());
- for(int i=0; i<args.size(); i++) {
- con.addArgument((ExpressionNode)args.get(i));
+ for (int i = 0; i < args.size(); i++) {
+ con.addArgument((ExpressionNode) args.get(i));
}
/* Could have flag set or tag added here */
- if (pn.getChild("flag_list")!=null||pn.getChild("tag_list")!=null) {
- FlagEffects fe=new FlagEffects(null);
- if (pn.getChild("flag_list")!=null)
- parseFlagEffect(fe, pn.getChild("flag_list"));
-
- if (pn.getChild("tag_list")!=null)
- parseTagEffect(fe, pn.getChild("tag_list"));
- con.addFlagEffects(fe);
+ if (pn.getChild("flag_list") != null || pn.getChild("tag_list") != null) {
+ FlagEffects fe = new FlagEffects(null);
+ if (pn.getChild("flag_list") != null)
+ parseFlagEffect(fe, pn.getChild("flag_list"));
+
+ if (pn.getChild("tag_list") != null)
+ parseTagEffect(fe, pn.getChild("tag_list"));
+ con.addFlagEffects(fe);
}
return con;
Stack loopstack;
HashSet toanalyze;
HashSet completed;
+
+ //This is the class mappings for a particular file based
+ //on the import names. Maps class to canonical class name.
+ Hashtable classnameMappings;
public SemanticCheck(State state, TypeUtil tu) {
}
public void semanticCheck() {
- SymbolTable classtable=state.getClassSymbolTable();
+ SymbolTable classtable = state.getClassSymbolTable();
toanalyze.addAll(classtable.getValueSet());
toanalyze.addAll(state.getTaskSymbolTable().getValueSet());
// Do methods next
- while(!toanalyze.isEmpty()) {
- Object obj=toanalyze.iterator().next();
+ while (!toanalyze.isEmpty()) {
+ Object obj = toanalyze.iterator().next();
if (obj instanceof TaskDescriptor) {
- toanalyze.remove(obj);
- TaskDescriptor td=(TaskDescriptor)obj;
- try {
- checkTask(td);
- } catch( Error e ) {
- System.out.println( "Error in "+td );
- throw e;
- }
+ toanalyze.remove(obj);
+ TaskDescriptor td = (TaskDescriptor) obj;
+ try {
+ checkTask(td);
+ } catch (Error e) {
+ System.out.println("Error in " + td);
+ throw e;
+ }
} else {
- ClassDescriptor cd=(ClassDescriptor)obj;
- toanalyze.remove(cd);
- //need to initialize typeutil object here...only place we can
- //get class descriptors without first calling getclass
- getClass(cd.getSymbol());
- for(Iterator method_it=cd.getMethods(); method_it.hasNext();) {
- MethodDescriptor md=(MethodDescriptor)method_it.next();
- try {
- checkMethodBody(cd,md);
- } catch( Error e ) {
- System.out.println( "Error in "+md );
- throw e;
- }
- }
+ ClassDescriptor cd = (ClassDescriptor) obj;
+ toanalyze.remove(cd);
+ //set the class mappings based on imports.
+ classnameMappings = cd.getSingleImportMappings();
+
+ // need to initialize typeutil object here...only place we can
+ // get class descriptors without first calling getclass
+ getClass(cd.getSymbol());
+ for (Iterator method_it = cd.getMethods(); method_it.hasNext();) {
+ MethodDescriptor md = (MethodDescriptor) method_it.next();
+ try {
+ checkMethodBody(cd, md);
+ } catch (Error e) {
+ System.out.println("Error in " + md);
+ throw e;
+ }
+ }
}
}
}
if(index != -1) {
name = name.substring(index+1);
}
- ClassDescriptor field_cd=getClass(name);
+ ClassDescriptor field_cd=getClass(getFullName(name));
if (field_cd==null)
throw new Error("Undefined class "+name);
td.setClassDescriptor(field_cd);
/* Get type descriptor */
if (cn.getType()==null) {
NameDescriptor typenamed=cn.getTypeName().getName();
- String typename=typenamed.toString();
+ String typename=getFullName(typenamed.toString());
TypeDescriptor ntd=new TypeDescriptor(getClass(typename));
cn.setType(ntd);
}
if(varname.equals("this")) {
throw new Error("Error: access this obj in a static block");
}
- cd=getClass(varname);
+ cd=getClass(getFullName(varname));
if(cd != null) {
// this is a class name
nn.setClassDesc(cd);
}
- void checkCreateObjectNode(Descriptor md, SymbolTable nametable, CreateObjectNode con, TypeDescriptor td) {
- TypeDescriptor[] tdarray=new TypeDescriptor[con.numArgs()];
- for(int i=0; i<con.numArgs(); i++) {
- ExpressionNode en=con.getArg(i);
- checkExpressionNode(md,nametable,en,null);
- tdarray[i]=en.getType();
+ void checkCreateObjectNode(Descriptor md, SymbolTable nametable, CreateObjectNode con,
+ TypeDescriptor td) {
+ TypeDescriptor[] tdarray = new TypeDescriptor[con.numArgs()];
+ for (int i = 0; i < con.numArgs(); i++) {
+ ExpressionNode en = con.getArg(i);
+ checkExpressionNode(md, nametable, en, null);
+ tdarray[i] = en.getType();
}
- TypeDescriptor typetolookin=con.getType();
+ TypeDescriptor typetolookin = con.getType();
checkTypeDescriptor(typetolookin);
- if (td!=null&&!typeutil.isSuperorType(td, typetolookin))
- throw new Error(typetolookin + " isn't a "+td);
-
+ if (td != null && !typeutil.isSuperorType(td, typetolookin))
+ throw new Error(typetolookin + " isn't a " + td);
+
/* Check Array Initializers */
- if((con.getArrayInitializer() != null)) {
+ if ((con.getArrayInitializer() != null)) {
checkArrayInitializerNode(md, nametable, con.getArrayInitializer(), td);
}
/* Check flag effects */
- if (con.getFlagEffects()!=null) {
- FlagEffects fe=con.getFlagEffects();
- ClassDescriptor cd=typetolookin.getClassDesc();
-
- for(int j=0; j<fe.numEffects(); j++) {
- FlagEffect flag=fe.getEffect(j);
- String name=flag.getName();
- FlagDescriptor flag_d=(FlagDescriptor)cd.getFlagTable().get(name);
- //Make sure the flag is declared
- if (flag_d==null)
- throw new Error("Flag descriptor "+name+" undefined in class: "+cd.getSymbol());
- if (flag_d.getExternal())
- throw new Error("Attempting to modify external flag: "+name);
- flag.setFlag(flag_d);
+ if (con.getFlagEffects() != null) {
+ FlagEffects fe = con.getFlagEffects();
+ ClassDescriptor cd = typetolookin.getClassDesc();
+
+ for (int j = 0; j < fe.numEffects(); j++) {
+ FlagEffect flag = fe.getEffect(j);
+ String name = flag.getName();
+ FlagDescriptor flag_d = (FlagDescriptor) cd.getFlagTable().get(name);
+ // Make sure the flag is declared
+ if (flag_d == null)
+ throw new Error("Flag descriptor " + name + " undefined in class: " + cd.getSymbol());
+ if (flag_d.getExternal())
+ throw new Error("Attempting to modify external flag: " + name);
+ flag.setFlag(flag_d);
}
- for(int j=0; j<fe.numTagEffects(); j++) {
- TagEffect tag=fe.getTagEffect(j);
- String name=tag.getName();
-
- Descriptor d=(Descriptor)nametable.get(name);
- if (d==null)
- throw new Error("Tag descriptor "+name+" undeclared");
- else if (!(d instanceof TagVarDescriptor))
- throw new Error(name+" is not a tag descriptor");
- tag.setTag((TagVarDescriptor)d);
+ for (int j = 0; j < fe.numTagEffects(); j++) {
+ TagEffect tag = fe.getTagEffect(j);
+ String name = tag.getName();
+
+ Descriptor d = (Descriptor) nametable.get(name);
+ if (d == null)
+ throw new Error("Tag descriptor " + name + " undeclared");
+ else if (!(d instanceof TagVarDescriptor))
+ throw new Error(name + " is not a tag descriptor");
+ tag.setTag((TagVarDescriptor) d);
}
}
- if ((!typetolookin.isClass())&&(!typetolookin.isArray()))
- throw new Error("Can't allocate primitive type:"+con.printNode(0));
+ if ((!typetolookin.isClass()) && (!typetolookin.isArray()))
+ throw new Error("Can't allocate primitive type:" + con.printNode(0));
if (!typetolookin.isArray()) {
- //Array's don't need constructor calls
- ClassDescriptor classtolookin=typetolookin.getClassDesc();
-
- Set methoddescriptorset=classtolookin.getMethodTable().getSet(typetolookin.getSymbol());
- MethodDescriptor bestmd=null;
-NextMethod:
- for(Iterator methodit=methoddescriptorset.iterator(); methodit.hasNext();) {
- MethodDescriptor currmd=(MethodDescriptor)methodit.next();
- /* Need correct number of parameters */
- if (con.numArgs()!=currmd.numParameters())
- continue;
- for(int i=0; i<con.numArgs(); i++) {
- if (!typeutil.isSuperorType(currmd.getParamType(i),tdarray[i]))
- continue NextMethod;
- }
- /* Local allocations can't call global allocator */
- if (!con.isGlobal()&&currmd.isGlobal())
- continue;
-
- /* Method okay so far */
- if (bestmd==null)
- bestmd=currmd;
- else {
- if (typeutil.isMoreSpecific(currmd,bestmd)) {
- bestmd=currmd;
- } else if (con.isGlobal()&&match(currmd, bestmd)) {
- if (currmd.isGlobal()&&!bestmd.isGlobal())
- bestmd=currmd;
- else if (currmd.isGlobal()&&bestmd.isGlobal())
- throw new Error();
- } else if (!typeutil.isMoreSpecific(bestmd, currmd)) {
- throw new Error("No method is most specific:"+bestmd+" and "+currmd);
- }
+ // Array's don't need constructor calls
+ ClassDescriptor classtolookin = typetolookin.getClassDesc();
+
+ Set methoddescriptorset = classtolookin.getMethodTable().getSet(typetolookin.getSymbol());
+ MethodDescriptor bestmd = null;
+ NextMethod: for (Iterator methodit = methoddescriptorset.iterator(); methodit.hasNext();) {
+ MethodDescriptor currmd = (MethodDescriptor) methodit.next();
+ /* Need correct number of parameters */
+ if (con.numArgs() != currmd.numParameters())
+ continue;
+ for (int i = 0; i < con.numArgs(); i++) {
+ if (!typeutil.isSuperorType(currmd.getParamType(i), tdarray[i]))
+ continue NextMethod;
+ }
+ /* Local allocations can't call global allocator */
+ if (!con.isGlobal() && currmd.isGlobal())
+ continue;
+
+ /* Method okay so far */
+ if (bestmd == null)
+ bestmd = currmd;
+ else {
+ if (typeutil.isMoreSpecific(currmd, bestmd)) {
+ bestmd = currmd;
+ } else if (con.isGlobal() && match(currmd, bestmd)) {
+ if (currmd.isGlobal() && !bestmd.isGlobal())
+ bestmd = currmd;
+ else if (currmd.isGlobal() && bestmd.isGlobal())
+ throw new Error();
+ } else if (!typeutil.isMoreSpecific(bestmd, currmd)) {
+ throw new Error("No method is most specific:" + bestmd + " and " + currmd);
+ }
- /* Is this more specific than bestmd */
- }
+ /* Is this more specific than bestmd */
+ }
}
- if (bestmd==null)
- throw new Error("No method found for "+con.printNode(0)+" in "+md);
+ if (bestmd == null)
+ throw new Error("No method found for " + con.printNode(0) + " in " + md);
con.setConstructor(bestmd);
}
}
throw new Error("Type of rside not compatible with type of lside"+on.printNode(0));
}
}
+
+ public String getFullName(String classIn) {
+ return (String) ((this.classnameMappings.containsKey(classIn))?classnameMappings.get(classIn):classIn);
+ }
}
}
public void addNewClass(String cl, Set todo) {
- for(int i=0;i<state.classpath.size();i++) {
- String path=(String)state.classpath.get(i);
- File f=new File(path, cl+".java");
+ for (int i = 0; i < state.classpath.size(); i++) {
+ String path = (String) state.classpath.get(i);
+ //The name has ___________ to separate out packages
+ File f = new File(path, cl.replaceAll("___________", "/") + ".java");
if (f.exists()) {
- try {
- ParseNode pn=Main.readSourceFile(state, f.getCanonicalPath());
- bir.buildtree(pn, todo,f.getCanonicalPath());
- return;
- } catch (Exception e) {
- throw new Error(e);
- }
+ try {
+ ParseNode pn = Main.readSourceFile(state, f.getCanonicalPath());
+ bir.buildtree(pn, todo, f.getCanonicalPath());
+ return;
+ } catch (Exception e) {
+ throw new Error(e);
+ }
}
}
- throw new Error("Couldn't find class "+cl);
+ throw new Error("Couldn't find class " + cl);
}