move JS's Hashtable to JS.O
[org.ibex.core.git] / src / org / ibex / js / JS.java
index 0290e46..81feec1 100644 (file)
@@ -6,29 +6,68 @@ import java.io.*;
 import java.util.*;
 
 /** The minimum set of functionality required for objects which are manipulated by JavaScript */
-public class JS extends org.ibex.util.BalancedTree { 
+public abstract class JS { 
 
     public static boolean checkAssertions = false;
 
     public static final Object METHOD = new Object() { public String toString() { return "JS.METHOD"; } };
     public final JS unclone() { return _unclone(); }
     public final JS jsclone() throws JSExn { return new Clone(this); }
-    public Enumeration keys() throws JSExn { return entries == null ? emptyEnumeration : entries.keys(); }
-    public Object get(Object key) throws JSExn { return entries == null ? null : entries.get(key, null); }
-    public void put(Object key, Object val) throws JSExn { (entries==null?entries=new Hash():entries).put(key,null,val); }
+
+    public Enumeration keys() throws JSExn { throw new JSExn("you can't enumerate the keys of this object (class=" + this.getClass().getName() +")"); }
+    public Object get(Object key) throws JSExn { return null; }
+    public void put(Object key, Object val) throws JSExn { throw new JSExn("this object is read only (class=" + this.getClass().getName() +")"); }
+        
+    public final boolean hasTrap(Object key) { return getTrap(key) != null; }
+    
     public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
-        throw new JSExn("you cannot call this object (class=" + this.getClass().getName() +")");
+        throw new JSExn("method not found (" + method + ")");
     }    
+    
     // FIXME: JSArgs objects, pointers into stack frame
     public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
         throw new JSExn("you cannot call this object (class=" + this.getClass().getName() +")");
     }
 
+    Trap getTrap(Object key) { return null; }
+    void putTrap(Object key, Trap value) throws JSExn { throw new JSExn("traps cannot be placed on this object (class=" + this.getClass().getName() +")"); }
+    
+    public static class O extends JS {
+        private Hash entries;
+        
+        public Enumeration keys() throws JSExn { return entries == null ? emptyEnumeration : entries.keys(); }
+        public Object get(Object key) throws JSExn { return entries == null ? null : entries.get(key, null); }
+        public void put(Object key, Object val) throws JSExn { (entries==null?entries=new Hash():entries).put(key,null,val); }        
+
+        /** retrieve a trap from the entries hash */
+        final Trap getTrap(Object key) {
+            return entries == null ? null : (Trap)entries.get(key, Trap.class);
+        }
+        
+        /** retrieve a trap from the entries hash */
+        final void putTrap(Object key, Trap value) {
+            if (entries == null) entries = new Hash();
+            entries.put(key, Trap.class, value);
+        }    
+    }
+    
+    public static class BT extends O {
+        private BalancedTree bt;
+        private final BalancedTree bt() { if(bt != null) return bt; return bt = new BalancedTree(); }
+        public final void insertNode(int index, Object o) { bt().insertNode(index,o); }
+        public final void clear() { bt().clear(); }
+        public final Object getNode(int i) { return bt().getNode(i); }
+        public final int treeSize() { return bt().treeSize(); }
+        public final Object deleteNode(int i) { return bt().deleteNode(i); }
+        public final void replaceNode(int index, Object o) { bt().replaceNode(index,o); }
+        public final int indexNode(Object o) { return bt().indexNode(o); }
+    }
+    
     JS _unclone() { return this; }
     
     public interface Cloneable { }
     
-    public static class Clone extends JS implements Cloneable {
+    public static class Clone extends JS.O implements Cloneable {
         protected JS clonee;
         JS _unclone() { return clonee.unclone(); }
         public JS getClonee() { return clonee; }
@@ -178,8 +217,6 @@ public class JS extends org.ibex.util.BalancedTree {
             public Object nextElement() { throw new NoSuchElementException(); }
         };
     
-    private Hash entries = null;
-
     public static JS fromReader(String sourceName, int firstLine, Reader sourceCode) throws IOException {
         return JSFunction._fromReader(sourceName, firstLine, sourceCode);
     }
@@ -192,11 +229,6 @@ public class JS extends org.ibex.util.BalancedTree {
 
     // Trap support //////////////////////////////////////////////////////////////////////////////
 
-    /** override and return true to allow placing traps on this object.
-     *  if isRead true, this is a read trap, otherwise write trap
-     **/
-    protected boolean isTrappable(Object name, boolean isRead) { return true; }
-
     /** performs a put, triggering traps if present; traps are run in an unpauseable interpreter */
     public void putAndTriggerTraps(Object key, Object value) throws JSExn {
         Trap t = getTrap(key);
@@ -211,28 +243,16 @@ public class JS extends org.ibex.util.BalancedTree {
         else return get(key);
     }
 
-    /** retrieve a trap from the entries hash */
-    protected final Trap getTrap(Object key) {
-        return entries == null ? null : (Trap)entries.get(key, Trap.class);
-    }
-
-    /** retrieve a trap from the entries hash */
-    protected final void putTrap(Object key, Trap value) {
-        if (entries == null) entries = new Hash();
-        entries.put(key, Trap.class, value);
-    }
-
     /** adds a trap, avoiding duplicates */
     protected final void addTrap(Object name, JSFunction f) throws JSExn {
         if (f.numFormalArgs > 1) throw new JSExn("traps must take either one argument (write) or no arguments (read)");
         boolean isRead = f.numFormalArgs == 0;
-        if (!isTrappable(name, isRead)) throw new JSExn("not allowed "+(isRead?"read":"write")+" trap on property: "+name);
         for(Trap t = getTrap(name); t != null; t = t.next) if (t.f == f) return;
         putTrap(name, new Trap(this, name.toString(), f, (Trap)getTrap(name)));
     }
 
     /** deletes a trap, if present */
-    protected final void delTrap(Object name, JSFunction f) {
+    protected final void delTrap(Object name, JSFunction f) throws JSExn {
         Trap t = (Trap)getTrap(name);
         if (t == null) return;
         if (t.f == f) { putTrap(t.name, t.next); return; }