📄 scriptableobject.java
字号:
/** * Puts an indexed property in an object or in an object in its prototype chain. * <p> * Seaches for the indexed property in the prototype chain. If it is found, * the value of the property in <code>obj</code> is changed through a call * to {@link Scriptable#put(int, Scriptable, Object)} on the prototype * passing <code>obj</code> as the <code>start</code> argument. This allows * the prototype to veto the property setting in case the prototype defines * the property with [[ReadOnly]] attribute. If the property is not found, * it is added in <code>obj</code>. * @param obj a JavaScript object * @param index a property index * @param value any JavaScript value accepted by Scriptable.put * @since 1.5R2 */ public static void putProperty(Scriptable obj, int index, Object value) { Scriptable base = getBase(obj, index); if (base == null) base = obj; base.put(index, obj, value); } /** * Removes the property from an object or its prototype chain. * <p> * Searches for a property with <code>name</code> in obj or * its prototype chain. If it is found, the object's delete * method is called. * @param obj a JavaScript object * @param name a property name * @return true if the property doesn't exist or was successfully removed * @since 1.5R2 */ public static boolean deleteProperty(Scriptable obj, String name) { Scriptable base = getBase(obj, name); if (base == null) return true; base.delete(name); return !base.has(name, obj); } /** * Removes the property from an object or its prototype chain. * <p> * Searches for a property with <code>index</code> in obj or * its prototype chain. If it is found, the object's delete * method is called. * @param obj a JavaScript object * @param index a property index * @return true if the property doesn't exist or was successfully removed * @since 1.5R2 */ public static boolean deleteProperty(Scriptable obj, int index) { Scriptable base = getBase(obj, index); if (base == null) return true; base.delete(index); return !base.has(index, obj); } /** * Returns an array of all ids from an object and its prototypes. * <p> * @param obj a JavaScript object * @return an array of all ids from all object in the prototype chain. * If a given id occurs multiple times in the prototype chain, * it will occur only once in this list. * @since 1.5R2 */ public static Object[] getPropertyIds(Scriptable obj) { if (obj == null) { return ScriptRuntime.emptyArgs; } Object[] result = obj.getIds(); ObjToIntMap map = null; for (;;) { obj = obj.getPrototype(); if (obj == null) { break; } Object[] ids = obj.getIds(); if (ids.length == 0) { continue; } if (map == null) { if (result.length == 0) { result = ids; continue; } map = new ObjToIntMap(result.length + ids.length); for (int i = 0; i != result.length; ++i) { map.intern(result[i]); } result = null; // Allow to GC the result } for (int i = 0; i != ids.length; ++i) { map.intern(ids[i]); } } if (map != null) { result = map.getKeys(); } return result; } /** * Call a method of an object. * @param obj the JavaScript object * @param methodName the name of the function property * @param args the arguments for the call * * @see Context#getCurrentContext() */ public static Object callMethod(Scriptable obj, String methodName, Object[] args) { return callMethod(null, obj, methodName, args); } /** * Call a method of an object. * @param cx the Context object associated with the current thread. * @param obj the JavaScript object * @param methodName the name of the function property * @param args the arguments for the call */ public static Object callMethod(Context cx, Scriptable obj, String methodName, Object[] args) { Object funObj = getProperty(obj, methodName); if (!(funObj instanceof Function)) { throw ScriptRuntime.notFunctionError(obj, methodName); } Function fun = (Function)funObj; // XXX: What should be the scope when calling funObj? // The following favor scope stored in the object on the assumption // that is more useful especially under dynamic scope setup. // An alternative is to check for dynamic scope flag // and use ScriptableObject.getTopLevelScope(fun) if the flag is not // set. But that require access to Context and messy code // so for now it is not checked. Scriptable scope = ScriptableObject.getTopLevelScope(obj); if (cx != null) { return fun.call(cx, scope, obj, args); } else { return Context.call(null, fun, scope, obj, args); } } private static Scriptable getBase(Scriptable obj, String name) { do { if (obj.has(name, obj)) break; obj = obj.getPrototype(); } while(obj != null); return obj; } private static Scriptable getBase(Scriptable obj, int index) { do { if (obj.has(index, obj)) break; obj = obj.getPrototype(); } while(obj != null); return obj; } /** * Get arbitrary application-specific value associated with this object. * @param key key object to select particular value. * @see #associateValue(Object key, Object value) */ public final Object getAssociatedValue(Object key) { Hashtable h = associatedValues; if (h == null) return null; return h.get(key); } /** * Get arbitrary application-specific value associated with the top scope * of the given scope. * The method first calls {@link #getTopLevelScope(Scriptable scope)} * and then searches the prototype chain of the top scope for the first * object containing the associated value with the given key. * * @param scope the starting scope. * @param key key object to select particular value. * @see #getAssociatedValue(Object key) */ public static Object getTopScopeValue(Scriptable scope, Object key) { scope = ScriptableObject.getTopLevelScope(scope); for (;;) { if (scope instanceof ScriptableObject) { ScriptableObject so = (ScriptableObject)scope; Object value = so.getAssociatedValue(key); if (value != null) { return value; } } scope = scope.getPrototype(); if (scope == null) { return null; } } } /** * Associate arbitrary application-specific value with this object. * Value can only be associated with the given object and key only once. * The method ignores any subsequent attempts to change the already * associated value. * <p> The associated values are not serilized. * @param key key object to select particular value. * @param value the value to associate * @return the passed value if the method is called first time for the * given key or old value for any subsequent calls. * @see #getAssociatedValue(Object key) */ public final Object associateValue(Object key, Object value) { if (value == null) throw new IllegalArgumentException(); Hashtable h = associatedValues; if (h == null) { synchronized (this) { h = associatedValues; if (h == null) { h = new Hashtable(); associatedValues = h; } } } return Kit.initHash(h, key, value); } private Object getByGetter(GetterSlot slot, Scriptable start) { Object getterThis; Object[] args; if (slot.delegateTo == null) { if (start != this) { // Walk the prototype chain to find an appropriate // object to invoke the getter on. Class clazz = slot.getter.getDeclaringClass(); while (!clazz.isInstance(start)) { start = start.getPrototype(); if (start == this) { break; } if (start == null) { start = this; break; } } } getterThis = start; args = ScriptRuntime.emptyArgs; } else { getterThis = slot.delegateTo; args = new Object[] { this }; } return slot.getter.invoke(getterThis, args); } private void setBySetter(GetterSlot slot, Scriptable start, Object value) { if (start != this) { if (slot.delegateTo != null || !slot.setter.getDeclaringClass().isInstance(start)) { start.put(slot.stringKey, start, value); return; } } Object setterThis; Object[] args; Object setterResult; Context cx = Context.getContext(); Class pTypes[] = slot.setter.argTypes; Class desired = pTypes[pTypes.length - 1]; // ALERT: cache tag since it is already calculated in defineProperty ? int tag = FunctionObject.getTypeTag(desired); Object actualArg = FunctionObject.convertArg(cx, start, value, tag); if (slot.delegateTo == null) { setterThis = start; args = new Object[] { actualArg }; } else { if (start != this) Kit.codeBug(); setterThis = slot.delegateTo; args = new Object[] { this, actualArg }; } // Check start is sealed: start is always instance of ScriptableObject // due to logic in if (start != this) above if (((ScriptableObject)start).isSealed()) { throw Context.reportRuntimeError1("msg.modify.sealed", slot.stringKey); } setterResult = slot.setter.invoke(setterThis, args); if (slot.setter.method().getReturnType() != Void.TYPE) { // Replace Getter slot by a simple one Slot replacement = new Slot(); replacement.intKey = slot.intKey; replacement.stringKey = slot.stringKey; replacement.attributes = slot.attributes; replacement.value = setterResult; synchronized (this) { int i = getSlotPosition(slots, slot.stringKey, slot.intKey); // Check slot was not deleted/replaced before synchronization if (i >= 0 && slots[i] == slot) { slots[i] = replacement; // It is important to make sure that lastAccess != slot // to prevent accessing the old slot via lastAccess and // then invoking setter one more time lastAccess = replacement; } } } } private Slot getNamedSlot(String name) { // Query last access cache and check that it was not deleted Slot slot = lastAccess; if (name == slot.stringKey && slot.wasDeleted == 0) { return slot; } int hash = name.hashCode(); Slot[] slots = this.slots; // Get stable local reference int i = getSlotPosition(slots, name, hash); if (i < 0) { return null; } slot = slots[i]; // Update cache - here stringKey.equals(name) holds, but it can b
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -