Index: src/org/mozilla/javascript/ScriptableObject.java =================================================================== RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/ScriptableObject.java,v retrieving revision 1.125 diff -u -r1.125 ScriptableObject.java --- src/org/mozilla/javascript/ScriptableObject.java 18 Jul 2007 15:09:25 -0000 1.125 +++ src/org/mozilla/javascript/ScriptableObject.java 14 Jan 2008 13:24:32 -0000 @@ -149,6 +149,12 @@ private static final int SLOT_MODIFY_GETTER_SETTER = 4; private static final int SLOT_MODIFY_CONST = 5; + // Used to indicate a failed MOP call. + private static final Object MOP_CALL_FAILED = new Object(); + + // Guards to protect against infinite recursion with __metaobject__.get. + boolean disableMop = false; + private static class Slot implements Serializable { static final long serialVersionUID = -3539051633409902634L; @@ -240,6 +246,60 @@ * method. */ public abstract String getClassName(); + + /** + * Gets the metaobject for the specified object. + */ + protected ScriptableObject getMetaobject(Scriptable object) + { + if (this.has("__metaobject__", object, null)) + { + Object o = this.get("__metaobject__", object, null); + if (o instanceof ScriptableObject) + { + ScriptableObject metaobject = (ScriptableObject) o; + //We want to disable MOP for the metaobject itself + metaobject.put("__metaobject__", metaobject, null); + + return metaobject; + } + } + return null; + } + + /** + * Call the function on the specified metaobject, if the function + * is specified by the metaobject. + * + * @param metaobject Metaobject which may have the function. + * @param metafunction Function name to call. + * @param start Scope of the metaobject, used to disable the metaobject. + * @param args Arguments to pass to the function. + * + * @return Result of the call, or MOP_CALL_FAILED. + */ + protected Object callMetafunction(ScriptableObject metaobject, String metafunction, + Scriptable start, Object[] args) + { + Object value = MOP_CALL_FAILED; + if (metaobject.has(metafunction, metaobject, null)) + { + try + { + start.put("__metaobject__", start, null); + Context cx = Context.getCurrentContext(); + Callable f = ScriptRuntime.getNameFunctionAndThis(metafunction, cx, metaobject); + Scriptable thisObj = ScriptRuntime.lastStoredScriptable(cx); + + value = f.call(cx, start, thisObj, args); + } + finally + { + start.put("__metaobject__", start, metaobject); + } + } + return value; + } /** * Returns true if the named property is defined. @@ -248,10 +308,30 @@ * @param start the object in which the lookup began * @return true if and only if the property was found in the object */ - public boolean has(String name, Scriptable start) + public boolean has(String name, Scriptable start, Object flag) { return null != getSlot(name, 0, SLOT_QUERY); } + + /** + * This looks for a metaobject, specified by the __metaobject__ property. + * If one is found, __metaobject__.has(thisObj,property) will be called. + * + * @see org.mozilla.javascript.Scriptable#has(java.lang.String, org.mozilla.javascript.Scriptable) + */ + public boolean has(String name, Scriptable start) { + ScriptableObject metaobject = this.getMetaobject(start); + if (metaobject != null) + { + Object[] args = new Object[2]; + args[0] = start; + args[1] = name; + + Object value = this.callMetafunction(metaobject, "has", start, args); + if (value != MOP_CALL_FAILED) return ScriptRuntime.toBoolean(value); + } + return this.has(name, start, null); + } /** * Returns true if the property index is defined. @@ -260,10 +340,28 @@ * @param start the object in which the lookup began * @return true if and only if the property was found in the object */ - public boolean has(int index, Scriptable start) + public boolean has(int index, Scriptable start, Object flag) { return null != getSlot(null, index, SLOT_QUERY); } + + /** + * Looks for __metaobject__.has. If not found, works normally. + * @see org.mozilla.javascript.Scriptable#has(int, org.mozilla.javascript.Scriptable) + */ + public boolean has(int index, Scriptable start) { + ScriptableObject metaobject = this.getMetaobject(start); + if (metaobject != null) + { + Object[] args = new Object[2]; + args[0] = start; + args[1] = new Integer(index); + + Object value = this.callMetafunction(metaobject, "has", start, args); + if (value != MOP_CALL_FAILED) return ScriptRuntime.toBoolean(value); + } + return this.has(index, start, null); + } /** * Returns the value of the named property or NOT_FOUND. @@ -275,10 +373,36 @@ * @param start the object in which the lookup began * @return the value of the property (may be null), or NOT_FOUND */ - public Object get(String name, Scriptable start) + public Object get(String name, Scriptable start, Object flag) { return getImpl(name, 0, start); } + + /** + * This checks for the existence of a __metaobject__ property with a get method. + * Otherwise, this method will work normally. + * + * @see org.mozilla.javascript.Scriptable#get(java.lang.String, org.mozilla.javascript.Scriptable) + */ + public Object get(String name, Scriptable start) + { + ScriptableObject metaobject = this.getMetaobject(start); + if (metaobject != null && !this.disableMop)// && !name.equals(lastPropCalled)) + { + //lastPropCalled = name; + this.disableMop = true; + + Object[] args = new Object[2]; + args[0] = start; + args[1] = name; + + Object value = this.callMetafunction(metaobject, "get", start, args); + //lastPropCalled = null; + this.disableMop = false; + if (value != MOP_CALL_FAILED) return value; + } + return this.get(name, start, null); + } /** * Returns the value of the indexed property or NOT_FOUND. @@ -287,10 +411,34 @@ * @param start the object in which the lookup began * @return the value of the property (may be null), or NOT_FOUND */ - public Object get(int index, Scriptable start) + public Object get(int index, Scriptable start, Object flag) { return getImpl(null, index, start); } + + /** + * This checks for the existence of a __metaobject__ property with a get method. + * Otherwise, this method will work normally. + * @see org.mozilla.javascript.Scriptable#get(int, org.mozilla.javascript.Scriptable) + */ + public Object get(int index, Scriptable start) { + ScriptableObject metaobject = this.getMetaobject(start); + if (metaobject != null && !this.disableMop)//(lastIndexCalled==null || index!=lastIndexCalled.intValue())) + { + //lastIndexCalled = new Integer(index); + this.disableMop = true; + + Object[] args = new Object[2]; + args[0] = start; + args[1] = new Integer(index); + + Object value = this.callMetafunction(metaobject, "get", start, args); + //lastIndexCalled = null; + this.disableMop = false; + if (value != MOP_CALL_FAILED) return value; + } + return this.get(index, start, null); + } /** * Sets the value of the named property, creating it if need be. @@ -307,7 +455,7 @@ * @param start the object whose property is being set * @param value value to set the property to */ - public void put(String name, Scriptable start, Object value) + public void put(String name, Scriptable start, Object value, Object flag) { if (putImpl(name, 0, start, value, EMPTY)) return; @@ -315,6 +463,26 @@ if (start == this) throw Kit.codeBug(); start.put(name, start, value); } + + /** + * Looks for __metaobject__.set. Otherwise, works normally. + * @see org.mozilla.javascript.Scriptable#put(java.lang.String, org.mozilla.javascript.Scriptable, java.lang.Object) + */ + public void put(String name, Scriptable start, Object value) + { + ScriptableObject metaobject = this.getMetaobject(start); + if (metaobject != null && !name.equals("__metaobject__")) + { + Object[] args = new Object[3]; + args[0] = start; + args[1] = name; + args[2] = value; + + Object result = this.callMetafunction(metaobject, "set", start, args); + if (result != MOP_CALL_FAILED) return; + } + this.put(name, start, value, null); + } /** * Sets the value of the indexed property, creating it if need be. @@ -323,7 +491,7 @@ * @param start the object whose property is being set * @param value value to set the property to */ - public void put(int index, Scriptable start, Object value) + public void put(int index, Scriptable start, Object value, Object flag) { if (putImpl(null, index, start, value, EMPTY)) return; @@ -331,6 +499,26 @@ if (start == this) throw Kit.codeBug(); start.put(index, start, value); } + + /** + * Looks for __metaobject__.set. Otherwise, works normally. + * @see org.mozilla.javascript.Scriptable#put(int, org.mozilla.javascript.Scriptable, java.lang.Object) + */ + public void put(int index, Scriptable start, Object value) + { + ScriptableObject metaobject = this.getMetaobject(start); + if (metaobject != null) + { + Object[] args = new Object[3]; + args[0] = start; + args[1] = new Integer(index); + args[2] = value; + + Object result = this.callMetafunction(metaobject, "set", start, args); + if (result != MOP_CALL_FAILED) return; + } + this.put(index, start, value, null); + } /** * Removes a named property from the object. @@ -340,11 +528,31 @@ * * @param name the name of the property */ - public void delete(String name) + public void delete(String name, Object flag) { checkNotSealed(name, 0); accessSlot(name, 0, SLOT_REMOVE); } + + /** + * Looks for __metaobject__.remove(). If not found, works normally. + * @see org.mozilla.javascript.Scriptable#delete(java.lang.String) + */ + public void delete(String name) + { + Scriptable start = this; + ScriptableObject metaobject = this.getMetaobject(start); + if (metaobject != null) + { + Object[] args = new Object[2]; + args[0] = start; + args[1] = name; + + Object result = this.callMetafunction(metaobject, "remove", start, args); + if (result != MOP_CALL_FAILED) return; + } + this.delete(name, null); + } /** * Removes the indexed property from the object. @@ -354,11 +562,31 @@ * * @param index the numeric index for the property */ - public void delete(int index) + public void delete(int index, Object flag) { checkNotSealed(null, index); accessSlot(null, index, SLOT_REMOVE); } + + /** + * Looks for __metaobject__.remove(). If not found, works normally. + * @see org.mozilla.javascript.Scriptable#delete(int) + */ + public void delete(int index) + { + Scriptable start = this; + ScriptableObject metaobject = this.getMetaobject(start); + if (metaobject != null) + { + Object[] args = new Object[2]; + args[0] = start; + args[1] = new Integer(index); + + Object result = this.callMetafunction(metaobject, "remove", start, args); + if (result != MOP_CALL_FAILED) return; + } + this.delete(index, null); + } /** * Sets the value of the named const property, creating it if need be. @@ -606,6 +834,7 @@ /** * Sets the prototype of the object. + * @return */ public void setPrototype(Scriptable m) { @@ -639,9 +868,44 @@ * Integer entry in the returned array. Properties accessed by * a String will have a String entry in the returned array. */ - public Object[] getIds() { + public Object[] getIds(Object flag) { return getIds(false); } + + /** + * Returns ids for the current object, deferring to + * __metaobject__.getIds(thisObj), if that exists. + * @see org.mozilla.javascript.Scriptable#getIds() + */ + public Object[] getIds() + { + Scriptable start = this; + ScriptableObject metaobject = this.getMetaobject(start); + + if (metaobject != null) + { + Object[] args = new Object[1]; + args[0] = start; + + Object result = this.callMetafunction(metaobject, "getIds", start, args); + if (result != MOP_CALL_FAILED) { + if (result instanceof NativeArray) { + NativeArray na = (NativeArray) result; + Object[] naIds = na.getIds(); + Object[] ids = new Object[naIds.length]; + for (int i=0; i== operator. Index: src/org/mozilla/javascript/BaseFunction.java =================================================================== RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/BaseFunction.java,v retrieving revision 1.65 diff -u -r1.65 BaseFunction.java --- src/org/mozilla/javascript/BaseFunction.java 28 Nov 2007 10:15:06 -0000 1.65 +++ src/org/mozilla/javascript/BaseFunction.java 14 Jan 2008 13:24:32 -0000 @@ -88,7 +88,7 @@ * value's prototype chain * */ - public boolean hasInstance(Scriptable instance) + public boolean hasInstance(Scriptable instance, Object flag) { Object protoProp = ScriptableObject.getProperty(this, "prototype"); if (protoProp instanceof Scriptable) {