+++ /dev/null
-/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-\r
- *\r
- * The contents of this file are subject to the Netscape Public\r
- * License Version 1.1 (the "License"); you may not use this file\r
- * except in compliance with the License. You may obtain a copy of\r
- * the License at http://www.mozilla.org/NPL/\r
- *\r
- * Software distributed under the License is distributed on an "AS\r
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
- * implied. See the License for the specific language governing\r
- * rights and limitations under the License.\r
- *\r
- * The Original Code is Rhino code, released\r
- * May 6, 1999.\r
- *\r
- * The Initial Developer of the Original Code is Netscape\r
- * Communications Corporation. Portions created by Netscape are\r
- * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
- * Rights Reserved.\r
- *\r
- * Contributor(s):\r
- * Igor Bukanov\r
- *\r
- * Alternatively, the contents of this file may be used under the\r
- * terms of the GNU Public License (the "GPL"), in which case the\r
- * provisions of the GPL are applicable instead of those above.\r
- * If you wish to allow use of your version of this file only\r
- * under the terms of the GPL and not to allow others to use your\r
- * version of this file under the NPL, indicate your decision by\r
- * deleting the provisions above and replace them with the notice\r
- * and other provisions required by the GPL. If you do not delete\r
- * the provisions above, a recipient may use your version of this\r
- * file under either the NPL or the GPL.\r
- */\r
-\r
-package org.mozilla.javascript;\r
-\r
-/**\r
-Base class for native object implementation that uses IdFunction to export its methods to script via <class-name>.prototype object.\r
-\r
-Any descendant should implement at least the following methods:\r
- mapNameToId\r
- getIdName\r
- execMethod\r
- methodArity\r
-\r
-To define non-function properties, the descendant should customize\r
- getIdValue\r
- setIdValue\r
- getIdDefaultAttributes\r
- maxInstanceId\r
-to get/set property value and provide its default attributes.\r
-\r
-To customize initializition of constructor and protype objects, descendant\r
-may override scopeInit or fillConstructorProperties methods.\r
-\r
-*/\r
-public abstract class IdScriptable extends ScriptableObject\r
- implements IdFunctionMaster\r
-{\r
- /** NULL_TAG can be used to distinguish between uninitialized and null\r
- ** values\r
- */\r
- protected static final Object NULL_TAG = new Object();\r
-\r
- public IdScriptable() {\r
- activateIdMap(maxInstanceId());\r
- }\r
- \r
- public boolean has(String name, Scriptable start) {\r
- if (maxId != 0) {\r
- int id = mapNameToId(name);\r
- if (id != 0) {\r
- return hasValue(id);\r
- }\r
- }\r
- return super.has(name, start);\r
- }\r
-\r
- public Object get(String name, Scriptable start) {\r
- if (CACHE_NAMES) {\r
- int maxId = this.maxId;\r
- L:if (maxId != 0) {\r
- Object[] data = idMapData;\r
- if (data == null) { \r
- int id = mapNameToId(name);\r
- if (id != 0) {\r
- return getIdValue(id);\r
- }\r
- }\r
- else {\r
- int id = lastIdCache;\r
- if (data[id - 1 + maxId] != name) {\r
- id = mapNameToId(name);\r
- if (id == 0) { break L; }\r
- data[id - 1 + maxId] = name;\r
- lastIdCache = id;\r
- }\r
- Object value = data[id - 1];\r
- if (value == null) {\r
- value = getIdValue(id);\r
- }\r
- else if (value == NULL_TAG) {\r
- value = null;\r
- }\r
- return value;\r
- }\r
- }\r
- }\r
- else {\r
- if (maxId != 0) {\r
- int id = mapNameToId(name);\r
- if (id != 0) {\r
- Object[] data = idMapData;\r
- if (data == null) { \r
- return getIdValue(id);\r
- }\r
- else {\r
- Object value = data[id - 1];\r
- if (value == null) {\r
- value = getIdValue(id);\r
- }\r
- else if (value == NULL_TAG) {\r
- value = null;\r
- }\r
- return value;\r
- }\r
- }\r
- }\r
- }\r
- return super.get(name, start);\r
- }\r
-\r
- public void put(String name, Scriptable start, Object value) {\r
- if (maxId != 0) {\r
- int id = mapNameToId(name);\r
- if (id != 0) {\r
- int attr = getAttributes(id);\r
- if ((attr & READONLY) == 0) {\r
- if (start == this) {\r
- setIdValue(id, value);\r
- }\r
- else {\r
- start.put(name, start, value);\r
- }\r
- }\r
- return;\r
- }\r
- }\r
- super.put(name, start, value);\r
- }\r
-\r
- public void delete(String name) {\r
- if (maxId != 0) {\r
- int id = mapNameToId(name);\r
- if (id != 0) {\r
- // Let the super class to throw exceptions for sealed objects\r
- if (!isSealed()) {\r
- int attr = getAttributes(id);\r
- if ((attr & PERMANENT) == 0) {\r
- deleteIdValue(id);\r
- }\r
- return;\r
- }\r
- }\r
- }\r
- super.delete(name);\r
- }\r
-\r
- public int getAttributes(String name, Scriptable start)\r
- throws PropertyException\r
- {\r
- if (maxId != 0) {\r
- int id = mapNameToId(name);\r
- if (id != 0) {\r
- if (hasValue(id)) {\r
- return getAttributes(id);\r
- }\r
- // For ids with deleted values super will throw exceptions\r
- }\r
- }\r
- return super.getAttributes(name, start);\r
- }\r
-\r
- public void setAttributes(String name, Scriptable start,\r
- int attributes)\r
- throws PropertyException\r
- {\r
- if (maxId != 0) {\r
- int id = mapNameToId(name);\r
- if (id != 0) {\r
- if (hasValue(id)) {\r
- synchronized (this) {\r
- setAttributes(id, attributes);\r
- }\r
- return;\r
- }\r
- // For ids with deleted values super will throw exceptions\r
- }\r
- }\r
- super.setAttributes(name, start, attributes);\r
- }\r
-\r
- synchronized void addPropertyAttribute(int attribute) {\r
- extraIdAttributes |= (byte)attribute;\r
- super.addPropertyAttribute(attribute);\r
- }\r
-\r
- /**\r
- * Redefine ScriptableObject.defineProperty to allow changing\r
- * values/attributes of id-based properties unless \r
- * getIdDefaultAttributes contains the READONLY attribute.\r
- * @see #getIdDefaultAttributes\r
- * @see org.mozilla.javascript.ScriptableObject#defineProperty\r
- */\r
- public void defineProperty(String propertyName, Object value,\r
- int attributes)\r
- {\r
- if (maxId != 0) {\r
- int id = mapNameToId(propertyName);\r
- if (id != 0) {\r
- int default_attributes = getIdDefaultAttributes(id);\r
- if ((default_attributes & READONLY) != 0) {\r
- // It is a bug to redefine id with readonly attributes\r
- throw new RuntimeException\r
- ("Attempt to redefine read-only id " + propertyName);\r
- }\r
- setAttributes(id, attributes);\r
- setIdValue(id, value);\r
- return;\r
- }\r
- }\r
- super.defineProperty(propertyName, value, attributes);\r
- }\r
-\r
- Object[] getIds(boolean getAll) {\r
- Object[] result = super.getIds(getAll);\r
- \r
- if (maxId != 0) {\r
- Object[] ids = null;\r
- int count = 0;\r
- \r
- for (int id = maxId; id != 0; --id) {\r
- if (hasValue(id)) {\r
- if (getAll || (getAttributes(id) & DONTENUM) == 0) {\r
- if (count == 0) {\r
- // Need extra room for nor more then [1..id] names\r
- ids = new Object[id];\r
- }\r
- ids[count++] = getIdName(id);\r
- }\r
- }\r
- }\r
- if (count != 0) {\r
- if (result.length == 0 && ids.length == count) {\r
- result = ids;\r
- }\r
- else {\r
- Object[] tmp = new Object[result.length + count];\r
- System.arraycopy(result, 0, tmp, 0, result.length);\r
- System.arraycopy(ids, 0, tmp, result.length, count);\r
- result = tmp;\r
- }\r
- }\r
- }\r
- return result;\r
- }\r
-\r
- /** Return maximum id number that should be present in each instance. */\r
- protected int maxInstanceId() { return 0; }\r
-\r
- /**\r
- * Map name to id of prototype or instance property.\r
- * Should return 0 if not found\r
- */\r
- protected abstract int mapNameToId(String name);\r
-\r
- /** Map id back to property name it defines.\r
- */\r
- protected abstract String getIdName(int id);\r
-\r
- /** Get default attributes for id. \r
- ** Default implementation return DONTENUM that is the standard attribute \r
- ** for core EcmaScript function. Typically descendants need to overwrite\r
- ** this for non-function attributes like length to return\r
- ** DONTENUM | READONLY | PERMANENT or DONTENUM | PERMANENT\r
- */\r
- protected int getIdDefaultAttributes(int id) {\r
- return DONTENUM;\r
- }\r
-\r
- /** Check if id value exists.\r
- ** Default implementation always returns true */\r
- protected boolean hasIdValue(int id) {\r
- return true;\r
- }\r
-\r
- /** Get id value. \r
- ** If id value is constant, descendant can call cacheIdValue to store\r
- ** value in the permanent cache.\r
- ** Default implementation creates IdFunction instance for given id\r
- ** and cache its value\r
- */\r
- protected Object getIdValue(int id) {\r
- IdFunction f = newIdFunction(id);\r
- f.setParentScope(getParentScope());\r
- return cacheIdValue(id, f);\r
- }\r
-\r
- /**\r
- * Set id value. \r
- * IdScriptable never calls this method if result of\r
- * <code>getIdDefaultAttributes(id)</code> contains READONLY attribute.\r
- * Descendants can overwrite this method to provide custom handler for\r
- * property assignments.\r
- */\r
- protected void setIdValue(int id, Object value) {\r
- synchronized (this) {\r
- ensureIdData()[id - 1] = (value != null) ? value : NULL_TAG;\r
- }\r
- }\r
- \r
- /**\r
- * Store value in permanent cache unless value was already assigned to id.\r
- * After this call IdScriptable never calls hasIdValue and getIdValue \r
- * for the given id.\r
- */\r
- protected Object cacheIdValue(int id, Object value) {\r
- synchronized (this) {\r
- Object[] data = ensureIdData();\r
- Object curValue = data[id - 1];\r
- if (curValue == null) {\r
- data[id - 1] = (value != null) ? value : NULL_TAG;\r
- }\r
- else {\r
- value = curValue;\r
- }\r
- }\r
- return value;\r
- }\r
- \r
- /**\r
- * Delete value represented by id so hasIdValue return false. \r
- * IdScriptable never calls this method if result of\r
- * <code>getIdDefaultAttributes(id)</code> contains PERMANENT attribute.\r
- * Descendants can overwrite this method to provide custom handler for\r
- * property delete.\r
- */\r
- protected void deleteIdValue(int id) {\r
- synchronized (this) {\r
- ensureIdData()[id - 1] = NOT_FOUND;\r
- }\r
- }\r
- \r
- /** 'thisObj' will be null if invoked as constructor, in which case\r
- ** instance of Scriptable should be returned. */\r
- public Object execMethod(int methodId, IdFunction function,\r
- Context cx, Scriptable scope,\r
- Scriptable thisObj, Object[] args)\r
- throws JavaScriptException\r
- {\r
- throw IdFunction.onBadMethodId(this, methodId);\r
- }\r
-\r
- /** Get arity or defined argument count for method with given id. \r
- ** Should return -1 if methodId is not known or can not be used\r
- ** with execMethod call. */\r
- public int methodArity(int methodId) {\r
- return -1;\r
- }\r
- \r
- /** Activate id support with the given maximum id */\r
- protected void activateIdMap(int maxId) {\r
- this.maxId = maxId;\r
- }\r
- \r
- /** Sets whether newly constructed function objects should be sealed */\r
- protected void setSealFunctionsFlag(boolean sealed) {\r
- setSetupFlag(SEAL_FUNCTIONS_FLAG, sealed);\r
- }\r
- \r
- /** \r
- * Set parameters of function properties. \r
- * Currently only determines whether functions should use dynamic scope.\r
- * @param cx context to read function parameters.\r
- * \r
- * @see org.mozilla.javascript.Context#hasCompileFunctionsWithDynamicScope\r
- */\r
- protected void setFunctionParametrs(Context cx) {\r
- setSetupFlag(USE_DYNAMIC_SCOPE_FLAG,\r
- cx.hasCompileFunctionsWithDynamicScope());\r
- }\r
- \r
- private void setSetupFlag(int flag, boolean value) {\r
- setupFlags = (byte)(value ? setupFlags | flag : setupFlags & ~flag);\r
- }\r
-\r
- /** \r
- * Prepare this object to serve as the prototype property of constructor \r
- * object with name <code>getClassName()<code> defined in\r
- * <code>scope</code>.\r
- * @param maxId maximum id available in prototype object\r
- * @param cx current context\r
- * @param scope object to define constructor in.\r
- * @param sealed indicates whether object and all its properties should \r
- * be sealed \r
- */ \r
- public void addAsPrototype(int maxId, Context cx, Scriptable scope, \r
- boolean sealed) \r
- {\r
- activateIdMap(maxId);\r
-\r
- setSealFunctionsFlag(sealed);\r
- setFunctionParametrs(cx);\r
- \r
- int constructorId = mapNameToId("constructor");\r
- if (constructorId == 0) {\r
- // It is a bug to call this function without id for constructor \r
- throw new RuntimeException("No id for constructor property");\r
- }\r
-\r
- IdFunction ctor = newIdFunction(constructorId);\r
- ctor.initAsConstructor(scope, this);\r
- fillConstructorProperties(cx, ctor, sealed);\r
- if (sealed) {\r
- ctor.sealObject();\r
- ctor.addPropertyAttribute(READONLY);\r
- }\r
-\r
- setParentScope(ctor);\r
- setPrototype(getObjectPrototype(scope));\r
- cacheIdValue(constructorId, ctor);\r
-\r
- if (sealed) {\r
- sealObject();\r
- }\r
-\r
- defineProperty(scope, getClassName(), ctor, ScriptableObject.DONTENUM);\r
- }\r
-\r
- protected void fillConstructorProperties\r
- (Context cx, IdFunction ctor, boolean sealed)\r
- {\r
- }\r
-\r
- protected void addIdFunctionProperty\r
- (Scriptable obj, int id, boolean sealed)\r
- {\r
- IdFunction f = newIdFunction(id);\r
- if (sealed) { f.sealObject(); }\r
- defineProperty(obj, getIdName(id), f, DONTENUM);\r
- }\r
-\r
- /** \r
- * Utility method for converting target object into native this.\r
- * Possible usage would be to have a private function like realThis:\r
- * <pre>\r
- private NativeSomething realThis(Scriptable thisObj,\r
- IdFunction f, boolean readOnly)\r
- {\r
- while (!(thisObj instanceof NativeSomething)) {\r
- thisObj = nextInstanceCheck(thisObj, f, readOnly);\r
- }\r
- return (NativeSomething)thisObj;\r
- }\r
- * </pre>\r
- * Note that although such function can be implemented universally via\r
- * java.lang.Class.isInstance(), it would be much more slower.\r
- * @param readOnly specify if the function f does not change state of object.\r
- * @return Scriptable object suitable for a check by the instanceof operator.\r
- * @throws RuntimeException if no more instanceof target can be found\r
- */\r
- protected Scriptable nextInstanceCheck(Scriptable thisObj,\r
- IdFunction f,\r
- boolean readOnly)\r
- {\r
- if (readOnly && 0 != (setupFlags & USE_DYNAMIC_SCOPE_FLAG)) {\r
- // for read only functions under dynamic scope look prototype chain\r
- thisObj = thisObj.getPrototype();\r
- if (thisObj != null) { return thisObj; }\r
- }\r
- throw NativeGlobal.typeError1("msg.incompat.call", \r
- f.getFunctionName(), f);\r
- }\r
-\r
- protected IdFunction newIdFunction(int id) {\r
- IdFunction f = new IdFunction(this, getIdName(id), id);\r
- if (0 != (setupFlags & SEAL_FUNCTIONS_FLAG)) { f.sealObject(); }\r
- return f;\r
- }\r
-\r
- protected final Object wrap_double(double x) {\r
- return (x == x) ? new Double(x) : ScriptRuntime.NaNobj;\r
- }\r
-\r
- protected final Object wrap_int(int x) {\r
- byte b = (byte)x;\r
- if (b == x) { return new Byte(b); }\r
- return new Integer(x);\r
- }\r
-\r
- protected final Object wrap_long(long x) {\r
- int i = (int)x;\r
- if (i == x) { return wrap_int(i); }\r
- return new Long(x);\r
- }\r
-\r
- protected final Object wrap_boolean(boolean x) {\r
- return x ? Boolean.TRUE : Boolean.FALSE;\r
- }\r
- \r
- private boolean hasValue(int id) {\r
- Object value;\r
- Object[] data = idMapData;\r
- if (data == null || (value = data[id - 1]) == null) {\r
- return hasIdValue(id);\r
- }\r
- else {\r
- return value != NOT_FOUND;\r
- }\r
- }\r
-\r
- // Must be called only from synchronized (this)\r
- private Object[] ensureIdData() {\r
- Object[] data = idMapData;\r
- if (data == null) { \r
- idMapData = data = new Object[CACHE_NAMES ? maxId * 2 : maxId];\r
- }\r
- return data;\r
- }\r
- \r
- private int getAttributes(int id) {\r
- int attributes = getIdDefaultAttributes(id) | extraIdAttributes;\r
- byte[] array = attributesArray;\r
- if (array != null) {\r
- attributes |= 0xFF & array[id - 1];\r
- }\r
- return attributes;\r
- }\r
-\r
- private void setAttributes(int id, int attributes) {\r
- int defaultAttrs = getIdDefaultAttributes(id);\r
- if ((attributes & defaultAttrs) != defaultAttrs) {\r
- // It is a bug to set attributes to less restrictive values \r
- // then given by defaultAttrs\r
- throw new RuntimeException("Attempt to unset default attributes");\r
- }\r
- // Store only additional bits\r
- attributes &= ~defaultAttrs;\r
- byte[] array = attributesArray;\r
- if (array == null && attributes != 0) {\r
- synchronized (this) {\r
- array = attributesArray;\r
- if (array == null) {\r
- attributesArray = array = new byte[maxId];\r
- }\r
- }\r
- }\r
- if (array != null) {\r
- array[id - 1] = (byte)attributes;\r
- }\r
- }\r
-\r
- private int maxId;\r
- private Object[] idMapData;\r
- private byte[] attributesArray;\r
-\r
- private static final boolean CACHE_NAMES = true;\r
- private int lastIdCache;\r
-\r
- private static final int USE_DYNAMIC_SCOPE_FLAG = 1 << 0;\r
- private static final int SEAL_FUNCTIONS_FLAG = 1 << 1;\r
- \r
- private byte setupFlags;\r
- private byte extraIdAttributes;\r
-}\r
-\r