📄 scriptableobject.java
字号:
* <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 be // that slot.stringKey != name. To make last name cache work, need // to change the key slot.stringKey = name; lastAccess = slot; return slot; } private Slot getSlot(String id, int index) { Slot[] slots = this.slots; // Get local copy int i = getSlotPosition(slots, id, index); return (i < 0) ? null : slots[i]; } private static int getSlotPosition(Slot[] slots, String id, int index) { if (slots != null) { int start = (index & 0x7fffffff) % slots.length; int i = start; do { Slot slot = slots[i]; if (slot == null) break; if (slot != REMOVED && slot.intKey == index && (slot.stringKey == id || (id != null && id.equals(slot.stringKey)))) { return i; } if (++i == slots.length) i = 0; } while (i != start); } return -1; } /** * Add a new slot to the hash table. * * This method must be synchronized since it is altering the hash * table itself. Note that we search again for the slot to set * since another thread could have added the given property or * caused the table to grow while this thread was searching. */ private synchronized Slot addSlot(String id, int index, Slot newSlot) { if (isSealed()) { String str = (id != null) ? id : Integer.toString(index); throw Context.reportRuntimeError1("msg.add.sealed", str); } if (slots == null) { slots = new Slot[5]; } return addSlotImpl(id, index, newSlot); } // Must be inside synchronized (this) private Slot addSlotImpl(String id, int index, Slot newSlot) { int start = (index & 0x7fffffff) % slots.length; int i = start; for (;;) { Slot slot = slots[i]; if (slot == null || slot == REMOVED) { if ((4 * (count + 1)) > (3 * slots.length)) { grow(); return addSlotImpl(id, index, newSlot); } slot = (newSlot == null) ? new Slot() : newSlot;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -