2007-02-02 17:25:09 +00:00
|
|
|
/******************************************************************************
|
|
|
|
* Copyright (c) 2002 - 2006 IBM Corporation.
|
|
|
|
* All rights reserved. This program and the accompanying materials
|
|
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
|
|
* which accompanies this distribution, and is available at
|
|
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
*
|
|
|
|
* Contributors:
|
|
|
|
* IBM Corporation - initial API and implementation
|
|
|
|
*****************************************************************************/
|
|
|
|
package com.ibm.wala.cast.js.loader;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
2007-02-08 19:07:30 +00:00
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Set;
|
2007-02-02 17:25:09 +00:00
|
|
|
|
2007-02-08 19:07:30 +00:00
|
|
|
import com.ibm.wala.cast.js.translator.JavaScriptTranslatorFactory;
|
|
|
|
import com.ibm.wala.cast.js.types.JavaScriptTypes;
|
|
|
|
import com.ibm.wala.cast.loader.AstClass;
|
|
|
|
import com.ibm.wala.cast.loader.AstDynamicPropertyClass;
|
|
|
|
import com.ibm.wala.cast.loader.AstFunctionClass;
|
|
|
|
import com.ibm.wala.cast.loader.AstMethod;
|
2007-02-02 17:25:09 +00:00
|
|
|
import com.ibm.wala.cast.loader.AstMethod.DebuggingInformation;
|
|
|
|
import com.ibm.wala.cast.loader.AstMethod.LexicalInformation;
|
2007-02-08 19:07:30 +00:00
|
|
|
import com.ibm.wala.cast.tree.CAstQualifier;
|
|
|
|
import com.ibm.wala.cast.tree.CAstSourcePositionMap;
|
|
|
|
import com.ibm.wala.cast.types.AstMethodReference;
|
2007-02-02 17:25:09 +00:00
|
|
|
import com.ibm.wala.cfg.AbstractCFG;
|
2007-06-01 03:32:56 +00:00
|
|
|
import com.ibm.wala.classLoader.*;
|
|
|
|
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
2007-02-08 19:07:30 +00:00
|
|
|
import com.ibm.wala.ipa.cha.ClassHierarchyException;
|
2007-02-02 17:25:09 +00:00
|
|
|
import com.ibm.wala.ssa.SymbolTable;
|
2007-02-08 19:07:30 +00:00
|
|
|
import com.ibm.wala.types.ClassLoaderReference;
|
2007-02-09 15:34:25 +00:00
|
|
|
import com.ibm.wala.types.Selector;
|
2007-02-08 19:07:30 +00:00
|
|
|
import com.ibm.wala.types.TypeName;
|
|
|
|
import com.ibm.wala.types.TypeReference;
|
2007-02-02 17:25:09 +00:00
|
|
|
import com.ibm.wala.util.Atom;
|
2007-02-08 19:07:30 +00:00
|
|
|
import com.ibm.wala.util.debug.Assertions;
|
|
|
|
import com.ibm.wala.util.debug.Trace;
|
2007-02-02 17:25:09 +00:00
|
|
|
|
|
|
|
public class JavaScriptLoader implements IClassLoader {
|
2007-06-01 03:32:56 +00:00
|
|
|
|
|
|
|
public final static Language JS = new Language() {
|
|
|
|
|
|
|
|
public Atom getName() {
|
|
|
|
return Atom.findOrCreateUnicodeAtom("JavaScript");
|
|
|
|
}
|
|
|
|
|
|
|
|
public TypeReference getRootType() {
|
|
|
|
return JavaScriptTypes.Root;
|
|
|
|
}
|
|
|
|
|
|
|
|
public TypeReference getConstantType(Object o) {
|
|
|
|
if (o == null) {
|
|
|
|
return JavaScriptTypes.Null;
|
|
|
|
} else {
|
|
|
|
Class c = o.getClass();
|
|
|
|
if (c == Boolean.class) {
|
|
|
|
return JavaScriptTypes.Boolean;
|
|
|
|
} else if (c == String.class) {
|
|
|
|
return JavaScriptTypes.String;
|
|
|
|
} else if (c == Integer.class) {
|
|
|
|
return JavaScriptTypes.Number;
|
|
|
|
} else if (c == Float.class) {
|
|
|
|
return JavaScriptTypes.Number;
|
|
|
|
} else if (c == Double.class) {
|
|
|
|
return JavaScriptTypes.Number;
|
|
|
|
} else {
|
2007-06-28 14:32:15 +00:00
|
|
|
assert false : "cannot determine type for " + o + " of class " + c;
|
2007-06-01 03:32:56 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-06-28 14:32:15 +00:00
|
|
|
|
|
|
|
public boolean isNullType(TypeReference type) {
|
|
|
|
return
|
|
|
|
type.equals(JavaScriptTypes.Undefined)
|
|
|
|
||
|
|
|
|
type.equals(JavaScriptTypes.Null);
|
|
|
|
}
|
2007-06-01 03:32:56 +00:00
|
|
|
};
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
private final Map<TypeName,IClass> types = new HashMap<TypeName,IClass>();
|
|
|
|
|
|
|
|
private static final Map<Selector,IMethod> emptyMap1 = Collections.emptyMap();
|
|
|
|
private static final Map<Atom,IField> emptyMap2 = Collections.emptyMap();
|
|
|
|
|
2007-02-02 17:25:09 +00:00
|
|
|
private final JavaScriptTranslatorFactory translatorFactory;
|
2007-02-09 15:34:25 +00:00
|
|
|
|
2007-06-01 03:32:56 +00:00
|
|
|
private final IClassHierarchy cha;
|
2007-02-02 17:25:09 +00:00
|
|
|
|
2007-06-01 03:32:56 +00:00
|
|
|
public JavaScriptLoader(IClassHierarchy cha, JavaScriptTranslatorFactory translatorFactory) {
|
2007-02-02 17:25:09 +00:00
|
|
|
this.cha = cha;
|
|
|
|
this.translatorFactory = translatorFactory;
|
|
|
|
}
|
|
|
|
|
|
|
|
class JavaScriptClass extends AstClass {
|
|
|
|
private IClass superClass;
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
private JavaScriptClass(IClassLoader loader, TypeReference classRef, TypeReference superRef,
|
|
|
|
CAstSourcePositionMap.Position sourcePosition) {
|
|
|
|
super(sourcePosition, classRef.getName(), loader, (short) 0, emptyMap2, emptyMap1);
|
2007-02-02 17:25:09 +00:00
|
|
|
types.put(classRef.getName(), this);
|
2007-07-06 03:07:29 +00:00
|
|
|
superClass = superRef == null ? null : loader.lookupClass(superRef.getName());
|
2007-02-02 17:25:09 +00:00
|
|
|
}
|
|
|
|
|
2007-06-01 03:32:56 +00:00
|
|
|
public IClassHierarchy getClassHierarchy() {
|
2007-02-02 17:25:09 +00:00
|
|
|
return cha;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String toString() {
|
|
|
|
return "JS:" + getReference().toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2007-02-09 15:34:25 +00:00
|
|
|
public Collection<IClass> getDirectInterfaces() throws ClassHierarchyException {
|
|
|
|
return Collections.emptySet();
|
2007-02-02 17:25:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2007-02-09 15:34:25 +00:00
|
|
|
public IClass getSuperclass() throws ClassHierarchyException {
|
2007-02-02 17:25:09 +00:00
|
|
|
return superClass;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class JavaScriptRootClass extends AstDynamicPropertyClass {
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
private JavaScriptRootClass(IClassLoader loader, CAstSourcePositionMap.Position sourcePosition) {
|
|
|
|
super(sourcePosition, JavaScriptTypes.Root.getName(), loader, (short) 0, emptyMap1, JavaScriptTypes.Root);
|
2007-02-02 17:25:09 +00:00
|
|
|
|
|
|
|
types.put(JavaScriptTypes.Root.getName(), this);
|
|
|
|
}
|
|
|
|
|
2007-06-01 03:32:56 +00:00
|
|
|
public IClassHierarchy getClassHierarchy() {
|
2007-02-02 17:25:09 +00:00
|
|
|
return cha;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String toString() {
|
|
|
|
return "JS Root:" + getReference().toString();
|
|
|
|
}
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
public Collection<IClass> getDirectInterfaces() throws ClassHierarchyException {
|
|
|
|
return Collections.emptySet();
|
2007-02-02 17:25:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public IClass getSuperclass() throws ClassHierarchyException {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class JavaScriptCodeBody extends AstFunctionClass {
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
public JavaScriptCodeBody(TypeReference codeName, TypeReference parent, IClassLoader loader,
|
|
|
|
CAstSourcePositionMap.Position sourcePosition) {
|
2007-02-02 17:25:09 +00:00
|
|
|
super(codeName, parent, loader, sourcePosition);
|
|
|
|
types.put(codeName.getName(), this);
|
2007-02-09 15:34:25 +00:00
|
|
|
}
|
2007-02-02 17:25:09 +00:00
|
|
|
|
2007-06-01 03:32:56 +00:00
|
|
|
public IClassHierarchy getClassHierarchy() {
|
2007-02-02 17:25:09 +00:00
|
|
|
return cha;
|
|
|
|
}
|
|
|
|
|
|
|
|
private IMethod setCodeBody(IMethod codeBody) {
|
|
|
|
this.functionBody = codeBody;
|
|
|
|
return codeBody;
|
|
|
|
}
|
|
|
|
}
|
2007-02-09 15:34:25 +00:00
|
|
|
|
|
|
|
private final Set<CAstQualifier> functionQualifiers;
|
|
|
|
|
|
|
|
{
|
|
|
|
functionQualifiers = new HashSet<CAstQualifier>();
|
2007-02-02 17:25:09 +00:00
|
|
|
functionQualifiers.add(CAstQualifier.PUBLIC);
|
|
|
|
functionQualifiers.add(CAstQualifier.FINAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
public class JavaScriptMethodObject extends AstMethod {
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
JavaScriptMethodObject(JavaScriptCodeBody cls, AbstractCFG cfg, SymbolTable symtab, boolean hasCatchBlock,
|
|
|
|
TypeReference[][] caughtTypes, LexicalInformation lexicalInfo, DebuggingInformation debugInfo) {
|
|
|
|
super(cls, functionQualifiers, cfg, symtab, AstMethodReference.fnReference(cls.getReference()), hasCatchBlock, caughtTypes,
|
|
|
|
lexicalInfo, debugInfo);
|
2007-02-02 17:25:09 +00:00
|
|
|
}
|
|
|
|
|
2007-06-01 03:32:56 +00:00
|
|
|
public IClassHierarchy getClassHierarchy() {
|
2007-02-02 17:25:09 +00:00
|
|
|
return cha;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String toString() {
|
|
|
|
return "<Code body of " + cls + ">";
|
|
|
|
}
|
|
|
|
|
|
|
|
public TypeReference[] getDeclaredExceptions() {
|
|
|
|
return null;
|
|
|
|
}
|
2007-02-09 15:34:25 +00:00
|
|
|
|
2007-02-02 17:25:09 +00:00
|
|
|
public LexicalParent[] getParents() {
|
2007-02-09 15:34:25 +00:00
|
|
|
if (lexicalInfo == null)
|
|
|
|
return new LexicalParent[0];
|
|
|
|
|
2007-02-02 17:25:09 +00:00
|
|
|
final String[] parents = lexicalInfo.getScopingParents();
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
if (parents == null)
|
|
|
|
return new LexicalParent[0];
|
|
|
|
|
|
|
|
LexicalParent result[] = new LexicalParent[parents.length];
|
2007-02-02 17:25:09 +00:00
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
for (int i = 0; i < parents.length; i++) {
|
|
|
|
final int hack = i;
|
|
|
|
final AstMethod method = (AstMethod) lookupClass(parents[i], cha).getMethod(AstMethodReference.fnSelector);
|
|
|
|
result[i] = new LexicalParent() {
|
|
|
|
public String getName() {
|
|
|
|
return parents[hack];
|
|
|
|
}
|
2007-02-02 17:25:09 +00:00
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
public AstMethod getMethod() {
|
|
|
|
return method;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Trace.println("parent " + result[i].getName() + " is " + result[i].getMethod());
|
2007-02-02 17:25:09 +00:00
|
|
|
}
|
2007-02-09 15:34:25 +00:00
|
|
|
|
2007-02-02 17:25:09 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getLocalVariableName(int bcIndex, int localNumber) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean hasLocalVariableTable() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getMaxLocals() {
|
|
|
|
Assertions.UNREACHABLE();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getMaxStackHeight() {
|
|
|
|
Assertions.UNREACHABLE();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public TypeReference getParameterType(int i) {
|
|
|
|
return JavaScriptTypes.Root;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
public IClass defineCodeBodyType(String name, TypeReference P, CAstSourcePositionMap.Position sourcePosition) {
|
|
|
|
return new JavaScriptCodeBody(TypeReference.findOrCreate(JavaScriptTypes.jsLoader, TypeName.string2TypeName(name)), P, this,
|
|
|
|
sourcePosition);
|
2007-02-02 17:25:09 +00:00
|
|
|
}
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
public IClass defineFunctionType(String name, CAstSourcePositionMap.Position pos) {
|
2007-02-02 17:25:09 +00:00
|
|
|
return defineCodeBodyType(name, JavaScriptTypes.Function, pos);
|
|
|
|
}
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
public IClass defineScriptType(String name, CAstSourcePositionMap.Position pos) {
|
2007-02-02 17:25:09 +00:00
|
|
|
return defineCodeBodyType(name, JavaScriptTypes.Script, pos);
|
|
|
|
}
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
public IMethod defineCodeBodyCode(String clsName, AbstractCFG cfg, SymbolTable symtab, boolean hasCatchBlock,
|
|
|
|
TypeReference[][] caughtTypes, LexicalInformation lexicalInfo, DebuggingInformation debugInfo) {
|
2007-02-02 17:25:09 +00:00
|
|
|
JavaScriptCodeBody C = (JavaScriptCodeBody) lookupClass(clsName, cha);
|
|
|
|
Assertions._assert(C != null, clsName);
|
|
|
|
return C.setCodeBody(new JavaScriptMethodObject(C, cfg, symtab, hasCatchBlock, caughtTypes, lexicalInfo, debugInfo));
|
|
|
|
}
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
final JavaScriptRootClass ROOT = new JavaScriptRootClass(this, null);
|
|
|
|
|
|
|
|
final JavaScriptClass UNDEFINED = new JavaScriptClass(this, JavaScriptTypes.Undefined, JavaScriptTypes.Root, null);
|
|
|
|
|
|
|
|
final JavaScriptClass PRIMITIVES = new JavaScriptClass(this, JavaScriptTypes.Primitives, JavaScriptTypes.Root, null);
|
|
|
|
|
2007-06-01 03:32:56 +00:00
|
|
|
final JavaScriptClass FAKEROOT = new JavaScriptClass(this, JavaScriptTypes.FakeRoot, JavaScriptTypes.Root, null);
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
final JavaScriptClass STRING = new JavaScriptClass(this, JavaScriptTypes.String, JavaScriptTypes.Root, null);
|
|
|
|
|
|
|
|
final JavaScriptClass NULL = new JavaScriptClass(this, JavaScriptTypes.Null, JavaScriptTypes.Root, null);
|
|
|
|
|
2007-04-19 13:53:31 +00:00
|
|
|
final JavaScriptClass ARRAY = new JavaScriptClass(this, JavaScriptTypes.Array, JavaScriptTypes.Root, null);
|
|
|
|
|
|
|
|
final JavaScriptClass OBJECT = new JavaScriptClass(this, JavaScriptTypes.Object, JavaScriptTypes.Root, null);
|
|
|
|
|
|
|
|
final JavaScriptClass TYPE_ERROR = new JavaScriptClass(this, JavaScriptTypes.TypeError, JavaScriptTypes.Root, null);
|
|
|
|
|
|
|
|
final JavaScriptClass CODE_BODY = new JavaScriptClass(this, JavaScriptTypes.CodeBody, JavaScriptTypes.Root, null);
|
|
|
|
|
|
|
|
final JavaScriptClass FUNCTION = new JavaScriptClass(this, JavaScriptTypes.Function, JavaScriptTypes.CodeBody, null);
|
|
|
|
|
|
|
|
final JavaScriptClass SCRIPT = new JavaScriptClass(this, JavaScriptTypes.Script, JavaScriptTypes.CodeBody, null);
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
final JavaScriptClass BOOLEAN = new JavaScriptClass(this, JavaScriptTypes.Boolean, JavaScriptTypes.Root, null);
|
|
|
|
|
|
|
|
final JavaScriptClass NUMBER = new JavaScriptClass(this, JavaScriptTypes.Number, JavaScriptTypes.Root, null);
|
|
|
|
|
|
|
|
final JavaScriptClass DATE = new JavaScriptClass(this, JavaScriptTypes.Date, JavaScriptTypes.Root, null);
|
|
|
|
|
|
|
|
final JavaScriptClass REGEXP = new JavaScriptClass(this, JavaScriptTypes.RegExp, JavaScriptTypes.Root, null);
|
|
|
|
|
2007-04-19 13:53:31 +00:00
|
|
|
final JavaScriptClass BOOLEAN_OBJECT = new JavaScriptClass(this, JavaScriptTypes.BooleanObject, JavaScriptTypes.Object, null);
|
2007-02-09 15:34:25 +00:00
|
|
|
|
2007-04-19 13:53:31 +00:00
|
|
|
final JavaScriptClass NUMBER_OBJECT = new JavaScriptClass(this, JavaScriptTypes.NumberObject, JavaScriptTypes.Object, null);
|
2007-02-09 15:34:25 +00:00
|
|
|
|
2007-04-19 13:53:31 +00:00
|
|
|
final JavaScriptClass DATE_OBJECT = new JavaScriptClass(this, JavaScriptTypes.DateObject, JavaScriptTypes.Object, null);
|
2007-02-09 15:34:25 +00:00
|
|
|
|
2007-04-19 13:53:31 +00:00
|
|
|
final JavaScriptClass REGEXP_OBJECT = new JavaScriptClass(this, JavaScriptTypes.RegExpObject, JavaScriptTypes.Object, null);
|
2007-02-09 15:34:25 +00:00
|
|
|
|
2007-04-19 13:53:31 +00:00
|
|
|
final JavaScriptClass STRING_OBJECT = new JavaScriptClass(this, JavaScriptTypes.StringObject, JavaScriptTypes.Object, null);
|
2007-02-02 17:25:09 +00:00
|
|
|
|
2007-06-01 03:32:56 +00:00
|
|
|
public IClass lookupClass(String className, IClassHierarchy cha) {
|
2007-02-02 17:25:09 +00:00
|
|
|
Assertions._assert(this.cha == cha);
|
2007-02-09 15:34:25 +00:00
|
|
|
return (IClass) types.get(TypeName.string2TypeName(className));
|
2007-02-02 17:25:09 +00:00
|
|
|
}
|
|
|
|
|
2007-07-06 03:07:29 +00:00
|
|
|
public IClass lookupClass(TypeName className) {
|
2007-02-09 15:34:25 +00:00
|
|
|
return (IClass) types.get(className);
|
2007-02-02 17:25:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public ClassLoaderReference getReference() {
|
|
|
|
return JavaScriptTypes.jsLoader;
|
|
|
|
}
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
public Iterator<IClass> iterateAllClasses() {
|
2007-02-02 17:25:09 +00:00
|
|
|
return types.values().iterator();
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getNumberOfClasses() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Atom getName() {
|
|
|
|
return getReference().getName();
|
|
|
|
}
|
|
|
|
|
2007-06-01 03:32:56 +00:00
|
|
|
public Language getLanguage() {
|
|
|
|
return JS;
|
|
|
|
}
|
|
|
|
|
2007-02-02 17:25:09 +00:00
|
|
|
public int getNumberOfMethods() {
|
2007-02-09 15:34:25 +00:00
|
|
|
return types.size();
|
2007-02-02 17:25:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public String getSourceFileName(IClass klass) {
|
|
|
|
return klass.getSourceFileName();
|
|
|
|
}
|
|
|
|
|
|
|
|
public IClassLoader getParent() {
|
|
|
|
// currently, JavaScript land does not interact with any other loaders
|
|
|
|
Assertions.UNREACHABLE("JavaScriptLoader.getParent() called?!?");
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void init(Set modules) throws IOException {
|
|
|
|
translatorFactory.make(this).translate(modules);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void removeAll(Collection toRemove) {
|
2007-02-09 15:34:25 +00:00
|
|
|
Set<TypeName> keys = new HashSet<TypeName>();
|
2007-02-02 17:25:09 +00:00
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
for (Iterator<Map.Entry<TypeName,IClass>> EE = types.entrySet().iterator(); EE.hasNext();) {
|
|
|
|
Map.Entry<TypeName,IClass> E = EE.next();
|
|
|
|
if (toRemove.contains(E.getValue())) {
|
|
|
|
keys.add(E.getKey());
|
2007-02-02 17:25:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-02-09 15:34:25 +00:00
|
|
|
for (Iterator KK = keys.iterator(); KK.hasNext();) {
|
|
|
|
types.remove(KK.next());
|
2007-02-02 17:25:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|