From 6b4b1e2803b46ca73f1fe3397a4289514118c980 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Mon, 18 Jan 2021 10:26:44 +0100 Subject: [PATCH 01/14] Add generic type support and improve java.util.{List,Map} handling --- examples/PrimitiveWrapFactory.java | 4 +- src/org/mozilla/javascript/Context.java | 2 +- .../javascript/IdScriptableObject.java | 1 + src/org/mozilla/javascript/JavaMembers.java | 35 +++- .../mozilla/javascript/NativeIterator.java | 71 +++++++ .../mozilla/javascript/NativeJavaList.java | 53 ++++-- src/org/mozilla/javascript/NativeJavaMap.java | 121 ++++++++++-- .../mozilla/javascript/NativeJavaMethod.java | 3 +- .../mozilla/javascript/NativeJavaObject.java | 9 +- src/org/mozilla/javascript/ScriptRuntime.java | 49 +++++ src/org/mozilla/javascript/WrapFactory.java | 20 +- .../tests/JavaIterableIteratorTest.java | 174 ++++++++++++++++++ .../javascript/tests/JavaListAccessTest.java | 111 +++++++++++ .../tests/JavaListIteratorTest.java | 111 +++++++++++ .../javascript/tests/JavaMapIteratorTest.java | 157 ++++++++++++++++ .../javascript/tests/NativeJavaMapTest.java | 57 ++++++ 16 files changed, 919 insertions(+), 59 deletions(-) create mode 100644 testsrc/org/mozilla/javascript/tests/JavaIterableIteratorTest.java create mode 100644 testsrc/org/mozilla/javascript/tests/JavaListAccessTest.java create mode 100644 testsrc/org/mozilla/javascript/tests/JavaListIteratorTest.java create mode 100644 testsrc/org/mozilla/javascript/tests/JavaMapIteratorTest.java diff --git a/examples/PrimitiveWrapFactory.java b/examples/PrimitiveWrapFactory.java index f527c4575e..a0093782e1 100644 --- a/examples/PrimitiveWrapFactory.java +++ b/examples/PrimitiveWrapFactory.java @@ -4,6 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import java.lang.reflect.Type; + import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.WrapFactory; @@ -27,7 +29,7 @@ public class PrimitiveWrapFactory extends WrapFactory { @Override public Object wrap(Context cx, Scriptable scope, Object obj, - Class staticType) + Type staticType) { if (obj instanceof String || obj instanceof Number || obj instanceof Boolean) diff --git a/src/org/mozilla/javascript/Context.java b/src/org/mozilla/javascript/Context.java index e52ef855b7..9eec6bcae5 100644 --- a/src/org/mozilla/javascript/Context.java +++ b/src/org/mozilla/javascript/Context.java @@ -1829,7 +1829,7 @@ public static Scriptable toObject(Object value, Scriptable scope, *

* The rest of values will be wrapped as LiveConnect objects * by calling {@link WrapFactory#wrap(Context cx, Scriptable scope, - * Object obj, Class staticType)} as in: + * Object obj, Type staticType)} as in: *

      *    Context cx = Context.getCurrentContext();
      *    return cx.getWrapFactory().wrap(cx, scope, value, null);
diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java
index 6e66dbcfc6..e819ee6ffc 100644
--- a/src/org/mozilla/javascript/IdScriptableObject.java
+++ b/src/org/mozilla/javascript/IdScriptableObject.java
@@ -896,6 +896,7 @@ protected void addIdFunctionProperty(Scriptable obj, Object tag, int id,
      * @return obj casted to the target type
      * @throws EcmaError if the cast failed.
      */
+    @SuppressWarnings("unchecked")
     protected static  T ensureType(Object obj, Class clazz, IdFunctionObject f)
     {
         if (clazz.isInstance(obj)) {
diff --git a/src/org/mozilla/javascript/JavaMembers.java b/src/org/mozilla/javascript/JavaMembers.java
index 48e08472f4..b52967db81 100644
--- a/src/org/mozilla/javascript/JavaMembers.java
+++ b/src/org/mozilla/javascript/JavaMembers.java
@@ -15,6 +15,7 @@
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -85,18 +86,18 @@ Object get(Scriptable scope, String name, Object javaObject,
         }
         Context cx = Context.getContext();
         Object rval;
-        Class type;
+        Type type;
         try {
             if (member instanceof BeanProperty) {
                 BeanProperty bp = (BeanProperty) member;
                 if (bp.getter == null)
                     return Scriptable.NOT_FOUND;
                 rval = bp.getter.invoke(javaObject, Context.emptyArgs);
-                type = bp.getter.method().getReturnType();
+                type = bp.getter.method().getGenericReturnType();
             } else {
                 Field field = (Field) member;
                 rval = field.get(isStatic ? null : javaObject);
-                type = field.getType();
+                type = field.getGenericType();
             }
         } catch (Exception ex) {
             throw Context.throwAsScriptRuntimeEx(ex);
@@ -633,6 +634,30 @@ private void reflect(Scriptable scope,
             ht.putAll(toAdd);
         }
 
+        // if we are a Map or Iterable, we add an iterator in order
+        // that the JavaObject can be used in 'for(key in o)' or
+        // 'for each (value in o)' loops
+        if (Map.class.isAssignableFrom(cl)) {
+            // Add Map iterator
+            members.put(NativeIterator.ITERATOR_PROPERTY_NAME,
+                    NativeIterator.JAVA_MAP_ITERATOR);
+            
+        } else if (Iterable.class.isAssignableFrom(cl)) {
+            // Add Iterable/Collection iterator
+            members.put(NativeIterator.ITERATOR_PROPERTY_NAME,
+                    NativeIterator.JAVA_COLLECTION_ITERATOR);
+            // look for size() method and register as length property
+            Object member = members.get("size");
+            if (member instanceof NativeJavaMethod) {
+                NativeJavaMethod njmGet = (NativeJavaMethod) member;
+                MemberBox sizeMethod = extractGetMethod(njmGet.methods, false);
+                if (sizeMethod != null) {
+                    BeanProperty bp = new BeanProperty(sizeMethod, null, null);
+                    members.put("length", bp);
+                }
+            }
+        }
+
         // Reflect constructors
         Constructor[] constructors = getAccessibleConstructors(includePrivate);
         MemberBox[] ctorMembers = new MemberBox[constructors.length];
@@ -896,10 +921,10 @@ public Object getDefaultValue(Class hint)
         if (hint == ScriptRuntime.FunctionClass)
             return this;
         Object rval;
-        Class type;
+        Type type;
         try {
             rval = field.get(javaObject);
-            type = field.getType();
+            type = field.getGenericType();
         } catch (IllegalAccessException accEx) {
             throw Context.reportRuntimeErrorById(
                 "msg.java.internal.private", field.getName());
diff --git a/src/org/mozilla/javascript/NativeIterator.java b/src/org/mozilla/javascript/NativeIterator.java
index 8156662dd6..e6b5521735 100644
--- a/src/org/mozilla/javascript/NativeIterator.java
+++ b/src/org/mozilla/javascript/NativeIterator.java
@@ -6,7 +6,9 @@
 
 package org.mozilla.javascript;
 
+import java.util.Collection;
 import java.util.Iterator;
+import java.util.Map;
 
 /**
  * This class implements iterator objects. See
@@ -18,6 +20,10 @@ public final class NativeIterator extends IdScriptableObject {
     private static final long serialVersionUID = -4136968203581667681L;
     private static final Object ITERATOR_TAG = "Iterator";
 
+    // Functions are registered as '__iterator__' for Iterables and Maps 
+    public static final BaseFunction JAVA_COLLECTION_ITERATOR = new CollectionIteratorFunction();
+    public static final BaseFunction JAVA_MAP_ITERATOR = new MapIteratorFunction();
+
     static void init(Context cx, ScriptableObject scope, boolean sealed) {
         // Iterator
         NativeIterator iterator = new NativeIterator();
@@ -221,6 +227,71 @@ static private Iterator getJavaIterator(Object obj) {
         return null;
     }
 
+    static class CollectionIteratorFunction extends BaseFunction {
+        @Override
+        public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+                Object[] args) {
+
+            Object wrapped = ((NativeJavaObject) thisObj).javaObject;
+            if (Boolean.TRUE.equals(args[0])) {
+                // key only iterator, we will return an iterator
+                // for the sequence of the collection length.
+                int length = ((Collection) wrapped).size();
+                return cx.getWrapFactory().wrap(cx, scope,
+                        new SequenceIterator(length, scope),
+                        WrappedJavaIterator.class);
+            } else {
+                Iterator iter = ((Iterable) wrapped).iterator();
+                return cx.getWrapFactory().wrap(cx, scope,
+                        new WrappedJavaIterator(iter, scope),
+                        WrappedJavaIterator.class);
+            }
+        }
+    }
+    
+    static public class SequenceIterator
+    {
+        SequenceIterator(int size, Scriptable scope) {
+            this.size = size;
+            this.scope = scope;
+        }
+
+        public Object next() {
+            if (pos >= size) {
+                // Out of values. Throw StopIteration.
+                throw new JavaScriptException(
+                    NativeIterator.getStopIterationObject(scope), null, 0);
+            }
+            return pos++;
+        }
+
+        public Object __iterator__(boolean b) {
+            return this;
+        }
+
+        private int size;
+        private int pos;
+        private Scriptable scope;
+    }
+    
+    static class MapIteratorFunction extends BaseFunction {
+        @Override
+        public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+                Object[] args) {
+
+            Map map = (Map) ((NativeJavaObject) thisObj).javaObject;
+            Iterator iter;
+            if (Boolean.TRUE.equals(args[0])) {
+                iter = map.keySet().iterator();
+            } else {
+                iter = map.values().iterator();
+            }
+            return cx.getWrapFactory().wrap(cx, scope,
+                    new WrappedJavaIterator(iter, scope),
+                    WrappedJavaIterator.class);
+        }
+    }
+    
     static public class WrappedJavaIterator
     {
         WrappedJavaIterator(Iterator iterator, Scriptable scope) {
diff --git a/src/org/mozilla/javascript/NativeJavaList.java b/src/org/mozilla/javascript/NativeJavaList.java
index 849896306a..6f5b5e7bff 100644
--- a/src/org/mozilla/javascript/NativeJavaList.java
+++ b/src/org/mozilla/javascript/NativeJavaList.java
@@ -5,17 +5,34 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mozilla.javascript;
 
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
 import java.util.List;
 
 public class NativeJavaList extends NativeJavaObject {
 
+    private static final long serialVersionUID = 6403865639690547921L;
+
     private List list;
+    
+    private Class valueType;
 
     @SuppressWarnings("unchecked")
-    public NativeJavaList(Scriptable scope, Object list) {
-        super(scope, list, list.getClass());
+    public NativeJavaList(Scriptable scope, Object list, Type staticType) {
+        super(scope, list, staticType);
         assert list instanceof List;
         this.list = (List) list;
+        if (staticType == null) {
+            staticType = list.getClass().getGenericSuperclass();
+        }
+        if (staticType instanceof ParameterizedType) {
+            Type[] types = ((ParameterizedType) staticType).getActualTypeArguments();
+            // types[0] contains the T of 'List'
+            this.valueType = ScriptRuntime.getRawType(types[0]);
+        } else {
+            this.valueType = Object.class;
+        }
     }
 
     @Override
@@ -24,14 +41,6 @@ public String getClassName() {
     }
 
 
-    @Override
-    public boolean has(String name, Scriptable start) {
-        if (name.equals("length")) {
-            return true;
-        }
-        return super.has(name, start);
-    }
-
     @Override
     public boolean has(int index, Scriptable start) {
         if (isWithValidIndex(index)) {
@@ -48,14 +57,6 @@ public boolean has(Symbol key, Scriptable start) {
         return super.has(key, start);
     }
 
-    @Override
-    public Object get(String name, Scriptable start) {
-        if ("length".equals(name)) {
-            return Integer.valueOf(list.size());
-        }
-        return super.get(name, start);
-    }
-
     @Override
     public Object get(int index, Scriptable start) {
         if (isWithValidIndex(index)) {
@@ -76,13 +77,25 @@ public Object get(Symbol key, Scriptable start) {
 
     @Override
     public void put(int index, Scriptable start, Object value) {
-        if (isWithValidIndex(index)) {
-            list.set(index, Context.jsToJava(value, Object.class));
+        if (index >= 0) {
+            ensureCapacity(index + 1);
+            list.set(index, Context.jsToJava(value, valueType));
             return;
         }
         super.put(index, start, value);
     }
 
+    private void ensureCapacity(int minCapacity) {
+        if (minCapacity > list.size()) {
+            if (list instanceof ArrayList) {
+                ((ArrayList) list).ensureCapacity(minCapacity);
+            }
+            while (minCapacity > list.size()) {
+              list.add(null);
+            }
+        }
+    }
+
     @Override
     public Object[] getIds() {
         List list = (List) javaObject;
diff --git a/src/org/mozilla/javascript/NativeJavaMap.java b/src/org/mozilla/javascript/NativeJavaMap.java
index 05cabff03d..624e7e661a 100644
--- a/src/org/mozilla/javascript/NativeJavaMap.java
+++ b/src/org/mozilla/javascript/NativeJavaMap.java
@@ -5,19 +5,36 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mozilla.javascript;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.HashMap;
 import java.util.Map;
 
 public class NativeJavaMap extends NativeJavaObject {
-
+    
+    private static final long serialVersionUID = 46513864372878618L;
+    
     private Map map;
+    private Class keyType;
+    private Class valueType;
+    private transient Map keyTranslationMap;
 
     @SuppressWarnings("unchecked")
-    public NativeJavaMap(Scriptable scope, Object map) {
-        super(scope, map, map.getClass());
+    public NativeJavaMap(Scriptable scope, Object map, Type staticType) {
+        super(scope, map, staticType);
         assert map instanceof Map;
         this.map = (Map) map;
+        if (staticType == null) {
+            staticType = map.getClass().getGenericSuperclass();
+        }
+        if (staticType instanceof ParameterizedType) {
+            Type[] types = ((ParameterizedType) staticType).getActualTypeArguments();
+            this.keyType = ScriptRuntime.getRawType(types[0]);
+            this.valueType = ScriptRuntime.getRawType(types[1]);
+        } else {
+            this.keyType = Object.class;
+            this.valueType = Object.class;
+        }
     }
 
     @Override
@@ -28,7 +45,7 @@ public String getClassName() {
 
     @Override
     public boolean has(String name, Scriptable start) {
-        if (map.containsKey(name)) {
+        if (map.containsKey(toKey(name, false))) {
             return true;
         }
         return super.has(name, start);
@@ -36,7 +53,7 @@ public boolean has(String name, Scriptable start) {
 
     @Override
     public boolean has(int index, Scriptable start) {
-        if (map.containsKey(Integer.valueOf(index))) {
+        if (map.containsKey(toKey(index, false))) {
             return true;
         }
         return super.has(index, start);
@@ -44,9 +61,13 @@ public boolean has(int index, Scriptable start) {
 
     @Override
     public Object get(String name, Scriptable start) {
-        if (map.containsKey(name)) {
+        Object key = toKey(name, false);
+        if (map.containsKey(key)) {
             Context cx = Context.getContext();
-            Object obj = map.get(name);
+            Object obj = map.get(key);
+            if (obj == null) {
+                return null;
+            }
             return cx.getWrapFactory().wrap(cx, this, obj, obj.getClass());
         }
         return super.get(name, start);
@@ -54,34 +75,98 @@ public Object get(String name, Scriptable start) {
 
     @Override
     public Object get(int index, Scriptable start) {
-        if (map.containsKey(Integer.valueOf(index))) {
+      Object key = toKey(Integer.valueOf(index), false);
+        if (map.containsKey(key)) {
             Context cx = Context.getContext();
-            Object obj = map.get(Integer.valueOf(index));
+            Object obj = map.get(key);
+            if (obj == null) {
+                return null;
+            }
             return cx.getWrapFactory().wrap(cx, this, obj, obj.getClass());
         }
         return super.get(index, start);
     }
+    
+    @SuppressWarnings("unchecked")
+    private Object toKey(Object key, boolean translateNew) {
+        if (keyType == String.class || map.containsKey(key)) {
+            // fast exit, if we know, that there are only string keys in the map o
+            return key;
+        }
+        String strKey = ScriptRuntime.toString(key);
+        if (map.containsKey(strKey)) {
+            // second fast exit, if the key is present as string.
+            return strKey;
+        }
+
+        // TODO: There is no change detection yet. The keys in the wrapped map could theoretically
+        // change though other java code. To reduce this risk, we clear the keyTranslationMap on
+        // unwrap. An approach to track if the underlying map was changed may be to read the
+        // 'modCount' property of HashMap, but this is not part of the Map interface.
+        // So for now, wrapped maps must not be changed by external code.
+        if (keyTranslationMap == null) {
+            keyTranslationMap = new HashMap<>();
+            map.keySet().forEach(k -> keyTranslationMap.put(ScriptRuntime.toString(k), k));
+        }
+        Object ret = keyTranslationMap.get(strKey);
+        if (ret == null) {
+            if (translateNew) {
+                // we do not have the key, and we need a new one, (due PUT operation e.g.)
+                if (keyType == Object.class) {
+                    // if we do not know the keyType, just pass through the key
+                    ret = key;
+                } else if (Enum.class.isAssignableFrom(keyType)) {
+                    // for enums use "valueOf" method
+                    ret = Enum.valueOf((Class) keyType, strKey);
+                } else {
+                    // for all other use jsToJava (which might run into a conversionError)
+                    ret = Context.jsToJava(key, keyType);
+                }
+                keyTranslationMap.put(strKey, ret);
+            } else {
+                ret = key;
+            }
+        }
+        return ret;
+    }
+    
+    private Object toValue(Object value) {
+        if (valueType == Object.class) {
+            return value;
+        } else {
+            return Context.jsToJava(value, valueType);
+        }
+    }
 
     @Override
     public void put(String name, Scriptable start, Object value) {
-        map.put(name, Context.jsToJava(value, Object.class));
+        map.put(toKey(name, true), toValue(value));
     }
 
     @Override
     public void put(int index, Scriptable start, Object value) {
-        map.put(Integer.valueOf(index), Context.jsToJava(value, Object.class));
+        map.put(toKey(index, true), toValue(value));
     }
 
+    @Override
+    public Object unwrap() {
+        // clear keyTranslationMap on unwrap, as native java code may modify the object now
+        keyTranslationMap = null;
+        return super.unwrap();
+    }
+    
     @Override
     public Object[] getIds() {
-        List ids = new ArrayList<>(map.size());
+        Object[] ids = new Object[map.size()];
+        int i = 0;
         for (Object key : map.keySet()) {
-            if (key instanceof Integer) {
-                ids.add((Integer)key);
+            if (key instanceof Number) {
+                ids[i++] = (Number)key;
             } else {
-                ids.add(ScriptRuntime.toString(key));
+                ids[i++] = ScriptRuntime.toString(key);
             }
         }
-        return ids.toArray();
+        return ids;
     }
+
 }
diff --git a/src/org/mozilla/javascript/NativeJavaMethod.java b/src/org/mozilla/javascript/NativeJavaMethod.java
index a43577233b..2d839f0cd5 100644
--- a/src/org/mozilla/javascript/NativeJavaMethod.java
+++ b/src/org/mozilla/javascript/NativeJavaMethod.java
@@ -8,6 +8,7 @@
 
 import java.lang.reflect.Array;
 import java.lang.reflect.Method;
+import java.lang.reflect.Type;
 import java.util.Arrays;
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -224,7 +225,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
         }
 
         Object retval = meth.invoke(javaObject, args);
-        Class staticType = meth.method().getReturnType();
+        Type staticType = meth.method().getGenericReturnType();
 
         if (debug) {
             Class actualType = (retval == null) ? null
diff --git a/src/org/mozilla/javascript/NativeJavaObject.java b/src/org/mozilla/javascript/NativeJavaObject.java
index 5157f1c23e..8583cdfd33 100644
--- a/src/org/mozilla/javascript/NativeJavaObject.java
+++ b/src/org/mozilla/javascript/NativeJavaObject.java
@@ -13,6 +13,7 @@
 import java.lang.reflect.Array;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.Type;
 import java.util.Date;
 import java.util.Map;
 
@@ -35,17 +36,17 @@ public class NativeJavaObject
     public NativeJavaObject() { }
 
     public NativeJavaObject(Scriptable scope, Object javaObject,
-                            Class staticType)
+                            Type staticType)
     {
         this(scope, javaObject, staticType, false);
     }
 
     public NativeJavaObject(Scriptable scope, Object javaObject,
-                            Class staticType, boolean isAdapter)
+                            Type staticType, boolean isAdapter)
     {
         this.parent = scope;
         this.javaObject = javaObject;
-        this.staticType = staticType;
+        this.staticType = ScriptRuntime.getRawType(staticType);
         this.isAdapter = isAdapter;
         initMembers();
     }
@@ -190,7 +191,7 @@ public Object[] getIds() {
 
     /**
      * @deprecated Use {@link Context#getWrapFactory()} together with calling {@link
-     * WrapFactory#wrap(Context, Scriptable, Object, Class)}
+     * WrapFactory#wrap(Context, Scriptable, Object, Type)}
      */
     @Deprecated
     public static Object wrap(Scriptable scope, Object obj, Class staticType) {
diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java
index 0976d6fa5f..0d7a2f3f01 100644
--- a/src/org/mozilla/javascript/ScriptRuntime.java
+++ b/src/org/mozilla/javascript/ScriptRuntime.java
@@ -7,7 +7,13 @@
 package org.mozilla.javascript;
 
 import java.io.Serializable;
+import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
 import java.text.MessageFormat;
 import java.util.Arrays;
 import java.util.Locale;
@@ -2900,6 +2906,49 @@ public static String typeofName(Scriptable scope, String id)
         return typeof(getObjectProp(val, id, cx));
     }
 
+    /**
+     * returns the raw type. Taken from google guice.
+     */
+    public static Class getRawType(Type type) {
+        if (type == null) {
+            return null;
+            
+        } else if (type instanceof Class) {
+            // Type is a normal class.
+            return (Class) type;
+
+        } else if (type instanceof ParameterizedType) {
+            ParameterizedType parameterizedType = (ParameterizedType) type;
+
+            // I'm not exactly sure why getRawType() returns Type instead of
+            // Class. Neal isn't either but suspects some pathological case
+            // related to nested classes exists.
+            Type rawType = parameterizedType.getRawType();
+            if (!(rawType instanceof Class)) {
+                throw new IllegalArgumentException();
+            }
+            return (Class) rawType;
+
+        } else if (type instanceof GenericArrayType) {
+            Type componentType = ((GenericArrayType) type)
+                    .getGenericComponentType();
+            return Array.newInstance(getRawType(componentType), 0).getClass();
+
+        } else if (type instanceof TypeVariable
+                || type instanceof WildcardType) {
+            // We could use the variable's bounds, but that won't work if there
+            // are multiple. Having a raw type that's more general than 
+            // necessary is okay.
+            return Object.class;
+
+        } else {
+            String className = type.getClass().getName();
+            throw new IllegalArgumentException("Expected a Class, "
+                    + "ParameterizedType, or GenericArrayType, but <"
+                    + type + "> is of type " + className);
+        }
+    }
+
     public static boolean isObject(Object value)
     {
         if (value == null) {
diff --git a/src/org/mozilla/javascript/WrapFactory.java b/src/org/mozilla/javascript/WrapFactory.java
index 0d08759963..bb1f23f733 100644
--- a/src/org/mozilla/javascript/WrapFactory.java
+++ b/src/org/mozilla/javascript/WrapFactory.java
@@ -8,6 +8,7 @@
 
 package org.mozilla.javascript;
 
+import java.lang.reflect.Type;
 import java.util.List;
 import java.util.Map;
 
@@ -45,14 +46,14 @@ public class WrapFactory
      * @return the wrapped value.
      */
     public Object wrap(Context cx, Scriptable scope,
-                       Object obj, Class staticType)
+                       Object obj, Type staticType)
     {
         if (obj == null || obj == Undefined.instance
             || obj instanceof Scriptable)
         {
             return obj;
         }
-        if (staticType != null && staticType.isPrimitive()) {
+        if (staticType instanceof Class && ((Class)staticType).isPrimitive()) {
             if (staticType == Void.TYPE)
                 return Undefined.instance;
             if (staticType == Character.TYPE)
@@ -103,7 +104,7 @@ public Scriptable wrapNewObject(Context cx, Scriptable scope, Object obj)
      * Wrap Java object as Scriptable instance to allow full access to its
      * methods and fields from JavaScript.
      * 

- * {@link #wrap(Context, Scriptable, Object, Class)} and + * {@link #wrap(Context, Scriptable, Object, Type)} and * {@link #wrapNewObject(Context, Scriptable, Object)} call this method * when they can not convert javaObject to JavaScript primitive * value or JavaScript array. @@ -118,14 +119,15 @@ public Scriptable wrapNewObject(Context cx, Scriptable scope, Object obj) * @return the wrapped value which shall not be null */ public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, - Object javaObject, Class staticType) + Object javaObject, Type staticType) { - if (List.class.isAssignableFrom(javaObject.getClass())) { - return new NativeJavaList(scope, javaObject); - } else if (Map.class.isAssignableFrom(javaObject.getClass())) { - return new NativeJavaMap(scope, javaObject); + if (javaObject instanceof List) { + return new NativeJavaList(scope, javaObject, staticType); + } else if (javaObject instanceof Map) { + return new NativeJavaMap(scope, javaObject, staticType); + } else { + return new NativeJavaObject(scope, javaObject, staticType); } - return new NativeJavaObject(scope, javaObject, staticType); } /** diff --git a/testsrc/org/mozilla/javascript/tests/JavaIterableIteratorTest.java b/testsrc/org/mozilla/javascript/tests/JavaIterableIteratorTest.java new file mode 100644 index 0000000000..28404a8142 --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/JavaIterableIteratorTest.java @@ -0,0 +1,174 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.javascript.tests; + +import java.util.AbstractCollection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.mozilla.javascript.ScriptableObject; +import org.mozilla.javascript.Wrapper; + +import junit.framework.TestCase; + +/* + * This testcase tests the basic access to Java classess implementing Iterable + * (eg. ArrayList) + */ +@RunWith(Parameterized.class) +public class JavaIterableIteratorTest extends TestCase { + + private static final String FOO_BAR_BAZ = "foo,bar,42.5,"; + + @Parameters + public static Collection> data() { + return Arrays.asList(new Iterable[] { + arrayList(), linkedHashSet(), iterable(), collection() + }); + } + + private Iterable iterable; + + public JavaIterableIteratorTest(Iterable iterable) { + this.iterable = iterable; + } + + private static List arrayList() { + List list = new ArrayList<>(); + list.add("foo"); + list.add("bar"); + list.add(42.5); + return list; + } + private static Set linkedHashSet() { + return new LinkedHashSet<>(arrayList()); + } + + private static Iterable iterable() { + return new Iterable() { + + @Override + public Iterator iterator() { + return arrayList().iterator(); + } + }; + } + + private static Collection collection() { + return new AbstractCollection() { + + @Override + public Iterator iterator() { + return arrayList().iterator(); + } + + @Override + public int size() { + return arrayList().size(); + } + }; + } + + + @Test + public void testArrayIterator() { + String js = "var ret = '';\n" + + "var iter = list.iterator();\n" + + "while(iter.hasNext()) ret += iter.next()+',';\n" + + "ret"; + testJavaObjectIterate(js, FOO_BAR_BAZ); + // there is no .iterator() function on the JS side + } + + @Test + public void testArrayForEach() { + String js = "var ret = '';\n" + + "for each(elem in list) ret += elem + ',';\n" + + "ret"; + testJsArrayIterate(js, FOO_BAR_BAZ); + testJavaObjectIterate(js, FOO_BAR_BAZ); + testJavaArrayIterate(js, FOO_BAR_BAZ); + } + + @Test + public void testArrayForKeys() { + String js = "var ret = '';\n" + + "for(elem in list) ret += elem + ',';\n" + + "ret"; + testJsArrayIterate(js, "0,1,2,"); + if (iterable instanceof Collection) { + testJavaObjectIterate(js, "0,1,2,"); + } + testJavaArrayIterate(js, "0,1,2,"); + } + + @Test + public void testArrayForIndex() { + String js = "var ret = '';\n" + + "for(var idx = 0; idx < list.length; idx++) ret += idx + ',';\n" + + "ret"; + testJsArrayIterate(js, "0,1,2,"); + testJavaArrayIterate(js, "0,1,2,"); + if (iterable instanceof Collection) { + testJavaObjectIterate(js, "0,1,2,"); + } + } + + // use NativeJavaArray + private void testJavaArrayIterate(String script, String expected) { + Utils.runWithAllOptimizationLevels(cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + List list = new ArrayList<>(); + iterable.forEach(list::add); + scope.put("list", scope, list.toArray()); + Object o = cx.evaluateString(scope, script, + "testJavaArrayIterate.js", 1, null); + assertEquals(expected, o); + + return null; + }); + } + + // use the java object directly + private void testJavaObjectIterate(String script, String expected) { + Utils.runWithAllOptimizationLevels(cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + scope.put("list", scope, iterable); + Object o = cx.evaluateString(scope, script, + "testJavaListIterate.js", 1, null); + assertEquals(expected, o); + + return null; + }); + + } + + // use nativeArray + private void testJsArrayIterate(String script, String expected) { + Utils.runWithAllOptimizationLevels(cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + List list = new ArrayList<>(); + iterable.forEach(list::add); + scope.put("list", scope, + cx.newArray(scope, list.toArray())); + Object o = cx.evaluateString(scope, script, + "testJsArrayIterate.js", 1, null); + assertEquals(expected, o); + return null; + }); + } + +} diff --git a/testsrc/org/mozilla/javascript/tests/JavaListAccessTest.java b/testsrc/org/mozilla/javascript/tests/JavaListAccessTest.java new file mode 100644 index 0000000000..a9c0b2f062 --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/JavaListAccessTest.java @@ -0,0 +1,111 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.javascript.tests; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import org.junit.Test; +import org.mozilla.javascript.ScriptableObject; +import org.mozilla.javascript.Wrapper; + +import junit.framework.TestCase; + +/* + * This testcase tests the basic access to java List with [] + */ +public class JavaListAccessTest extends TestCase { + + @Test + public void testBeanAccess() { + String js = "bean.integers[0] = 3;\n" + + "bean.doubles[0] = 3;" + + "bean.doubles[0].getClass().getSimpleName() + ' ' " + + "+ bean.integers[0].getClass().getSimpleName()\n"; + testIt(js, "Double Integer"); + } + + @Test + public void testListAccess() { + String js = "intList[0] = 3;\n" + + "dblList[0] = 3;" + + "dblList[0].getClass().getSimpleName() + ' ' " + + "+ intList[0].getClass().getSimpleName()\n"; + testIt(js, "Double Integer"); + } + + @Test + public void testIntListIncrement() { + String js = "intList[0] = 3.5;\n" + + "intList[0]++;\n" + + "intList[0].getClass().getSimpleName() + ' ' + intList[0]\n"; + testIt(js, "Integer 4"); + } + + @Test + public void testDblListIncrement() { + String js = "dblList[0] = 3.5;\n" + + "dblList[0]++;\n" + + "dblList[0].getClass().getSimpleName() + ' ' + dblList[0]\n"; + testIt(js, "Double 4.5"); + } + + + public static class Bean { + public List integers = new ArrayList<>(); + private List doubles = new ArrayList<>(); + + public List getDoubles() { + return doubles; + } + + public List numbers = new ArrayList<>(); + } + + + private List createIntegerList() { + List list = new ArrayList() { + + }; + list.add(42); + list.add(7); + return list; + } + + private List createDoubleList() { + List list = new ArrayList() { + + }; + list.add(42.5); + list.add(7.5); + return list; + } + + private List createNumberList() { + List list = new ArrayList() { + + }; + list.add(42); + list.add(7.5); + return list; + } + + private void testIt(String script, String expected) { + Utils.runWithAllOptimizationLevels(cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + scope.put("intList", scope, createIntegerList()); + scope.put("dblList", scope, createDoubleList()); + scope.put("numList", scope, createNumberList()); + scope.put("bean", scope, new Bean()); + Object o = cx.evaluateString(scope, script, + "testJavaArrayIterate.js", 1, null); + assertEquals(expected, o); + + return null; + }); + + } +} diff --git a/testsrc/org/mozilla/javascript/tests/JavaListIteratorTest.java b/testsrc/org/mozilla/javascript/tests/JavaListIteratorTest.java new file mode 100644 index 0000000000..9a0352efb0 --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/JavaListIteratorTest.java @@ -0,0 +1,111 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.javascript.tests; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import org.junit.Test; +import org.mozilla.javascript.ScriptableObject; +import org.mozilla.javascript.Wrapper; + +import junit.framework.TestCase; + +/* + * This testcase tests the basic access to Java classess implementing Iterable + * (eg. ArrayList) + */ +public class JavaListIteratorTest extends TestCase { + + private static final String FOO_BAR_BAZ = "foo,bar,42.5,"; + + private List createJavaList() { + List list = new ArrayList<>(); + list.add("foo"); + list.add("bar"); + list.add(42.5); + return list; + } + + @Test + public void testArrayIterator() { + String js = "var ret = '';\n" + + "var iter = list.iterator();\n" + + "while(iter.hasNext()) ret += iter.next()+',';\n" + + "ret"; + testJavaListIterate(js, FOO_BAR_BAZ); + // there is no .iterator() function on the JS side + } + + @Test + public void testArrayForEach() { + String js = "var ret = '';\n" + + "for each(elem in list) ret += elem + ',';\n" + + "ret"; + testJsArrayIterate(js, FOO_BAR_BAZ); + testJavaListIterate(js, FOO_BAR_BAZ); + testJavaArrayIterate(js, FOO_BAR_BAZ); + } + + @Test + public void testArrayForKeys() { + String js = "var ret = '';\n" + + "for(elem in list) ret += elem + ',';\n" + + "ret"; + testJsArrayIterate(js, "0,1,2,"); + testJavaListIterate(js, "0,1,2,"); + testJavaArrayIterate(js, "0,1,2,"); + } + + @Test + public void testArrayForIndex() { + String js = "var ret = '';\n" + + "for(var idx = 0; idx < list.length; idx++) ret += idx + ',';\n" + + "ret"; + testJsArrayIterate(js, "0,1,2,"); + testJavaArrayIterate(js, "0,1,2,"); + testJavaListIterate(js, "0,1,2,"); + } + + private void testJavaArrayIterate(String script, String expected) { + Utils.runWithAllOptimizationLevels(cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + scope.put("list", scope, createJavaList().toArray()); + Object o = cx.evaluateString(scope, script, + "testJavaArrayIterate.js", 1, null); + assertEquals(expected, o); + + return null; + }); + } + + private void testJavaListIterate(String script, String expected) { + Utils.runWithAllOptimizationLevels(cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + scope.put("list", scope, createJavaList()); + Object o = cx.evaluateString(scope, script, + "testJavaListIterate.js", 1, null); + assertEquals(expected, o); + + return null; + }); + + } + + private void testJsArrayIterate(String script, String expected) { + Utils.runWithAllOptimizationLevels(cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + + scope.put("list", scope, + cx.newArray(scope, createJavaList().toArray())); + Object o = cx.evaluateString(scope, script, + "testJsArrayIterate.js", 1, null); + assertEquals(expected, o); + return null; + }); + } + +} diff --git a/testsrc/org/mozilla/javascript/tests/JavaMapIteratorTest.java b/testsrc/org/mozilla/javascript/tests/JavaMapIteratorTest.java new file mode 100644 index 0000000000..95f730b991 --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/JavaMapIteratorTest.java @@ -0,0 +1,157 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.javascript.tests; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; + + +/* + * This testcase tests the basic access to Java classess implementing Iterable + * (eg. ArrayList) + */ +@RunWith(Parameterized.class) +public class JavaMapIteratorTest { + + private static final String EXPECTED_VALUES = "7,2,5,"; + private static final String EXPECTED_KEYS = "foo,bar,baz,"; + + @Parameters + public static Collection> data() { + return Arrays.asList(new Map[] { + mapWithEnumKey(), + mapWithStringKey() + }); + } + private static Map mapWithStringKey() { + Map map = new LinkedHashMap<>(); + map.put("foo", 7); + map.put("bar", 2); + map.put("baz", 5); + return map; + } + public enum MyEnum { + foo, bar, baz + } + private static Map mapWithEnumKey() { + Map map = new EnumMap<>(MyEnum.class); + map.put(MyEnum.foo, 7); + map.put(MyEnum.bar, 2); + map.put(MyEnum.baz, 5); + return map; + } + + private Map map; + + public JavaMapIteratorTest(Map map) { + this.map = map; + } + + // iterate over all values with 'for each' + @Test + public void testForEachValue() { + String js = "var ret = '';\n" + + "for each(value in map) ret += value + ',';\n" + + "ret"; + testJsMap(js, EXPECTED_VALUES); + testJavaMap(js, EXPECTED_VALUES); + } + + // iterate over all keys and concatenate them + @Test + public void testForKey() { + String js = "var ret = '';\n" + + "for(key in map) ret += key + ',';\n" + + "ret"; + testJsMap(js, EXPECTED_KEYS); + testJavaMap(js, EXPECTED_KEYS); + } + + // iterate over all keys and try to read the map value + @Test + public void testForKeyWithGet() { + String js = "var ret = '';\n" + + "for(key in map) ret += map[key] + ',';\n" + + "ret"; + testJsMap(js, EXPECTED_VALUES); + testJavaMap(js, EXPECTED_VALUES); + } + + // invoke map.forEach function. + // NOTE: signature of forEach is different + // EcmaScript Map: forEach(value, key, map) + // Java: forEach(key, value) + @Test + public void testMapForEach1() { + String js = "var ret = '';\n" + + "map.forEach(function(key) { ret += key + ',' });\n" + + "ret"; + testJavaMap(js, EXPECTED_KEYS); + } + + @Test + public void testMapForEach2() { + String js = "var ret = '';\n" + + "map.forEach(function(key, value) { ret += value + ',' });\n" + + "ret"; + testJavaMap(js, EXPECTED_VALUES); // forEach(key, value) + } + + @Test + public void testMapForEach3() { + String js = "var ret = '';\n" + + "map.forEach(function(key) { ret += map[key] + ',' });\n" + + "ret"; + testJavaMap(js, EXPECTED_VALUES); + } + + @Test + public void testObjectKeys() { + String js = "Object.keys(map).join(',')+',';\n"; + testJavaMap(js, EXPECTED_KEYS); + testJsMap(js, EXPECTED_KEYS); + } + + private void testJavaMap(String script, Object expected) { + Utils.runWithAllOptimizationLevels(cx -> { + cx.setLanguageVersion(Context.VERSION_ES6); + final ScriptableObject scope = cx.initStandardObjects(); + scope.put("map", scope, map); + Object o = cx.evaluateString(scope, script, + "testJavaMap.js", 1, null); + assertEquals(expected, o); + + return null; + }); + } + + private void testJsMap(String script, Object expected) { + Utils.runWithAllOptimizationLevels(cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + Scriptable obj = cx.newObject(scope); + map.forEach((key,value)->obj.put(String.valueOf(key), obj, value)); + scope.put("map", scope, obj); + Object o = cx.evaluateString(scope, script, + "testJsMap.js", 1, null); + assertEquals(expected, o); + + return null; + }); + } + +} diff --git a/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java b/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java index 67f3690dd3..c4196176b1 100644 --- a/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java +++ b/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java @@ -26,6 +26,10 @@ public class NativeJavaMapTest extends TestCase { public NativeJavaMapTest() { global.init(ContextFactory.getGlobal()); } + + public static enum MyEnum { + A, B, C, X, Y, Z + } public void testAccessingJavaMapIntegerValues() { @@ -37,7 +41,60 @@ public void testAccessingJavaMapIntegerValues() { assertEquals(2, runScriptAsInt("value[1]", map)); assertEquals(3, runScriptAsInt("value[2]", map)); } + public void testAccessingJavaMapLongValues() { + Map map = new HashMap<>(); + map.put(0L, 1); + map.put(1L, 2); + map.put(2L, 3); + + assertEquals(2, runScriptAsInt("value[1]", map)); + assertEquals(3, runScriptAsInt("value[2]", map)); + runScriptAsString("value[4] = 4.01", map); + assertEquals(Double.valueOf(4.01), map.get(4)); + assertEquals(null, map.get(4L)); + } + + public void testAccessingJavaMapEnumValuesWithGeneric() { + // genrate inner class, that contains type information. + Map map = new HashMap() { + private static final long serialVersionUID = 1L; + }; + + map.put(MyEnum.A, 1); + map.put(MyEnum.B, 2); + map.put(MyEnum.C, 3); + + assertEquals(2, runScriptAsInt("value['B']", map)); + assertEquals(3, runScriptAsInt("value['C']", map)); + runScriptAsString("value['X'] = 4.01", map); + // we know the type info and can convert the key to Long and the value is rounded to Integer + assertEquals(Integer.valueOf(4),map.get(MyEnum.X)); + + try { + runScriptAsString("value['D'] = 4.0", map); + fail();; + } catch (IllegalArgumentException ex) { + assertEquals("No enum constant org.mozilla.javascript.tests.NativeJavaMapTest.MyEnum.D", ex.getMessage()); + } + } + public void testAccessingJavaMapLongValuesWithGeneric() { + // genrate inner class, that contains type information. + Map map = new HashMap() { + private static final long serialVersionUID = 1L; + }; + + map.put(0L, 1); + map.put(1L, 2); + map.put(2L, 3); + + assertEquals(2, runScriptAsInt("value[1]", map)); + assertEquals(3, runScriptAsInt("value[2]", map)); + runScriptAsInt("value[4] = 4.0", map); + // we know the type info and can convert the key to Long and the value to Integer + assertEquals(Integer.valueOf(4),map.get(4L)); + assertEquals(null, map.get(4)); + } public void testJavaMethodCalls() { Map map = new HashMap<>(); map.put("a", 1); From 248fc4176096c60b2490feeb784938968a3a3f3a Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 8 Sep 2021 14:36:42 +0200 Subject: [PATCH 02/14] spotlessApply --- examples/PrimitiveWrapFactory.java | 46 +- src/org/mozilla/javascript/Context.java | 2158 ++++++-------- .../javascript/IdScriptableObject.java | 622 ++-- src/org/mozilla/javascript/JavaMembers.java | 430 ++- .../mozilla/javascript/NativeIterator.java | 207 +- .../mozilla/javascript/NativeJavaList.java | 7 +- src/org/mozilla/javascript/NativeJavaMap.java | 16 +- .../mozilla/javascript/NativeJavaMethod.java | 229 +- .../mozilla/javascript/NativeJavaObject.java | 870 +++--- src/org/mozilla/javascript/ScriptRuntime.java | 2579 +++++++---------- src/org/mozilla/javascript/WrapFactory.java | 143 +- .../tests/JavaIterableIteratorTest.java | 139 +- .../javascript/tests/JavaListAccessTest.java | 90 +- .../tests/JavaListIteratorTest.java | 87 +- .../javascript/tests/JavaMapIteratorTest.java | 122 +- .../javascript/tests/NativeJavaMapTest.java | 153 +- 16 files changed, 3462 insertions(+), 4436 deletions(-) diff --git a/examples/PrimitiveWrapFactory.java b/examples/PrimitiveWrapFactory.java index a0093782e1..cfd990d779 100644 --- a/examples/PrimitiveWrapFactory.java +++ b/examples/PrimitiveWrapFactory.java @@ -5,40 +5,32 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import java.lang.reflect.Type; - import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.WrapFactory; /** - * An example WrapFactory that can be used to avoid wrapping of Java types - * that can be converted to ECMA primitive values. - * So java.lang.String is mapped to ECMA string, all java.lang.Numbers are - * mapped to ECMA numbers, and java.lang.Booleans are mapped to ECMA booleans - * instead of being wrapped as objects. Additionally java.lang.Character is - * converted to ECMA string with length 1. + * An example WrapFactory that can be used to avoid wrapping of Java types that can be converted to + * ECMA primitive values. So java.lang.String is mapped to ECMA string, all java.lang.Numbers are + * mapped to ECMA numbers, and java.lang.Booleans are mapped to ECMA booleans instead of being + * wrapped as objects. Additionally java.lang.Character is converted to ECMA string with length 1. * Other types have the default behavior. - *

- * Note that calling "new java.lang.String('foo')" in JavaScript with this - * wrap factory enabled will still produce a wrapped Java object since the - * WrapFactory.wrapNewObject method is not overridden. - *

- * The PrimitiveWrapFactory is enabled on a Context by calling setWrapFactory - * on that context. + * + *

Note that calling "new java.lang.String('foo')" in JavaScript with this wrap factory enabled + * will still produce a wrapped Java object since the WrapFactory.wrapNewObject method is not + * overridden. + * + *

The PrimitiveWrapFactory is enabled on a Context by calling setWrapFactory on that context. */ public class PrimitiveWrapFactory extends WrapFactory { - @Override - public Object wrap(Context cx, Scriptable scope, Object obj, - Type staticType) - { - if (obj instanceof String || obj instanceof Number || - obj instanceof Boolean) - { - return obj; - } else if (obj instanceof Character) { - char[] a = { ((Character)obj).charValue() }; - return new String(a); + @Override + public Object wrap(Context cx, Scriptable scope, Object obj, Type staticType) { + if (obj instanceof String || obj instanceof Number || obj instanceof Boolean) { + return obj; + } else if (obj instanceof Character) { + char[] a = {((Character) obj).charValue()}; + return new String(a); + } + return super.wrap(cx, scope, obj, staticType); } - return super.wrap(cx, scope, obj, staticType); - } } diff --git a/src/org/mozilla/javascript/Context.java b/src/org/mozilla/javascript/Context.java index 9eec6bcae5..d344900667 100644 --- a/src/org/mozilla/javascript/Context.java +++ b/src/org/mozilla/javascript/Context.java @@ -22,7 +22,6 @@ import java.util.Locale; import java.util.Map; import java.util.Set; - import org.mozilla.classfile.ClassFileWriter.ClassFileFormatException; import org.mozilla.javascript.ast.AstRoot; import org.mozilla.javascript.ast.ScriptNode; @@ -33,361 +32,320 @@ /** * This class represents the runtime context of an executing script. * - * Before executing a script, an instance of Context must be created - * and associated with the thread that will be executing the script. - * The Context will be used to store information about the executing - * of the script such as the call stack. Contexts are associated with - * the current thread using the {@link #call(ContextAction)} - * or {@link #enter()} methods.

+ *

Before executing a script, an instance of Context must be created and associated with the + * thread that will be executing the script. The Context will be used to store information about the + * executing of the script such as the call stack. Contexts are associated with the current thread + * using the {@link #call(ContextAction)} or {@link #enter()} methods. * - * Different forms of script execution are supported. Scripts may be - * evaluated from the source directly, or first compiled and then later - * executed. Interactive execution is also supported.

+ *

Different forms of script execution are supported. Scripts may be evaluated from the source + * directly, or first compiled and then later executed. Interactive execution is also supported. * - * Some aspects of script execution, such as type conversions and - * object creation, may be accessed directly through methods of - * Context. + *

Some aspects of script execution, such as type conversions and object creation, may be + * accessed directly through methods of Context. * * @see Scriptable * @author Norris Boyd * @author Brendan Eich */ - -public class Context -{ +public class Context { /** * Language versions. * - * All integral values are reserved for future version numbers. + *

All integral values are reserved for future version numbers. */ /** * The unknown version. - *

Be aware, this version will not support many of the newer - * language features and will not change in the future.

- *

Please use one of the other constants like VERSION_ES6 to - * get support for recent language features.

+ * + *

Be aware, this version will not support many of the newer language features and will not + * change in the future. + * + *

Please use one of the other constants like VERSION_ES6 to get support for recent language + * features. */ - public static final int VERSION_UNKNOWN = -1; + public static final int VERSION_UNKNOWN = -1; - /** - * The default version. - */ - public static final int VERSION_DEFAULT = 0; + /** The default version. */ + public static final int VERSION_DEFAULT = 0; - /** - * JavaScript 1.0 - */ - public static final int VERSION_1_0 = 100; + /** JavaScript 1.0 */ + public static final int VERSION_1_0 = 100; - /** - * JavaScript 1.1 - */ - public static final int VERSION_1_1 = 110; + /** JavaScript 1.1 */ + public static final int VERSION_1_1 = 110; - /** - * JavaScript 1.2 - */ - public static final int VERSION_1_2 = 120; + /** JavaScript 1.2 */ + public static final int VERSION_1_2 = 120; - /** - * JavaScript 1.3 - */ - public static final int VERSION_1_3 = 130; + /** JavaScript 1.3 */ + public static final int VERSION_1_3 = 130; - /** - * JavaScript 1.4 - */ - public static final int VERSION_1_4 = 140; + /** JavaScript 1.4 */ + public static final int VERSION_1_4 = 140; - /** - * JavaScript 1.5 - */ - public static final int VERSION_1_5 = 150; + /** JavaScript 1.5 */ + public static final int VERSION_1_5 = 150; - /** - * JavaScript 1.6 - */ - public static final int VERSION_1_6 = 160; + /** JavaScript 1.6 */ + public static final int VERSION_1_6 = 160; - /** - * JavaScript 1.7 - */ - public static final int VERSION_1_7 = 170; + /** JavaScript 1.7 */ + public static final int VERSION_1_7 = 170; - /** - * JavaScript 1.8 - */ - public static final int VERSION_1_8 = 180; + /** JavaScript 1.8 */ + public static final int VERSION_1_8 = 180; - /** - * ECMAScript 6. - */ - public static final int VERSION_ES6 = 200; + /** ECMAScript 6. */ + public static final int VERSION_ES6 = 200; /** - * Controls behaviour of Date.prototype.getYear(). - * If hasFeature(FEATURE_NON_ECMA_GET_YEAR) returns true, - * Date.prototype.getYear subtructs 1900 only if 1900 <= date < 2000. - * The default behavior of {@link #hasFeature(int)} is always to subtruct - * 1900 as rquired by ECMAScript B.2.4. + * Controls behaviour of Date.prototype.getYear(). If + * hasFeature(FEATURE_NON_ECMA_GET_YEAR) returns true, Date.prototype.getYear subtructs + * 1900 only if 1900 <= date < 2000. The default behavior of {@link #hasFeature(int)} is + * always to subtruct 1900 as rquired by ECMAScript B.2.4. */ public static final int FEATURE_NON_ECMA_GET_YEAR = 1; /** - * Control if member expression as function name extension is available. - * If hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME) returns - * true, allow function memberExpression(args) { body } to be - * syntax sugar for memberExpression = function(args) { body }, - * when memberExpression is not a simple identifier. - * See ECMAScript-262, section 11.2 for definition of memberExpression. - * By default {@link #hasFeature(int)} returns false. + * Control if member expression as function name extension is available. If + * hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME) returns true, allow + * function memberExpression(args) { body } to be syntax sugar for + * memberExpression = function(args) { body }, when memberExpression is not a simple + * identifier. See ECMAScript-262, section 11.2 for definition of memberExpression. By default + * {@link #hasFeature(int)} returns false. */ public static final int FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME = 2; /** - * Control if reserved keywords are treated as identifiers. - * If hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER) returns true, - * treat future reserved keyword (see Ecma-262, section 7.5.3) as ordinary - * identifiers but warn about this usage. + * Control if reserved keywords are treated as identifiers. If + * hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER) returns true, treat future reserved keyword + * (see Ecma-262, section 7.5.3) as ordinary identifiers but warn about this usage. * - * By default {@link #hasFeature(int)} returns false. + *

By default {@link #hasFeature(int)} returns false. */ public static final int FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER = 3; /** - * Control if toString() should returns the same result - * as toSource() when applied to objects and arrays. - * If hasFeature(FEATURE_TO_STRING_AS_SOURCE) returns true, - * calling toString() on JS objects gives the same result as - * calling toSource(). That is it returns JS source with code - * to create an object with all enumeratable fields of the original object - * instead of printing [object result of + * Control if toString() should returns the same result as toSource() + * when applied to objects and arrays. If hasFeature(FEATURE_TO_STRING_AS_SOURCE) + * returns true, calling toString() on JS objects gives the same result as calling + * toSource(). That is it returns JS source with code to create an object with all + * enumeratable fields of the original object instead of printing [object result of * {@link Scriptable#getClassName()}]. - *

- * By default {@link #hasFeature(int)} returns true only if - * the current JS version is set to {@link #VERSION_1_2}. + * + *

By default {@link #hasFeature(int)} returns true only if the current JS version is set to + * {@link #VERSION_1_2}. */ public static final int FEATURE_TO_STRING_AS_SOURCE = 4; /** - * Control if properties __proto__ and __parent__ - * are treated specially. - * If hasFeature(FEATURE_PARENT_PROTO_PROPERTIES) returns true, - * treat __parent__ and __proto__ as special properties. - *

- * The properties allow to query and set scope and prototype chains for the - * objects. The special meaning of the properties is available - * only when they are used as the right hand side of the dot operator. - * For example, while x.__proto__ = y changes the prototype - * chain of the object x to point to y, - * x["__proto__"] = y simply assigns a new value to the property - * __proto__ in x even when the feature is on. + * Control if properties __proto__ and __parent__ are treated + * specially. If hasFeature(FEATURE_PARENT_PROTO_PROPERTIES) returns true, treat + * __parent__ and __proto__ as special properties. + * + *

The properties allow to query and set scope and prototype chains for the objects. The + * special meaning of the properties is available only when they are used as the right hand side + * of the dot operator. For example, while x.__proto__ = y changes the prototype + * chain of the object x to point to y, x["__proto__"] = y + * simply assigns a new value to the property __proto__ in x + * even when the feature is on. * - * By default {@link #hasFeature(int)} returns true. + *

By default {@link #hasFeature(int)} returns true. */ public static final int FEATURE_PARENT_PROTO_PROPERTIES = 5; - /** - * @deprecated In previous releases, this name was given to - * FEATURE_PARENT_PROTO_PROPERTIES. - */ - @Deprecated - public static final int FEATURE_PARENT_PROTO_PROPRTIES = 5; + /** @deprecated In previous releases, this name was given to FEATURE_PARENT_PROTO_PROPERTIES. */ + @Deprecated public static final int FEATURE_PARENT_PROTO_PROPRTIES = 5; /** - * Control if support for E4X(ECMAScript for XML) extension is available. - * If hasFeature(FEATURE_E4X) returns true, the XML syntax is available. - *

- * By default {@link #hasFeature(int)} returns true if - * the current JS version is set to {@link #VERSION_DEFAULT} - * or is at least {@link #VERSION_1_6}. + * Control if support for E4X(ECMAScript for XML) extension is available. If + * hasFeature(FEATURE_E4X) returns true, the XML syntax is available. + * + *

By default {@link #hasFeature(int)} returns true if the current JS version is set to + * {@link #VERSION_DEFAULT} or is at least {@link #VERSION_1_6}. + * * @since 1.6 Release 1 */ public static final int FEATURE_E4X = 6; /** - * Control if dynamic scope should be used for name access. - * If hasFeature(FEATURE_DYNAMIC_SCOPE) returns true, then the name lookup - * during name resolution will use the top scope of the script or function - * which is at the top of JS execution stack instead of the top scope of the - * script or function from the current stack frame if the top scope of - * the top stack frame contains the top scope of the current stack frame - * on its prototype chain. - *

- * This is useful to define shared scope containing functions that can - * be called from scripts and functions using private scopes. - *

- * By default {@link #hasFeature(int)} returns false. + * Control if dynamic scope should be used for name access. If hasFeature(FEATURE_DYNAMIC_SCOPE) + * returns true, then the name lookup during name resolution will use the top scope of the + * script or function which is at the top of JS execution stack instead of the top scope of the + * script or function from the current stack frame if the top scope of the top stack frame + * contains the top scope of the current stack frame on its prototype chain. + * + *

This is useful to define shared scope containing functions that can be called from scripts + * and functions using private scopes. + * + *

By default {@link #hasFeature(int)} returns false. + * * @since 1.6 Release 1 */ public static final int FEATURE_DYNAMIC_SCOPE = 7; /** - * Control if strict variable mode is enabled. - * When the feature is on Rhino reports runtime errors if assignment - * to a global variable that does not exist is executed. When the feature - * is off such assignments create a new variable in the global scope as - * required by ECMA 262. - *

- * By default {@link #hasFeature(int)} returns false. + * Control if strict variable mode is enabled. When the feature is on Rhino reports runtime + * errors if assignment to a global variable that does not exist is executed. When the feature + * is off such assignments create a new variable in the global scope as required by ECMA 262. + * + *

By default {@link #hasFeature(int)} returns false. + * * @since 1.6 Release 1 */ public static final int FEATURE_STRICT_VARS = 8; /** - * Control if strict eval mode is enabled. - * When the feature is on Rhino reports runtime errors if non-string - * argument is passed to the eval function. When the feature is off - * eval simply return non-string argument as is without performing any - * evaluation as required by ECMA 262. - *

- * By default {@link #hasFeature(int)} returns false. + * Control if strict eval mode is enabled. When the feature is on Rhino reports runtime errors + * if non-string argument is passed to the eval function. When the feature is off eval simply + * return non-string argument as is without performing any evaluation as required by ECMA 262. + * + *

By default {@link #hasFeature(int)} returns false. + * * @since 1.6 Release 1 */ public static final int FEATURE_STRICT_EVAL = 9; /** - * When the feature is on Rhino will add a "fileName" and "lineNumber" - * properties to Error objects automatically. When the feature is off, you - * have to explicitly pass them as the second and third argument to the - * Error constructor. Note that neither behavior is fully ECMA 262 - * compliant (as 262 doesn't specify a three-arg constructor), but keeping - * the feature off results in Error objects that don't have - * additional non-ECMA properties when constructed using the ECMA-defined - * single-arg constructor and is thus desirable if a stricter ECMA - * compliance is desired, specifically adherence to the point 15.11.5. of - * the standard. - *

- * By default {@link #hasFeature(int)} returns false. + * When the feature is on Rhino will add a "fileName" and "lineNumber" properties to Error + * objects automatically. When the feature is off, you have to explicitly pass them as the + * second and third argument to the Error constructor. Note that neither behavior is fully ECMA + * 262 compliant (as 262 doesn't specify a three-arg constructor), but keeping the feature off + * results in Error objects that don't have additional non-ECMA properties when constructed + * using the ECMA-defined single-arg constructor and is thus desirable if a stricter ECMA + * compliance is desired, specifically adherence to the point 15.11.5. of the standard. + * + *

By default {@link #hasFeature(int)} returns false. + * * @since 1.6 Release 6 */ public static final int FEATURE_LOCATION_INFORMATION_IN_ERROR = 10; /** - * Controls whether JS 1.5 'strict mode' is enabled. - * When the feature is on, Rhino reports more than a dozen different - * warnings. When the feature is off, these warnings are not generated. + * Controls whether JS 1.5 'strict mode' is enabled. When the feature is on, Rhino reports more + * than a dozen different warnings. When the feature is off, these warnings are not generated. * FEATURE_STRICT_MODE implies FEATURE_STRICT_VARS and FEATURE_STRICT_EVAL. - *

- * By default {@link #hasFeature(int)} returns false. + * + *

By default {@link #hasFeature(int)} returns false. + * * @since 1.6 Release 6 */ public static final int FEATURE_STRICT_MODE = 11; /** * Controls whether a warning should be treated as an error. + * * @since 1.6 Release 6 */ public static final int FEATURE_WARNING_AS_ERROR = 12; /** - * Enables enhanced access to Java. - * Specifically, controls whether private and protected members can be - * accessed, and whether scripts can catch all Java exceptions. - *

- * Note that this feature should only be enabled for trusted scripts. - *

- * By default {@link #hasFeature(int)} returns false. + * Enables enhanced access to Java. Specifically, controls whether private and protected members + * can be accessed, and whether scripts can catch all Java exceptions. + * + *

Note that this feature should only be enabled for trusted scripts. + * + *

By default {@link #hasFeature(int)} returns false. + * * @since 1.7 Release 1 */ public static final int FEATURE_ENHANCED_JAVA_ACCESS = 13; /** - * Enables access to JavaScript features from ECMAscript 6 that are present in - * JavaScript engines that do not yet support version 6, such as V8. - * This includes support for typed arrays. Default is true. + * Enables access to JavaScript features from ECMAscript 6 that are present in JavaScript + * engines that do not yet support version 6, such as V8. This includes support for typed + * arrays. Default is true. + * * @since 1.7 Release 3 */ public static final int FEATURE_V8_EXTENSIONS = 14; /** - * Defines how an undefined "this" parameter is handled in certain calls. Previously Rhino - * would convert an undefined "this" to null, whereas recent specs call for it to be treated + * Defines how an undefined "this" parameter is handled in certain calls. Previously Rhino would + * convert an undefined "this" to null, whereas recent specs call for it to be treated * differently. Default is to be set if language version <= 1.7. + * * @since 1.7.7 */ public static final int FEATURE_OLD_UNDEF_NULL_THIS = 15; /** - * If set, then the order of property key enumeration will be first numeric keys in numeric order, - * followed by string keys in order of creation, and finally Symbol keys, as specified in ES6. - * Default is true for language version >= "ES6" and false otherwise. + * If set, then the order of property key enumeration will be first numeric keys in numeric + * order, followed by string keys in order of creation, and finally Symbol keys, as specified in + * ES6. Default is true for language version >= "ES6" and false otherwise. + * * @since 1.7.7.1 */ public static final int FEATURE_ENUMERATE_IDS_FIRST = 16; /** * If set, then all objects will have a thread-safe property map. (Note that this doesn't make - * everything else that they do thread-safe -- that depends on the specific implementation. - * If not set, users should not share Rhino objects between threads, unless the "sync" - * function is used to wrap them with an explicit synchronizer. The default - * is false, which means that by default, individual objects are not thread-safe. + * everything else that they do thread-safe -- that depends on the specific implementation. If + * not set, users should not share Rhino objects between threads, unless the "sync" function is + * used to wrap them with an explicit synchronizer. The default is false, which means that by + * default, individual objects are not thread-safe. + * * @since 1.7.8 */ public static final int FEATURE_THREAD_SAFE_OBJECTS = 17; /** - * If set, then all integer numbers will be returned without decimal place. For instance - * assume there is a function like this: - * function foo() {return 5;} - * 5 will be returned if feature is set, 5.0 otherwise. + * If set, then all integer numbers will be returned without decimal place. For instance assume + * there is a function like this: function foo() {return 5;} 5 will be returned if + * feature is set, 5.0 otherwise. */ public static final int FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE = 18; /** - * TypedArray buffer uses little/big endian depending on the platform. - * The default is big endian for Rhino. + * TypedArray buffer uses little/big endian depending on the platform. The default is big endian + * for Rhino. + * * @since 1.7 Release 11 */ public static final int FEATURE_LITTLE_ENDIAN = 19; /** - * Configure the XMLProcessor to parse XML with security features or not. - * Security features include not fetching remote entity references and disabling XIncludes + * Configure the XMLProcessor to parse XML with security features or not. Security features + * include not fetching remote entity references and disabling XIncludes + * * @since 1.7 Release 12 */ public static final int FEATURE_ENABLE_XML_SECURE_PARSING = 20; public static final String languageVersionProperty = "language version"; - public static final String errorReporterProperty = "error reporter"; + public static final String errorReporterProperty = "error reporter"; - /** - * Convenient value to use as zero-length array of objects. - */ + /** Convenient value to use as zero-length array of objects. */ public static final Object[] emptyArgs = ScriptRuntime.emptyArgs; /** * Creates a new Context. The context will be associated with the {@link * ContextFactory#getGlobal() global context factory}. * - * Note that the Context must be associated with a thread before - * it can be used to execute a script. - * @deprecated this constructor is deprecated because it creates a - * dependency on a static singleton context factory. Use - * {@link ContextFactory#enter()} or - * {@link ContextFactory#call(ContextAction)} instead. If you subclass - * this class, consider using {@link #Context(ContextFactory)} constructor - * instead in the subclasses' constructors. + *

Note that the Context must be associated with a thread before it can be used to execute a + * script. + * + * @deprecated this constructor is deprecated because it creates a dependency on a static + * singleton context factory. Use {@link ContextFactory#enter()} or {@link + * ContextFactory#call(ContextAction)} instead. If you subclass this class, consider using + * {@link #Context(ContextFactory)} constructor instead in the subclasses' constructors. */ @Deprecated - public Context() - { + public Context() { this(ContextFactory.getGlobal()); } /** - * Creates a new context. Provided as a preferred super constructor for - * subclasses in place of the deprecated default public constructor. - * @param factory the context factory associated with this context (most - * likely, the one that created the context). Can not be null. The context - * features are inherited from the factory, and the context will also - * otherwise use its factory's services. + * Creates a new context. Provided as a preferred super constructor for subclasses in place of + * the deprecated default public constructor. + * + * @param factory the context factory associated with this context (most likely, the one that + * created the context). Can not be null. The context features are inherited from the + * factory, and the context will also otherwise use its factory's services. * @throws IllegalArgumentException if factory parameter is null. */ - protected Context(ContextFactory factory) - { - if(factory == null) { + protected Context(ContextFactory factory) { + if (factory == null) { throw new IllegalArgumentException("factory == null"); } this.factory = factory; @@ -399,57 +357,53 @@ protected Context(ContextFactory factory) /** * Get the current Context. * - * The current Context is per-thread; this method looks up - * the Context associated with the current thread.

+ *

The current Context is per-thread; this method looks up the Context associated with the + * current thread. * - * @return the Context associated with the current thread, or - * null if no context is associated with the current - * thread. + *

+ * + * @return the Context associated with the current thread, or null if no context is associated + * with the current thread. * @see ContextFactory#enterContext() * @see ContextFactory#call(ContextAction) */ - public static Context getCurrentContext() - { + public static Context getCurrentContext() { Object helper = VMBridge.instance.getThreadContextHelper(); return VMBridge.instance.getContext(helper); } /** - * Same as calling {@link ContextFactory#enterContext()} on the global - * ContextFactory instance. + * Same as calling {@link ContextFactory#enterContext()} on the global ContextFactory instance. + * * @return a Context associated with the current thread * @see #getCurrentContext() * @see #exit() * @see #call(ContextAction) */ - public static Context enter() - { + public static Context enter() { return enter(null, ContextFactory.getGlobal()); } /** - * Get a Context associated with the current thread, using - * the given Context if need be. - *

- * The same as enter() except that cx - * is associated with the current thread and returned if - * the current thread has no associated context and cx - * is not associated with any other thread. + * Get a Context associated with the current thread, using the given Context if need be. + * + *

The same as enter() except that cx is associated with the + * current thread and returned if the current thread has no associated context and cx + * is not associated with any other thread. + * * @param cx a Context to associate with the thread if possible * @return a Context associated with the current thread - * @deprecated use {@link ContextFactory#enterContext(Context)} instead as - * this method relies on usage of a static singleton "global" ContextFactory. + * @deprecated use {@link ContextFactory#enterContext(Context)} instead as this method relies on + * usage of a static singleton "global" ContextFactory. * @see ContextFactory#enterContext(Context) * @see ContextFactory#call(ContextAction) */ @Deprecated - public static Context enter(Context cx) - { + public static Context enter(Context cx) { return enter(cx, ContextFactory.getGlobal()); } - static final Context enter(Context cx, ContextFactory factory) - { + static final Context enter(Context cx, ContextFactory factory) { Object helper = VMBridge.instance.getThreadContextHelper(); Context old = VMBridge.instance.getContext(helper); if (old != null) { @@ -458,7 +412,8 @@ static final Context enter(Context cx, ContextFactory factory) if (cx == null) { cx = factory.makeContext(); if (cx.enterCount != 0) { - throw new IllegalStateException("factory.makeContext() returned Context instance already associated with some thread"); + throw new IllegalStateException( + "factory.makeContext() returned Context instance already associated with some thread"); } factory.onContextCreated(cx); if (factory.isSealed() && !cx.isSealed()) { @@ -466,33 +421,31 @@ static final Context enter(Context cx, ContextFactory factory) } } else { if (cx.enterCount != 0) { - throw new IllegalStateException("can not use Context instance already associated with some thread"); + throw new IllegalStateException( + "can not use Context instance already associated with some thread"); } } VMBridge.instance.setContext(helper, cx); } ++cx.enterCount; return cx; - } + } /** * Exit a block of code requiring a Context. * - * Calling exit() will remove the association between - * the current thread and a Context if the prior call to - * {@link ContextFactory#enterContext()} on this thread newly associated a - * Context with this thread. Once the current thread no longer has an - * associated Context, it cannot be used to execute JavaScript until it is - * again associated with a Context. + *

Calling exit() will remove the association between the current thread and a + * Context if the prior call to {@link ContextFactory#enterContext()} on this thread newly + * associated a Context with this thread. Once the current thread no longer has an associated + * Context, it cannot be used to execute JavaScript until it is again associated with a Context. + * * @see ContextFactory#enterContext() */ - public static void exit() - { + public static void exit() { Object helper = VMBridge.instance.getThreadContextHelper(); Context cx = VMBridge.instance.getContext(helper); if (cx == null) { - throw new IllegalStateException( - "Calling Context.exit without previous Context.enter"); + throw new IllegalStateException("Calling Context.exit without previous Context.enter"); } if (cx.enterCount < 1) Kit.codeBug(); if (--cx.enterCount == 0) { @@ -502,58 +455,52 @@ public static void exit() } /** - * Call {@link ContextAction#run(Context cx)} - * using the Context instance associated with the current thread. - * If no Context is associated with the thread, then - * ContextFactory.getGlobal().makeContext() will be called to - * construct new Context instance. The instance will be temporary - * associated with the thread during call to - * {@link ContextAction#run(Context)}. - * @deprecated use {@link ContextFactory#call(ContextAction)} instead as - * this method relies on usage of a static singleton "global" - * ContextFactory. + * Call {@link ContextAction#run(Context cx)} using the Context instance associated with the + * current thread. If no Context is associated with the thread, then + * ContextFactory.getGlobal().makeContext() will be called to construct new Context + * instance. The instance will be temporary associated with the thread during call to {@link + * ContextAction#run(Context)}. + * + * @deprecated use {@link ContextFactory#call(ContextAction)} instead as this method relies on + * usage of a static singleton "global" ContextFactory. * @return The result of {@link ContextAction#run(Context)}. */ @Deprecated - public static T call(ContextAction action) - { + public static T call(ContextAction action) { return call(ContextFactory.getGlobal(), action); } /** - * Call {@link - * Callable#call(Context cx, Scriptable scope, Scriptable thisObj, - * Object[] args)} - * using the Context instance associated with the current thread. - * If no Context is associated with the thread, then - * {@link ContextFactory#makeContext()} will be called to construct - * new Context instance. The instance will be temporary associated - * with the thread during call to {@link ContextAction#run(Context)}. - *

- * It is allowed but not advisable to use null for factory - * argument in which case the global static singleton ContextFactory - * instance will be used to create new context instances. + * Call {@link Callable#call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args)} + * using the Context instance associated with the current thread. If no Context is associated + * with the thread, then {@link ContextFactory#makeContext()} will be called to construct new + * Context instance. The instance will be temporary associated with the thread during call to + * {@link ContextAction#run(Context)}. + * + *

It is allowed but not advisable to use null for factory argument in which + * case the global static singleton ContextFactory instance will be used to create new context + * instances. + * * @see ContextFactory#call(ContextAction) */ - public static Object call(ContextFactory factory, final Callable callable, - final Scriptable scope, final Scriptable thisObj, - final Object[] args) - { - if(factory == null) { + public static Object call( + ContextFactory factory, + final Callable callable, + final Scriptable scope, + final Scriptable thisObj, + final Object[] args) { + if (factory == null) { factory = ContextFactory.getGlobal(); } return call(factory, cx -> callable.call(cx, scope, thisObj, args)); } - /** - * The method implements {@link ContextFactory#call(ContextAction)} logic. - */ + /** The method implements {@link ContextFactory#call(ContextAction)} logic. */ static T call(ContextFactory factory, ContextAction action) { Context cx = enter(null, factory); try { return action.run(cx); - } - finally { + } finally { exit(); } } @@ -564,16 +511,14 @@ static T call(ContextFactory factory, ContextAction action) { * @see ContextFactory#getGlobal() */ @Deprecated - public static void addContextListener(ContextListener listener) - { + public static void addContextListener(ContextListener listener) { // Special workaround for the debugger String DBG = "org.mozilla.javascript.tools.debugger.Main"; if (DBG.equals(listener.getClass().getName())) { Class cl = listener.getClass(); - Class factoryClass = Kit.classOrNull( - "org.mozilla.javascript.ContextFactory"); - Class[] sig = { factoryClass }; - Object[] args = { ContextFactory.getGlobal() }; + Class factoryClass = Kit.classOrNull("org.mozilla.javascript.ContextFactory"); + Class[] sig = {factoryClass}; + Object[] args = {ContextFactory.getGlobal()}; try { Method m = cl.getMethod("attachTo", sig); m.invoke(listener, args); @@ -592,60 +537,50 @@ public static void addContextListener(ContextListener listener) * @see ContextFactory#getGlobal() */ @Deprecated - public static void removeContextListener(ContextListener listener) - { + public static void removeContextListener(ContextListener listener) { ContextFactory.getGlobal().addListener(listener); } - /** - * Return {@link ContextFactory} instance used to create this Context. - */ - public final ContextFactory getFactory() - { + /** Return {@link ContextFactory} instance used to create this Context. */ + public final ContextFactory getFactory() { return factory; } /** - * Checks if this is a sealed Context. A sealed Context instance does not - * allow to modify any of its properties and will throw an exception - * on any such attempt. + * Checks if this is a sealed Context. A sealed Context instance does not allow to modify any of + * its properties and will throw an exception on any such attempt. + * * @see #seal(Object sealKey) */ - public final boolean isSealed() - { + public final boolean isSealed() { return sealed; } /** - * Seal this Context object so any attempt to modify any of its properties - * including calling {@link #enter()} and {@link #exit()} methods will - * throw an exception. - *

- * If sealKey is not null, calling - * {@link #unseal(Object sealKey)} with the same key unseals - * the object. If sealKey is null, unsealing is no longer possible. + * Seal this Context object so any attempt to modify any of its properties including calling + * {@link #enter()} and {@link #exit()} methods will throw an exception. + * + *

If sealKey is not null, calling {@link #unseal(Object sealKey)} with the same + * key unseals the object. If sealKey is null, unsealing is no longer possible. * * @see #isSealed() * @see #unseal(Object) */ - public final void seal(Object sealKey) - { + public final void seal(Object sealKey) { if (sealed) onSealedMutation(); sealed = true; this.sealKey = sealKey; } /** - * Unseal previously sealed Context object. - * The sealKey argument should not be null and should match - * sealKey suplied with the last call to - * {@link #seal(Object)} or an exception will be thrown. + * Unseal previously sealed Context object. The sealKey argument should not be null + * and should match sealKey suplied with the last call to {@link #seal(Object)} or + * an exception will be thrown. * * @see #isSealed() * @see #seal(Object sealKey) */ - public final void unseal(Object sealKey) - { + public final void unseal(Object sealKey) { if (sealKey == null) throw new IllegalArgumentException(); if (this.sealKey != sealKey) throw new IllegalArgumentException(); if (!sealed) throw new IllegalStateException(); @@ -653,49 +588,45 @@ public final void unseal(Object sealKey) this.sealKey = null; } - static void onSealedMutation() - { + static void onSealedMutation() { throw new IllegalStateException(); } /** * Get the current language version. - *

- * The language version number affects JavaScript semantics as detailed - * in the overview documentation. + * + *

The language version number affects JavaScript semantics as detailed in the overview + * documentation. * * @return an integer that is one of VERSION_1_0, VERSION_1_1, etc. */ - public final int getLanguageVersion() - { - return version; + public final int getLanguageVersion() { + return version; } /** * Set the language version. * - *

- * Setting the language version will affect functions and scripts compiled - * subsequently. See the overview documentation for version-specific - * behavior. + *

Setting the language version will affect functions and scripts compiled subsequently. See + * the overview documentation for version-specific behavior. * * @param version the version as specified by VERSION_1_0, VERSION_1_1, etc. */ - public void setLanguageVersion(int version) - { + public void setLanguageVersion(int version) { if (sealed) onSealedMutation(); checkLanguageVersion(version); Object listeners = propertyListeners; if (listeners != null && version != this.version) { - firePropertyChangeImpl(listeners, languageVersionProperty, - Integer.valueOf(this.version), - Integer.valueOf(version)); + firePropertyChangeImpl( + listeners, + languageVersionProperty, + Integer.valueOf(this.version), + Integer.valueOf(version)); } this.version = version; } - public static boolean isValidLanguageVersion(int version) - { + public static boolean isValidLanguageVersion(int version) { switch (version) { case VERSION_DEFAULT: case VERSION_1_0: @@ -713,29 +644,27 @@ public static boolean isValidLanguageVersion(int version) return false; } - public static void checkLanguageVersion(int version) - { + public static void checkLanguageVersion(int version) { if (isValidLanguageVersion(version)) { return; } - throw new IllegalArgumentException("Bad language version: "+version); + throw new IllegalArgumentException("Bad language version: " + version); } /** * Get the implementation version. * - *

- * The implementation version is of the form + *

The implementation version is of the form + * *

      *    "name langVer release relNum date"
      * 
- * where name is the name of the product, langVer is - * the language version, relNum is the release number, and - * date is the release date for that specific + * + * where name is the name of the product, langVer is the language version, + * relNum is the release number, and date is the release date for that specific * release in the form "yyyy mm dd". * - * @return a string that encodes the product, language version, release - * number, and date. + * @return a string that encodes the product, language version, release number, and date. */ public final String getImplementationVersion() { return ImplementationVersion.get(); @@ -746,8 +675,7 @@ public final String getImplementationVersion() { * * @see org.mozilla.javascript.ErrorReporter */ - public final ErrorReporter getErrorReporter() - { + public final ErrorReporter getErrorReporter() { if (errorReporter == null) { return DefaultErrorReporter.instance; } @@ -760,8 +688,7 @@ public final ErrorReporter getErrorReporter() * @return the previous error reporter * @see org.mozilla.javascript.ErrorReporter */ - public final ErrorReporter setErrorReporter(ErrorReporter reporter) - { + public final ErrorReporter setErrorReporter(ErrorReporter reporter) { if (sealed) onSealedMutation(); if (reporter == null) throw new IllegalArgumentException(); ErrorReporter old = getErrorReporter(); @@ -770,24 +697,21 @@ public final ErrorReporter setErrorReporter(ErrorReporter reporter) } Object listeners = propertyListeners; if (listeners != null) { - firePropertyChangeImpl(listeners, errorReporterProperty, - old, reporter); + firePropertyChangeImpl( + listeners, errorReporterProperty, + old, reporter); } this.errorReporter = reporter; return old; } /** - * Get the current locale. Returns the default locale if none has - * been set. + * Get the current locale. Returns the default locale if none has been set. * * @see java.util.Locale */ - - public final Locale getLocale() - { - if (locale == null) - locale = Locale.getDefault(); + public final Locale getLocale() { + if (locale == null) locale = Locale.getDefault(); return locale; } @@ -796,8 +720,7 @@ public final Locale getLocale() * * @see java.util.Locale */ - public final Locale setLocale(Locale loc) - { + public final Locale setLocale(Locale loc) { if (sealed) onSealedMutation(); Locale result = locale; locale = loc; @@ -805,61 +728,56 @@ public final Locale setLocale(Locale loc) } /** - * Register an object to receive notifications when a bound property - * has changed + * Register an object to receive notifications when a bound property has changed + * * @see java.beans.PropertyChangeEvent * @see #removePropertyChangeListener(java.beans.PropertyChangeListener) * @param l the listener */ - public final void addPropertyChangeListener(PropertyChangeListener l) - { + public final void addPropertyChangeListener(PropertyChangeListener l) { if (sealed) onSealedMutation(); propertyListeners = Kit.addListener(propertyListeners, l); } /** - * Remove an object from the list of objects registered to receive - * notification of changes to a bounded property + * Remove an object from the list of objects registered to receive notification of changes to a + * bounded property + * * @see java.beans.PropertyChangeEvent * @see #addPropertyChangeListener(java.beans.PropertyChangeListener) * @param l the listener */ - public final void removePropertyChangeListener(PropertyChangeListener l) - { + public final void removePropertyChangeListener(PropertyChangeListener l) { if (sealed) onSealedMutation(); propertyListeners = Kit.removeListener(propertyListeners, l); } /** * Notify any registered listeners that a bounded property has changed + * * @see #addPropertyChangeListener(java.beans.PropertyChangeListener) * @see #removePropertyChangeListener(java.beans.PropertyChangeListener) * @see java.beans.PropertyChangeListener * @see java.beans.PropertyChangeEvent - * @param property the bound property - * @param oldValue the old value - * @param newValue the new value + * @param property the bound property + * @param oldValue the old value + * @param newValue the new value */ - final void firePropertyChange(String property, Object oldValue, - Object newValue) - { + final void firePropertyChange(String property, Object oldValue, Object newValue) { Object listeners = propertyListeners; if (listeners != null) { firePropertyChangeImpl(listeners, property, oldValue, newValue); } } - private void firePropertyChangeImpl(Object listeners, String property, - Object oldValue, Object newValue) - { + private void firePropertyChangeImpl( + Object listeners, String property, Object oldValue, Object newValue) { for (int i = 0; ; ++i) { Object l = Kit.getListener(listeners, i); - if (l == null) - break; + if (l == null) break; if (l instanceof PropertyChangeListener) { - PropertyChangeListener pcl = (PropertyChangeListener)l; - pcl.propertyChange(new PropertyChangeEvent( - this, property, oldValue, newValue)); + PropertyChangeListener pcl = (PropertyChangeListener) l; + pcl.propertyChange(new PropertyChangeEvent(this, property, oldValue, newValue)); } } } @@ -874,16 +792,12 @@ private void firePropertyChangeImpl(Object listeners, String property, * @param lineOffset the offset into lineSource where problem was detected * @see org.mozilla.javascript.ErrorReporter */ - public static void reportWarning(String message, String sourceName, - int lineno, String lineSource, - int lineOffset) - { + public static void reportWarning( + String message, String sourceName, int lineno, String lineSource, int lineOffset) { Context cx = Context.getContext(); if (cx.hasFeature(FEATURE_WARNING_AS_ERROR)) reportError(message, sourceName, lineno, lineSource, lineOffset); - else - cx.getErrorReporter().warning(message, sourceName, lineno, - lineSource, lineOffset); + else cx.getErrorReporter().warning(message, sourceName, lineno, lineSource, lineOffset); } /** @@ -892,16 +806,14 @@ public static void reportWarning(String message, String sourceName, * @param message the warning message to report * @see org.mozilla.javascript.ErrorReporter */ - public static void reportWarning(String message) - { - int[] linep = { 0 }; + public static void reportWarning(String message) { + int[] linep = {0}; String filename = getSourcePositionFromStack(linep); Context.reportWarning(message, filename, linep[0], null, 0); } - public static void reportWarning(String message, Throwable t) - { - int[] linep = { 0 }; + public static void reportWarning(String message, Throwable t) { + int[] linep = {0}; String filename = getSourcePositionFromStack(linep); Writer sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); @@ -921,17 +833,13 @@ public static void reportWarning(String message, Throwable t) * @param lineOffset the offset into lineSource where problem was detected * @see org.mozilla.javascript.ErrorReporter */ - public static void reportError(String message, String sourceName, - int lineno, String lineSource, - int lineOffset) - { + public static void reportError( + String message, String sourceName, int lineno, String lineSource, int lineOffset) { Context cx = getCurrentContext(); if (cx != null) { - cx.getErrorReporter().error(message, sourceName, lineno, - lineSource, lineOffset); + cx.getErrorReporter().error(message, sourceName, lineno, lineSource, lineOffset); } else { - throw new EvaluatorException(message, sourceName, lineno, - lineSource, lineOffset); + throw new EvaluatorException(message, sourceName, lineno, lineSource, lineOffset); } } @@ -941,9 +849,8 @@ public static void reportError(String message, String sourceName, * @param message the error message to report * @see org.mozilla.javascript.ErrorReporter */ - public static void reportError(String message) - { - int[] linep = { 0 }; + public static void reportError(String message) { + int[] linep = {0}; String filename = getSourcePositionFromStack(linep); Context.reportError(message, filename, linep[0], null, 0); } @@ -956,85 +863,58 @@ public static void reportError(String message) * @param lineno the starting line number * @param lineSource the text of the line (may be null) * @param lineOffset the offset into lineSource where problem was detected - * @return a runtime exception that will be thrown to terminate the - * execution of the script + * @return a runtime exception that will be thrown to terminate the execution of the script * @see org.mozilla.javascript.ErrorReporter */ - public static EvaluatorException reportRuntimeError(String message, - String sourceName, - int lineno, - String lineSource, - int lineOffset) - { + public static EvaluatorException reportRuntimeError( + String message, String sourceName, int lineno, String lineSource, int lineOffset) { Context cx = getCurrentContext(); if (cx != null) { - return cx.getErrorReporter(). - runtimeError(message, sourceName, lineno, - lineSource, lineOffset); + return cx.getErrorReporter() + .runtimeError(message, sourceName, lineno, lineSource, lineOffset); } throw new EvaluatorException(message, sourceName, lineno, lineSource, lineOffset); } - static EvaluatorException reportRuntimeErrorById(String messageId, Object... args) - { + static EvaluatorException reportRuntimeErrorById(String messageId, Object... args) { String msg = ScriptRuntime.getMessageById(messageId, args); return reportRuntimeError(msg); } - /** - * @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */ @Deprecated - static EvaluatorException reportRuntimeError0(String messageId) - { + static EvaluatorException reportRuntimeError0(String messageId) { String msg = ScriptRuntime.getMessageById(messageId); return reportRuntimeError(msg); } - /** - * @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */ @Deprecated - static EvaluatorException reportRuntimeError1(String messageId, - Object arg1) - { + static EvaluatorException reportRuntimeError1(String messageId, Object arg1) { String msg = ScriptRuntime.getMessageById(messageId, arg1); return reportRuntimeError(msg); } - /** - * @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */ @Deprecated - static EvaluatorException reportRuntimeError2(String messageId, - Object arg1, Object arg2) - { + static EvaluatorException reportRuntimeError2(String messageId, Object arg1, Object arg2) { String msg = ScriptRuntime.getMessageById(messageId, arg1, arg2); return reportRuntimeError(msg); } - /** - * @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */ @Deprecated - static EvaluatorException reportRuntimeError3(String messageId, - Object arg1, Object arg2, - Object arg3) - { + static EvaluatorException reportRuntimeError3( + String messageId, Object arg1, Object arg2, Object arg3) { String msg = ScriptRuntime.getMessageById(messageId, arg1, arg2, arg3); return reportRuntimeError(msg); } - /** - * @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */ @Deprecated - static EvaluatorException reportRuntimeError4(String messageId, - Object arg1, Object arg2, - Object arg3, Object arg4) - { - String msg - = ScriptRuntime.getMessageById(messageId, arg1, arg2, arg3, arg4); + static EvaluatorException reportRuntimeError4( + String messageId, Object arg1, Object arg2, Object arg3, Object arg4) { + String msg = ScriptRuntime.getMessageById(messageId, arg1, arg2, arg3, arg4); return reportRuntimeError(msg); } @@ -1044,9 +924,8 @@ static EvaluatorException reportRuntimeError4(String messageId, * @param message the error message to report * @see org.mozilla.javascript.ErrorReporter */ - public static EvaluatorException reportRuntimeError(String message) - { - int[] linep = { 0 }; + public static EvaluatorException reportRuntimeError(String message) { + int[] linep = {0}; String filename = getSourcePositionFromStack(linep); return Context.reportRuntimeError(message, filename, linep[0], null, 0); } @@ -1054,208 +933,173 @@ public static EvaluatorException reportRuntimeError(String message) /** * Initialize the standard objects. * - * Creates instances of the standard objects and their constructors - * (Object, String, Number, Date, etc.), setting up 'scope' to act - * as a global object as in ECMA 15.1.

+ *

Creates instances of the standard objects and their constructors (Object, String, Number, + * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1. * - * This method must be called to initialize a scope before scripts - * can be evaluated in that scope.

+ *

This method must be called to initialize a scope before scripts can be evaluated in that + * scope. * - * This method does not affect the Context it is called upon. + *

This method does not affect the Context it is called upon. * * @return the initialized scope */ - public final ScriptableObject initStandardObjects() - { + public final ScriptableObject initStandardObjects() { return initStandardObjects(null, false); } /** - * Initialize the standard objects, leaving out those that offer access directly - * to Java classes. This sets up "scope" to have access to all the standard - * JavaScript classes, but does not create global objects for any top-level - * Java packages. In addition, the "Packages," "JavaAdapter," and - * "JavaImporter" classes, and the "getClass" function, are not - * initialized. + * Initialize the standard objects, leaving out those that offer access directly to Java + * classes. This sets up "scope" to have access to all the standard JavaScript classes, but does + * not create global objects for any top-level Java packages. In addition, the "Packages," + * "JavaAdapter," and "JavaImporter" classes, and the "getClass" function, are not initialized. * - * The result of this function is a scope that may be safely used in a "sandbox" - * environment where it is not desirable to give access to Java code from JavaScript. + *

The result of this function is a scope that may be safely used in a "sandbox" environment + * where it is not desirable to give access to Java code from JavaScript. * - * Creates instances of the standard objects and their constructors - * (Object, String, Number, Date, etc.), setting up 'scope' to act - * as a global object as in ECMA 15.1.

+ *

Creates instances of the standard objects and their constructors (Object, String, Number, + * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1. * - * This method must be called to initialize a scope before scripts - * can be evaluated in that scope.

+ *

This method must be called to initialize a scope before scripts can be evaluated in that + * scope. * - * This method does not affect the Context it is called upon. + *

This method does not affect the Context it is called upon. * * @return the initialized scope */ - public final ScriptableObject initSafeStandardObjects() - { + public final ScriptableObject initSafeStandardObjects() { return initSafeStandardObjects(null, false); } /** * Initialize the standard objects. * - * Creates instances of the standard objects and their constructors - * (Object, String, Number, Date, etc.), setting up 'scope' to act - * as a global object as in ECMA 15.1.

+ *

Creates instances of the standard objects and their constructors (Object, String, Number, + * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1. * - * This method must be called to initialize a scope before scripts - * can be evaluated in that scope.

+ *

This method must be called to initialize a scope before scripts can be evaluated in that + * scope. * - * This method does not affect the Context it is called upon. + *

This method does not affect the Context it is called upon. * - * @param scope the scope to initialize, or null, in which case a new - * object will be created to serve as the scope - * @return the initialized scope. The method returns the value of the scope - * argument if it is not null or newly allocated scope object which - * is an instance {@link ScriptableObject}. + * @param scope the scope to initialize, or null, in which case a new object will be created to + * serve as the scope + * @return the initialized scope. The method returns the value of the scope argument if it is + * not null or newly allocated scope object which is an instance {@link ScriptableObject}. */ - public final Scriptable initStandardObjects(ScriptableObject scope) - { + public final Scriptable initStandardObjects(ScriptableObject scope) { return initStandardObjects(scope, false); } /** - * Initialize the standard objects, leaving out those that offer access directly - * to Java classes. This sets up "scope" to have access to all the standard - * JavaScript classes, but does not create global objects for any top-level - * Java packages. In addition, the "Packages," "JavaAdapter," and - * "JavaImporter" classes, and the "getClass" function, are not - * initialized. + * Initialize the standard objects, leaving out those that offer access directly to Java + * classes. This sets up "scope" to have access to all the standard JavaScript classes, but does + * not create global objects for any top-level Java packages. In addition, the "Packages," + * "JavaAdapter," and "JavaImporter" classes, and the "getClass" function, are not initialized. * - * The result of this function is a scope that may be safely used in a "sandbox" - * environment where it is not desirable to give access to Java code from JavaScript. + *

The result of this function is a scope that may be safely used in a "sandbox" environment + * where it is not desirable to give access to Java code from JavaScript. * - * Creates instances of the standard objects and their constructors - * (Object, String, Number, Date, etc.), setting up 'scope' to act - * as a global object as in ECMA 15.1.

+ *

Creates instances of the standard objects and their constructors (Object, String, Number, + * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1. * - * This method must be called to initialize a scope before scripts - * can be evaluated in that scope.

+ *

This method must be called to initialize a scope before scripts can be evaluated in that + * scope. * - * This method does not affect the Context it is called upon. + *

This method does not affect the Context it is called upon. * - * @param scope the scope to initialize, or null, in which case a new - * object will be created to serve as the scope - * @return the initialized scope. The method returns the value of the scope - * argument if it is not null or newly allocated scope object which - * is an instance {@link ScriptableObject}. + * @param scope the scope to initialize, or null, in which case a new object will be created to + * serve as the scope + * @return the initialized scope. The method returns the value of the scope argument if it is + * not null or newly allocated scope object which is an instance {@link ScriptableObject}. */ - public final Scriptable initSafeStandardObjects(ScriptableObject scope) - { + public final Scriptable initSafeStandardObjects(ScriptableObject scope) { return initSafeStandardObjects(scope, false); } /** * Initialize the standard objects. * - * Creates instances of the standard objects and their constructors - * (Object, String, Number, Date, etc.), setting up 'scope' to act - * as a global object as in ECMA 15.1.

+ *

Creates instances of the standard objects and their constructors (Object, String, Number, + * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1. * - * This method must be called to initialize a scope before scripts - * can be evaluated in that scope.

+ *

This method must be called to initialize a scope before scripts can be evaluated in that + * scope. * - * This method does not affect the Context it is called upon.

+ *

This method does not affect the Context it is called upon. * - * This form of the method also allows for creating "sealed" standard - * objects. An object that is sealed cannot have properties added, changed, - * or removed. This is useful to create a "superglobal" that can be shared - * among several top-level objects. Note that sealing is not allowed in - * the current ECMA/ISO language specification, but is likely for - * the next version. + *

This form of the method also allows for creating "sealed" standard objects. An object that + * is sealed cannot have properties added, changed, or removed. This is useful to create a + * "superglobal" that can be shared among several top-level objects. Note that sealing is not + * allowed in the current ECMA/ISO language specification, but is likely for the next version. * - * @param scope the scope to initialize, or null, in which case a new - * object will be created to serve as the scope - * @param sealed whether or not to create sealed standard objects that - * cannot be modified. - * @return the initialized scope. The method returns the value of the scope - * argument if it is not null or newly allocated scope object. + * @param scope the scope to initialize, or null, in which case a new object will be created to + * serve as the scope + * @param sealed whether or not to create sealed standard objects that cannot be modified. + * @return the initialized scope. The method returns the value of the scope argument if it is + * not null or newly allocated scope object. * @since 1.4R3 */ - public ScriptableObject initStandardObjects(ScriptableObject scope, - boolean sealed) - { + public ScriptableObject initStandardObjects(ScriptableObject scope, boolean sealed) { return ScriptRuntime.initStandardObjects(this, scope, sealed); } /** - * Initialize the standard objects, leaving out those that offer access directly - * to Java classes. This sets up "scope" to have access to all the standard - * JavaScript classes, but does not create global objects for any top-level - * Java packages. In addition, the "Packages," "JavaAdapter," and - * "JavaImporter" classes, and the "getClass" function, are not - * initialized. + * Initialize the standard objects, leaving out those that offer access directly to Java + * classes. This sets up "scope" to have access to all the standard JavaScript classes, but does + * not create global objects for any top-level Java packages. In addition, the "Packages," + * "JavaAdapter," and "JavaImporter" classes, and the "getClass" function, are not initialized. * - * The result of this function is a scope that may be safely used in a "sandbox" - * environment where it is not desirable to give access to Java code from JavaScript. + *

The result of this function is a scope that may be safely used in a "sandbox" environment + * where it is not desirable to give access to Java code from JavaScript. * - * Creates instances of the standard objects and their constructors - * (Object, String, Number, Date, etc.), setting up 'scope' to act - * as a global object as in ECMA 15.1.

+ *

Creates instances of the standard objects and their constructors (Object, String, Number, + * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1. * - * This method must be called to initialize a scope before scripts - * can be evaluated in that scope.

+ *

This method must be called to initialize a scope before scripts can be evaluated in that + * scope. * - * This method does not affect the Context it is called upon.

+ *

This method does not affect the Context it is called upon. * - * This form of the method also allows for creating "sealed" standard - * objects. An object that is sealed cannot have properties added, changed, - * or removed. This is useful to create a "superglobal" that can be shared - * among several top-level objects. Note that sealing is not allowed in - * the current ECMA/ISO language specification, but is likely for - * the next version. + *

This form of the method also allows for creating "sealed" standard objects. An object that + * is sealed cannot have properties added, changed, or removed. This is useful to create a + * "superglobal" that can be shared among several top-level objects. Note that sealing is not + * allowed in the current ECMA/ISO language specification, but is likely for the next version. * - * @param scope the scope to initialize, or null, in which case a new - * object will be created to serve as the scope - * @param sealed whether or not to create sealed standard objects that - * cannot be modified. - * @return the initialized scope. The method returns the value of the scope - * argument if it is not null or newly allocated scope object. + * @param scope the scope to initialize, or null, in which case a new object will be created to + * serve as the scope + * @param sealed whether or not to create sealed standard objects that cannot be modified. + * @return the initialized scope. The method returns the value of the scope argument if it is + * not null or newly allocated scope object. * @since 1.7.6 */ - public ScriptableObject initSafeStandardObjects(ScriptableObject scope, - boolean sealed) - { + public ScriptableObject initSafeStandardObjects(ScriptableObject scope, boolean sealed) { return ScriptRuntime.initSafeStandardObjects(this, scope, sealed); } - /** - * Get the singleton object that represents the JavaScript Undefined value. - */ - public static Object getUndefinedValue() - { + /** Get the singleton object that represents the JavaScript Undefined value. */ + public static Object getUndefinedValue() { return Undefined.instance; } /** * Evaluate a JavaScript source string. * - * The provided source name and line number are used for error messages - * and for producing debug information. + *

The provided source name and line number are used for error messages and for producing + * debug information. * * @param scope the scope to execute in * @param source the JavaScript source * @param sourceName a string describing the source, such as a filename * @param lineno the starting line number - * @param securityDomain an arbitrary object that specifies security - * information about the origin or owner of the script. For - * implementations that don't care about security, this value - * may be null. + * @param securityDomain an arbitrary object that specifies security information about the + * origin or owner of the script. For implementations that don't care about security, this + * value may be null. * @return the result of evaluating the string * @see org.mozilla.javascript.SecurityController */ - public final Object evaluateString(Scriptable scope, String source, - String sourceName, int lineno, - Object securityDomain) - { - Script script = compileString(source, sourceName, lineno, - securityDomain); + public final Object evaluateString( + Scriptable scope, String source, String sourceName, int lineno, Object securityDomain) { + Script script = compileString(source, sourceName, lineno, securityDomain); if (script != null) { return script.exec(this, scope); } @@ -1265,27 +1109,22 @@ public final Object evaluateString(Scriptable scope, String source, /** * Evaluate a reader as JavaScript source. * - * All characters of the reader are consumed. + *

All characters of the reader are consumed. * * @param scope the scope to execute in * @param in the Reader to get JavaScript source from * @param sourceName a string describing the source, such as a filename * @param lineno the starting line number - * @param securityDomain an arbitrary object that specifies security - * information about the origin or owner of the script. For - * implementations that don't care about security, this value - * may be null. + * @param securityDomain an arbitrary object that specifies security information about the + * origin or owner of the script. For implementations that don't care about security, this + * value may be null. * @return the result of evaluating the source - * * @exception IOException if an IOException was generated by the Reader */ - public final Object evaluateReader(Scriptable scope, Reader in, - String sourceName, int lineno, - Object securityDomain) - throws IOException - { - Script script = compileReader(scope, in, sourceName, lineno, - securityDomain); + public final Object evaluateReader( + Scriptable scope, Reader in, String sourceName, int lineno, Object securityDomain) + throws IOException { + Script script = compileReader(scope, in, sourceName, lineno, securityDomain); if (script != null) { return script.exec(this, scope); } @@ -1293,57 +1132,54 @@ public final Object evaluateReader(Scriptable scope, Reader in, } /** - * Execute script that may pause execution by capturing a continuation. - * Caller must be prepared to catch a ContinuationPending exception - * and resume execution by calling - * {@link #resumeContinuation(Object, Scriptable, Object)}. - * @param script The script to execute. Script must have been compiled - * with interpreted mode (optimization level -1) + * Execute script that may pause execution by capturing a continuation. Caller must be prepared + * to catch a ContinuationPending exception and resume execution by calling {@link + * #resumeContinuation(Object, Scriptable, Object)}. + * + * @param script The script to execute. Script must have been compiled with interpreted mode + * (optimization level -1) * @param scope The scope to execute the script against - * @throws ContinuationPending if the script calls a function that results - * in a call to {@link #captureContinuation()} + * @throws ContinuationPending if the script calls a function that results in a call to {@link + * #captureContinuation()} * @since 1.7 Release 2 */ - public Object executeScriptWithContinuations(Script script, - Scriptable scope) - throws ContinuationPending - { - if (!(script instanceof InterpretedFunction) || - !((InterpretedFunction)script).isScript()) - { + public Object executeScriptWithContinuations(Script script, Scriptable scope) + throws ContinuationPending { + if (!(script instanceof InterpretedFunction) + || !((InterpretedFunction) script).isScript()) { // Can only be applied to scripts - throw new IllegalArgumentException("Script argument was not" + - " a script or was not created by interpreted mode "); + throw new IllegalArgumentException( + "Script argument was not" + + " a script or was not created by interpreted mode "); } - return callFunctionWithContinuations((InterpretedFunction) script, - scope, ScriptRuntime.emptyArgs); + return callFunctionWithContinuations( + (InterpretedFunction) script, scope, ScriptRuntime.emptyArgs); } /** - * Call function that may pause execution by capturing a continuation. - * Caller must be prepared to catch a ContinuationPending exception - * and resume execution by calling - * {@link #resumeContinuation(Object, Scriptable, Object)}. - * @param function The function to call. The function must have been - * compiled with interpreted mode (optimization level -1) + * Call function that may pause execution by capturing a continuation. Caller must be prepared + * to catch a ContinuationPending exception and resume execution by calling {@link + * #resumeContinuation(Object, Scriptable, Object)}. + * + * @param function The function to call. The function must have been compiled with interpreted + * mode (optimization level -1) * @param scope The scope to execute the script against * @param args The arguments for the function - * @throws ContinuationPending if the script calls a function that results - * in a call to {@link #captureContinuation()} + * @throws ContinuationPending if the script calls a function that results in a call to {@link + * #captureContinuation()} * @since 1.7 Release 2 */ - public Object callFunctionWithContinuations(Callable function, - Scriptable scope, Object[] args) - throws ContinuationPending - { + public Object callFunctionWithContinuations(Callable function, Scriptable scope, Object[] args) + throws ContinuationPending { if (!(function instanceof InterpretedFunction)) { // Can only be applied to scripts - throw new IllegalArgumentException("Function argument was not" + - " created by interpreted mode "); + throw new IllegalArgumentException( + "Function argument was not" + " created by interpreted mode "); } if (ScriptRuntime.hasTopCall(this)) { - throw new IllegalStateException("Cannot have any pending top " + - "calls when executing a script with continuations"); + throw new IllegalStateException( + "Cannot have any pending top " + + "calls when executing a script with continuations"); } // Annotate so we can check later to ensure no java code in // intervening frames @@ -1352,60 +1188,49 @@ public Object callFunctionWithContinuations(Callable function, } /** - * Capture a continuation from the current execution. The execution must - * have been started via a call to - * {@link #executeScriptWithContinuations(Script, Scriptable)} or - * {@link #callFunctionWithContinuations(Callable, Scriptable, Object[])}. - * This implies that the code calling - * this method must have been called as a function from the - * JavaScript script. Also, there cannot be any non-JavaScript code - * between the JavaScript frames (e.g., a call to eval()). The - * ContinuationPending exception returned must be thrown. + * Capture a continuation from the current execution. The execution must have been started via a + * call to {@link #executeScriptWithContinuations(Script, Scriptable)} or {@link + * #callFunctionWithContinuations(Callable, Scriptable, Object[])}. This implies that the code + * calling this method must have been called as a function from the JavaScript script. Also, + * there cannot be any non-JavaScript code between the JavaScript frames (e.g., a call to + * eval()). The ContinuationPending exception returned must be thrown. + * * @return A ContinuationPending exception that must be thrown * @since 1.7 Release 2 */ public ContinuationPending captureContinuation() { - return new ContinuationPending( - Interpreter.captureContinuation(this)); - } - - /** - * Restarts execution of the JavaScript suspended at the call - * to {@link #captureContinuation()}. Execution of the code will resume - * with the functionResult as the result of the call that captured the - * continuation. - * Execution of the script will either conclude normally and the - * result returned, another continuation will be captured and - * thrown, or the script will terminate abnormally and throw an exception. - * @param continuation The value returned by - * {@link ContinuationPending#getContinuation()} - * @param functionResult This value will appear to the code being resumed - * as the result of the function that captured the continuation - * @throws ContinuationPending if another continuation is captured before - * the code terminates + return new ContinuationPending(Interpreter.captureContinuation(this)); + } + + /** + * Restarts execution of the JavaScript suspended at the call to {@link #captureContinuation()}. + * Execution of the code will resume with the functionResult as the result of the call that + * captured the continuation. Execution of the script will either conclude normally and the + * result returned, another continuation will be captured and thrown, or the script will + * terminate abnormally and throw an exception. + * + * @param continuation The value returned by {@link ContinuationPending#getContinuation()} + * @param functionResult This value will appear to the code being resumed as the result of the + * function that captured the continuation + * @throws ContinuationPending if another continuation is captured before the code terminates * @since 1.7 Release 2 */ - public Object resumeContinuation(Object continuation, - Scriptable scope, Object functionResult) - throws ContinuationPending - { - Object[] args = { functionResult }; + public Object resumeContinuation(Object continuation, Scriptable scope, Object functionResult) + throws ContinuationPending { + Object[] args = {functionResult}; return Interpreter.restartContinuation( - (org.mozilla.javascript.NativeContinuation) continuation, - this, scope, args); + (org.mozilla.javascript.NativeContinuation) continuation, this, scope, args); } /** * Check whether a string is ready to be compiled. - *

- * stringIsCompilableUnit is intended to support interactive compilation of - * JavaScript. If compiling the string would result in an error - * that might be fixed by appending more source, this method - * returns false. In every other case, it returns true. - *

- * Interactive shells may accumulate source lines, using this - * method after each new line is appended to check whether the - * statement being entered is complete. + * + *

stringIsCompilableUnit is intended to support interactive compilation of JavaScript. If + * compiling the string would result in an error that might be fixed by appending more source, + * this method returns false. In every other case, it returns true. + * + *

Interactive shells may accumulate source lines, using this method after each new line is + * appended to check whether the statement being entered is complete. * * @param source the source buffer to check * @return whether the source is ready for compilation @@ -1432,86 +1257,91 @@ public final boolean stringIsCompilableUnit(String source) { /** * @deprecated - * @see #compileReader(Reader in, String sourceName, int lineno, - * Object securityDomain) + * @see #compileReader(Reader in, String sourceName, int lineno, Object securityDomain) */ @Deprecated - public final Script compileReader(Scriptable scope, Reader in, - String sourceName, int lineno, - Object securityDomain) - throws IOException - { + public final Script compileReader( + Scriptable scope, Reader in, String sourceName, int lineno, Object securityDomain) + throws IOException { return compileReader(in, sourceName, lineno, securityDomain); } /** * Compiles the source in the given reader. - *

- * Returns a script that may later be executed. - * Will consume all the source in the reader. + * + *

Returns a script that may later be executed. Will consume all the source in the reader. * * @param in the input reader * @param sourceName a string describing the source, such as a filename * @param lineno the starting line number for reporting errors - * @param securityDomain an arbitrary object that specifies security - * information about the origin or owner of the script. For - * implementations that don't care about security, this value - * may be null. + * @param securityDomain an arbitrary object that specifies security information about the + * origin or owner of the script. For implementations that don't care about security, this + * value may be null. * @return a script that may later be executed * @exception IOException if an IOException was generated by the Reader * @see org.mozilla.javascript.Script */ - public final Script compileReader(Reader in, String sourceName, - int lineno, Object securityDomain) - throws IOException - { + public final Script compileReader( + Reader in, String sourceName, int lineno, Object securityDomain) throws IOException { if (lineno < 0) { // For compatibility IllegalArgumentException can not be thrown here lineno = 0; } - return (Script) compileImpl(null, Kit.readReader(in), sourceName, lineno, - securityDomain, false, null, null); + return (Script) + compileImpl( + null, + Kit.readReader(in), + sourceName, + lineno, + securityDomain, + false, + null, + null); } /** * Compiles the source in the given string. - *

- * Returns a script that may later be executed. + * + *

Returns a script that may later be executed. * * @param source the source string * @param sourceName a string describing the source, such as a filename - * @param lineno the starting line number for reporting errors. Use - * 0 if the line number is unknown. - * @param securityDomain an arbitrary object that specifies security - * information about the origin or owner of the script. For - * implementations that don't care about security, this value - * may be null. + * @param lineno the starting line number for reporting errors. Use 0 if the line number is + * unknown. + * @param securityDomain an arbitrary object that specifies security information about the + * origin or owner of the script. For implementations that don't care about security, this + * value may be null. * @return a script that may later be executed * @see org.mozilla.javascript.Script */ - public final Script compileString(String source, - String sourceName, int lineno, - Object securityDomain) - { + public final Script compileString( + String source, String sourceName, int lineno, Object securityDomain) { if (lineno < 0) { // For compatibility IllegalArgumentException can not be thrown here lineno = 0; } - return compileString(source, null, null, sourceName, lineno, - securityDomain); + return compileString(source, null, null, sourceName, lineno, securityDomain); } - final Script compileString(String source, - Evaluator compiler, - ErrorReporter compilationErrorReporter, - String sourceName, int lineno, - Object securityDomain) - { + final Script compileString( + String source, + Evaluator compiler, + ErrorReporter compilationErrorReporter, + String sourceName, + int lineno, + Object securityDomain) { try { - return (Script) compileImpl(null, source, sourceName, lineno, - securityDomain, false, - compiler, compilationErrorReporter); + return (Script) + compileImpl( + null, + source, + sourceName, + lineno, + securityDomain, + false, + compiler, + compilationErrorReporter); } catch (IOException ioe) { // Should not happen when dealing with source as string throw new RuntimeException(ioe); @@ -1520,41 +1350,45 @@ final Script compileString(String source, /** * Compile a JavaScript function. - *

- * The function source must be a function definition as defined by - * ECMA (e.g., "function f(a) { return a; }"). + * + *

The function source must be a function definition as defined by ECMA (e.g., "function f(a) + * { return a; }"). * * @param scope the scope to compile relative to * @param source the function definition source * @param sourceName a string describing the source, such as a filename * @param lineno the starting line number - * @param securityDomain an arbitrary object that specifies security - * information about the origin or owner of the script. For - * implementations that don't care about security, this value - * may be null. + * @param securityDomain an arbitrary object that specifies security information about the + * origin or owner of the script. For implementations that don't care about security, this + * value may be null. * @return a Function that may later be called * @see org.mozilla.javascript.Function */ - public final Function compileFunction(Scriptable scope, String source, - String sourceName, int lineno, - Object securityDomain) - { - return compileFunction(scope, source, null, null, sourceName, lineno, - securityDomain); + public final Function compileFunction( + Scriptable scope, String source, String sourceName, int lineno, Object securityDomain) { + return compileFunction(scope, source, null, null, sourceName, lineno, securityDomain); } - final Function compileFunction(Scriptable scope, String source, - Evaluator compiler, - ErrorReporter compilationErrorReporter, - String sourceName, int lineno, - Object securityDomain) - { + final Function compileFunction( + Scriptable scope, + String source, + Evaluator compiler, + ErrorReporter compilationErrorReporter, + String sourceName, + int lineno, + Object securityDomain) { try { - return (Function) compileImpl(scope, source, sourceName, - lineno, securityDomain, true, - compiler, compilationErrorReporter); - } - catch (IOException ioe) { + return (Function) + compileImpl( + scope, + source, + sourceName, + lineno, + securityDomain, + true, + compiler, + compilationErrorReporter); + } catch (IOException ioe) { // Should never happen because we just made the reader // from a String throw new RuntimeException(ioe); @@ -1563,57 +1397,50 @@ final Function compileFunction(Scriptable scope, String source, /** * Decompile the script. - *

- * The canonical source of the script is returned. + * + *

The canonical source of the script is returned. * * @param script the script to decompile * @param indent the number of spaces to indent the result * @return a string representing the script source */ - public final String decompileScript(Script script, int indent) - { + public final String decompileScript(Script script, int indent) { NativeFunction scriptImpl = (NativeFunction) script; return scriptImpl.decompile(indent, 0); } /** * Decompile a JavaScript Function. - *

- * Decompiles a previously compiled JavaScript function object to - * canonical source. - *

- * Returns function body of '[native code]' if no decompilation - * information is available. + * + *

Decompiles a previously compiled JavaScript function object to canonical source. + * + *

Returns function body of '[native code]' if no decompilation information is available. * * @param fun the JavaScript function to decompile * @param indent the number of spaces to indent the result * @return a string representing the function source */ - public final String decompileFunction(Function fun, int indent) - { - if (fun instanceof BaseFunction) - return ((BaseFunction)fun).decompile(indent, 0); + public final String decompileFunction(Function fun, int indent) { + if (fun instanceof BaseFunction) return ((BaseFunction) fun).decompile(indent, 0); return "function " + fun.getClassName() + "() {\n\t[native code]\n}\n"; } /** * Decompile the body of a JavaScript Function. - *

- * Decompiles the body a previously compiled JavaScript Function - * object to canonical source, omitting the function header and - * trailing brace. * - * Returns '[native code]' if no decompilation information is available. + *

Decompiles the body a previously compiled JavaScript Function object to canonical source, + * omitting the function header and trailing brace. + * + *

Returns '[native code]' if no decompilation information is available. * * @param fun the JavaScript function to decompile * @param indent the number of spaces to indent the result * @return a string representing the function body source. */ - public final String decompileFunctionBody(Function fun, int indent) - { + public final String decompileFunctionBody(Function fun, int indent) { if (fun instanceof BaseFunction) { - BaseFunction bf = (BaseFunction)fun; + BaseFunction bf = (BaseFunction) fun; return bf.decompile(indent, Decompiler.ONLY_BODY_FLAG); } // ALERT: not sure what the right response here is. @@ -1623,72 +1450,67 @@ public final String decompileFunctionBody(Function fun, int indent) /** * Create a new JavaScript object. * - * Equivalent to evaluating "new Object()". - * @param scope the scope to search for the constructor and to evaluate - * against + *

Equivalent to evaluating "new Object()". + * + * @param scope the scope to search for the constructor and to evaluate against * @return the new object */ - public Scriptable newObject(Scriptable scope) - { + public Scriptable newObject(Scriptable scope) { NativeObject result = new NativeObject(); - ScriptRuntime.setBuiltinProtoAndParent(result, scope, - TopLevel.Builtins.Object); + ScriptRuntime.setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Object); return result; } /** * Create a new JavaScript object by executing the named constructor. * - * The call newObject(scope, "Foo") is equivalent to - * evaluating "new Foo()". + *

The call newObject(scope, "Foo") is equivalent to evaluating "new Foo()". * * @param scope the scope to search for the constructor and to evaluate against * @param constructorName the name of the constructor to call * @return the new object */ - public Scriptable newObject(Scriptable scope, String constructorName) - { + public Scriptable newObject(Scriptable scope, String constructorName) { return newObject(scope, constructorName, ScriptRuntime.emptyArgs); } /** * Creates a new JavaScript object by executing the named constructor. * - * Searches scope for the named constructor, calls it with - * the given arguments, and returns the result.

+ *

Searches scope for the named constructor, calls it with the given arguments, + * and returns the result. + * + *

The code * - * The code *

      * Object[] args = { "a", "b" };
      * newObject(scope, "Foo", args)
- * is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo - * constructor has been defined in scope. * - * @param scope The scope to search for the constructor and to evaluate - * against + * is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo constructor has been + * defined in scope. + * + * @param scope The scope to search for the constructor and to evaluate against * @param constructorName the name of the constructor to call * @param args the array of arguments for the constructor * @return the new object */ - public Scriptable newObject(Scriptable scope, String constructorName, - Object[] args) - { + public Scriptable newObject(Scriptable scope, String constructorName, Object[] args) { return ScriptRuntime.newObject(this, scope, constructorName, args); } /** * Create an array with a specified initial length. + * *

+ * * @param scope the scope to create the object in - * @param length the initial length (JavaScript arrays may have - * additional properties added dynamically). + * @param length the initial length (JavaScript arrays may have additional properties added + * dynamically). * @return the new array object */ - public Scriptable newArray(Scriptable scope, int length) - { + public Scriptable newArray(Scriptable scope, int length) { NativeArray result = new NativeArray(length); - ScriptRuntime.setBuiltinProtoAndParent(result, scope, - TopLevel.Builtins.Array); + ScriptRuntime.setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Array); return result; } @@ -1696,107 +1518,92 @@ public Scriptable newArray(Scriptable scope, int length) * Create an array with a set of initial elements. * * @param scope the scope to create the object in. - * @param elements the initial elements. Each object in this array - * must be an acceptable JavaScript type and type - * of array should be exactly Object[], not - * SomeObjectSubclass[]. + * @param elements the initial elements. Each object in this array must be an acceptable + * JavaScript type and type of array should be exactly Object[], not SomeObjectSubclass[]. * @return the new array object. */ - public Scriptable newArray(Scriptable scope, Object[] elements) - { + public Scriptable newArray(Scriptable scope, Object[] elements) { if (elements.getClass().getComponentType() != ScriptRuntime.ObjectClass) throw new IllegalArgumentException(); NativeArray result = new NativeArray(elements); - ScriptRuntime.setBuiltinProtoAndParent(result, scope, - TopLevel.Builtins.Array); + ScriptRuntime.setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Array); return result; } /** * Get the elements of a JavaScript array. - *

- * If the object defines a length property convertible to double number, - * then the number is converted Uint32 value as defined in Ecma 9.6 - * and Java array of that size is allocated. - * The array is initialized with the values obtained by - * calling get() on object for each value of i in [0,length-1]. If - * there is not a defined value for a property the Undefined value - * is used to initialize the corresponding element in the array. The - * Java array is then returned. - * If the object doesn't define a length property or it is not a number, - * empty array is returned. + * + *

If the object defines a length property convertible to double number, then the number is + * converted Uint32 value as defined in Ecma 9.6 and Java array of that size is allocated. The + * array is initialized with the values obtained by calling get() on object for each value of i + * in [0,length-1]. If there is not a defined value for a property the Undefined value is used + * to initialize the corresponding element in the array. The Java array is then returned. If the + * object doesn't define a length property or it is not a number, empty array is returned. + * * @param object the JavaScript array or array-like object * @return a Java array of objects * @since 1.4 release 2 */ - public final Object[] getElements(Scriptable object) - { + public final Object[] getElements(Scriptable object) { return ScriptRuntime.getArrayElements(object); } /** * Convert the value to a JavaScript boolean value. - *

- * See ECMA 9.2. + * + *

See ECMA 9.2. * * @param value a JavaScript value - * @return the corresponding boolean value converted using - * the ECMA rules + * @return the corresponding boolean value converted using the ECMA rules */ - public static boolean toBoolean(Object value) - { + public static boolean toBoolean(Object value) { return ScriptRuntime.toBoolean(value); } /** * Convert the value to a JavaScript Number value. - *

- * Returns a Java double for the JavaScript Number. - *

- * See ECMA 9.3. + * + *

Returns a Java double for the JavaScript Number. + * + *

See ECMA 9.3. * * @param value a JavaScript value - * @return the corresponding double value converted using - * the ECMA rules + * @return the corresponding double value converted using the ECMA rules */ - public static double toNumber(Object value) - { + public static double toNumber(Object value) { return ScriptRuntime.toNumber(value); } /** * Convert the value to a JavaScript String value. + * + *

See ECMA 9.8. + * *

- * See ECMA 9.8. - *

+ * * @param value a JavaScript value - * @return the corresponding String value converted using - * the ECMA rules + * @return the corresponding String value converted using the ECMA rules */ - public static String toString(Object value) - { + public static String toString(Object value) { return ScriptRuntime.toString(value); } /** * Convert the value to an JavaScript object value. - *

- * Note that a scope must be provided to look up the constructors - * for Number, Boolean, and String. - *

- * See ECMA 9.9. - *

- * Additionally, arbitrary Java objects and classes will be - * wrapped in a Scriptable object with its Java fields and methods - * reflected as JavaScript properties of the object. + * + *

Note that a scope must be provided to look up the constructors for Number, Boolean, and + * String. + * + *

See ECMA 9.9. + * + *

Additionally, arbitrary Java objects and classes will be wrapped in a Scriptable object + * with its Java fields and methods reflected as JavaScript properties of the object. * * @param value any Java object - * @param scope global scope containing constructors for Number, - * Boolean, and String + * @param scope global scope containing constructors for Number, Boolean, and String * @return new JavaScript object */ - public static Scriptable toObject(Object value, Scriptable scope) - { + public static Scriptable toObject(Object value, Scriptable scope) { return ScriptRuntime.toObject(scope, value); } @@ -1805,31 +1612,27 @@ public static Scriptable toObject(Object value, Scriptable scope) * @see #toObject(Object, Scriptable) */ @Deprecated - public static Scriptable toObject(Object value, Scriptable scope, - Class staticType) - { + public static Scriptable toObject(Object value, Scriptable scope, Class staticType) { return ScriptRuntime.toObject(scope, value); } /** - * Convenient method to convert java value to its closest representation - * in JavaScript. - *

- * If value is an instance of String, Number, Boolean, Function or - * Scriptable, it is returned as it and will be treated as the corresponding - * JavaScript type of string, number, boolean, function and object. - *

- * Note that for Number instances during any arithmetic operation in - * JavaScript the engine will always use the result of - * Number.doubleValue() resulting in a precision loss if - * the number can not fit into double. - *

- * If value is an instance of Character, it will be converted to string of - * length 1 and its JavaScript type will be string. - *

- * The rest of values will be wrapped as LiveConnect objects - * by calling {@link WrapFactory#wrap(Context cx, Scriptable scope, - * Object obj, Type staticType)} as in: + * Convenient method to convert java value to its closest representation in JavaScript. + * + *

If value is an instance of String, Number, Boolean, Function or Scriptable, it is returned + * as it and will be treated as the corresponding JavaScript type of string, number, boolean, + * function and object. + * + *

Note that for Number instances during any arithmetic operation in JavaScript the engine + * will always use the result of Number.doubleValue() resulting in a precision loss + * if the number can not fit into double. + * + *

If value is an instance of Character, it will be converted to string of length 1 and its + * JavaScript type will be string. + * + *

The rest of values will be wrapped as LiveConnect objects by calling {@link + * WrapFactory#wrap(Context cx, Scriptable scope, Object obj, Type staticType)} as in: + * *

      *    Context cx = Context.getCurrentContext();
      *    return cx.getWrapFactory().wrap(cx, scope, value, null);
@@ -1839,14 +1642,14 @@ public static Scriptable toObject(Object value, Scriptable scope,
      * @param scope top scope object
      * @return value suitable to pass to any API that takes JavaScript values.
      */
-    public static Object javaToJS(Object value, Scriptable scope)
-    {
-        if (value instanceof String || value instanceof Number
-            || value instanceof Boolean || value instanceof Scriptable)
-        {
+    public static Object javaToJS(Object value, Scriptable scope) {
+        if (value instanceof String
+                || value instanceof Number
+                || value instanceof Boolean
+                || value instanceof Scriptable) {
             return value;
         } else if (value instanceof Character) {
-            return String.valueOf(((Character)value).charValue());
+            return String.valueOf(((Character) value).charValue());
         } else {
             Context cx = Context.getContext();
             return cx.getWrapFactory().wrap(cx, scope, value, null);
@@ -1854,33 +1657,28 @@ public static Object javaToJS(Object value, Scriptable scope)
     }
 
     /**
-     * Convert a JavaScript value into the desired type.
-     * Uses the semantics defined with LiveConnect3 and throws an
-     * Illegal argument exception if the conversion cannot be performed.
+     * Convert a JavaScript value into the desired type. Uses the semantics defined with
+     * LiveConnect3 and throws an Illegal argument exception if the conversion cannot be performed.
+     *
      * @param value the JavaScript value to convert
-     * @param desiredType the Java type to convert to. Primitive Java
-     *        types are represented using the TYPE fields in the corresponding
-     *        wrapper class in java.lang.
+     * @param desiredType the Java type to convert to. Primitive Java types are represented using
+     *     the TYPE fields in the corresponding wrapper class in java.lang.
      * @return the converted value
      * @throws EvaluatorException if the conversion cannot be performed
      */
-    public static Object jsToJava(Object value, Class desiredType)
-        throws EvaluatorException
-    {
+    public static Object jsToJava(Object value, Class desiredType) throws EvaluatorException {
         return NativeJavaObject.coerceTypeImpl(desiredType, value);
     }
 
     /**
      * @deprecated
      * @see #jsToJava(Object, Class)
-     * @throws IllegalArgumentException if the conversion cannot be performed.
-     *         Note that {@link #jsToJava(Object, Class)} throws
-     *         {@link EvaluatorException} instead.
+     * @throws IllegalArgumentException if the conversion cannot be performed. Note that {@link
+     *     #jsToJava(Object, Class)} throws {@link EvaluatorException} instead.
      */
     @Deprecated
     public static Object toType(Object value, Class desiredType)
-        throws IllegalArgumentException
-    {
+            throws IllegalArgumentException {
         try {
             return jsToJava(value, desiredType);
         } catch (EvaluatorException ex) {
@@ -1889,254 +1687,233 @@ public static Object toType(Object value, Class desiredType)
     }
 
     /**
-     * Rethrow the exception wrapping it as the script runtime exception.
-     * Unless the exception is instance of {@link EcmaError} or
-     * {@link EvaluatorException} it will be wrapped as
-     * {@link WrappedException}, a subclass of {@link EvaluatorException}.
-     * The resulting exception object always contains
-     * source name and line number of script that triggered exception.
-     * 

- * This method always throws an exception, its return value is provided - * only for convenience to allow a usage like: + * Rethrow the exception wrapping it as the script runtime exception. Unless the exception is + * instance of {@link EcmaError} or {@link EvaluatorException} it will be wrapped as {@link + * WrappedException}, a subclass of {@link EvaluatorException}. The resulting exception object + * always contains source name and line number of script that triggered exception. + * + *

This method always throws an exception, its return value is provided only for convenience + * to allow a usage like: + * *

      * throw Context.throwAsScriptRuntimeEx(ex);
      * 
+ * * to indicate that code after the method is unreachable. + * * @throws EvaluatorException * @throws EcmaError */ - public static RuntimeException throwAsScriptRuntimeEx(Throwable e) - { + public static RuntimeException throwAsScriptRuntimeEx(Throwable e) { while ((e instanceof InvocationTargetException)) { e = ((InvocationTargetException) e).getTargetException(); } // special handling of Error so scripts would not catch them if (e instanceof Error) { Context cx = getContext(); - if (cx == null || - !cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) - { - throw (Error)e; + if (cx == null || !cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) { + throw (Error) e; } } if (e instanceof RhinoException) { - throw (RhinoException)e; + throw (RhinoException) e; } throw new WrappedException(e); } /** * Tell whether debug information is being generated. + * * @since 1.3 */ - public final boolean isGeneratingDebug() - { + public final boolean isGeneratingDebug() { return generatingDebug; } /** * Specify whether or not debug information should be generated. - *

- * Setting the generation of debug information on will set the - * optimization level to zero. + * + *

Setting the generation of debug information on will set the optimization level to zero. + * * @since 1.3 */ - public final void setGeneratingDebug(boolean generatingDebug) - { + public final void setGeneratingDebug(boolean generatingDebug) { if (sealed) onSealedMutation(); generatingDebugChanged = true; - if (generatingDebug && getOptimizationLevel() > 0) - setOptimizationLevel(0); + if (generatingDebug && getOptimizationLevel() > 0) setOptimizationLevel(0); this.generatingDebug = generatingDebug; } /** * Tell whether source information is being generated. + * * @since 1.3 */ - public final boolean isGeneratingSource() - { + public final boolean isGeneratingSource() { return generatingSource; } /** * Specify whether or not source information should be generated. - *

- * Without source information, evaluating the "toString" method - * on JavaScript functions produces only "[native code]" for - * the body of the function. - * Note that code generated without source is not fully ECMA - * conformant. + * + *

Without source information, evaluating the "toString" method on JavaScript functions + * produces only "[native code]" for the body of the function. Note that code generated without + * source is not fully ECMA conformant. + * * @since 1.3 */ - public final void setGeneratingSource(boolean generatingSource) - { + public final void setGeneratingSource(boolean generatingSource) { if (sealed) onSealedMutation(); this.generatingSource = generatingSource; } /** * Get the current optimization level. - *

- * The optimization level is expressed as an integer between -1 and - * 9. - * @since 1.3 * + *

The optimization level is expressed as an integer between -1 and 9. + * + * @since 1.3 */ - public final int getOptimizationLevel() - { + public final int getOptimizationLevel() { return optimizationLevel; } /** * Set the current optimization level. - *

- * The optimization level is expected to be an integer between -1 and - * 9. Any negative values will be interpreted as -1, and any values - * greater than 9 will be interpreted as 9. - * An optimization level of -1 indicates that interpretive mode will - * always be used. Levels 0 through 9 indicate that class files may - * be generated. Higher optimization levels trade off compile time - * performance for runtime performance. - * The optimizer level can't be set greater than -1 if the optimizer - * package doesn't exist at run time. - * @param optimizationLevel an integer indicating the level of - * optimization to perform - * @since 1.3 * + *

The optimization level is expected to be an integer between -1 and 9. Any negative values + * will be interpreted as -1, and any values greater than 9 will be interpreted as 9. An + * optimization level of -1 indicates that interpretive mode will always be used. Levels 0 + * through 9 indicate that class files may be generated. Higher optimization levels trade off + * compile time performance for runtime performance. The optimizer level can't be set greater + * than -1 if the optimizer package doesn't exist at run time. + * + * @param optimizationLevel an integer indicating the level of optimization to perform + * @since 1.3 */ - public final void setOptimizationLevel(int optimizationLevel) - { + public final void setOptimizationLevel(int optimizationLevel) { if (sealed) onSealedMutation(); if (optimizationLevel == -2) { // To be compatible with Cocoon fork optimizationLevel = -1; } checkOptimizationLevel(optimizationLevel); - if (codegenClass == null) - optimizationLevel = -1; + if (codegenClass == null) optimizationLevel = -1; this.optimizationLevel = optimizationLevel; } - public static boolean isValidOptimizationLevel(int optimizationLevel) - { + public static boolean isValidOptimizationLevel(int optimizationLevel) { return -1 <= optimizationLevel && optimizationLevel <= 9; } - public static void checkOptimizationLevel(int optimizationLevel) - { + public static void checkOptimizationLevel(int optimizationLevel) { if (isValidOptimizationLevel(optimizationLevel)) { return; } throw new IllegalArgumentException( - "Optimization level outside [-1..9]: "+optimizationLevel); + "Optimization level outside [-1..9]: " + optimizationLevel); } /** - * Returns the maximum stack depth (in terms of number of call frames) - * allowed in a single invocation of interpreter. If the set depth would be - * exceeded, the interpreter will throw an EvaluatorException in the script. - * Defaults to Integer.MAX_VALUE. The setting only has effect for - * interpreted functions (those compiled with optimization level set to -1). - * As the interpreter doesn't use the Java stack but rather manages its own - * stack in the heap memory, a runaway recursion in interpreted code would - * eventually consume all available memory and cause OutOfMemoryError - * instead of a StackOverflowError limited to only a single thread. This + * Returns the maximum stack depth (in terms of number of call frames) allowed in a single + * invocation of interpreter. If the set depth would be exceeded, the interpreter will throw an + * EvaluatorException in the script. Defaults to Integer.MAX_VALUE. The setting only has effect + * for interpreted functions (those compiled with optimization level set to -1). As the + * interpreter doesn't use the Java stack but rather manages its own stack in the heap memory, a + * runaway recursion in interpreted code would eventually consume all available memory and cause + * OutOfMemoryError instead of a StackOverflowError limited to only a single thread. This * setting helps prevent such situations. * * @return The current maximum interpreter stack depth. */ - public final int getMaximumInterpreterStackDepth() - { + public final int getMaximumInterpreterStackDepth() { return maximumInterpreterStackDepth; } /** - * Sets the maximum stack depth (in terms of number of call frames) - * allowed in a single invocation of interpreter. If the set depth would be - * exceeded, the interpreter will throw an EvaluatorException in the script. - * Defaults to Integer.MAX_VALUE. The setting only has effect for - * interpreted functions (those compiled with optimization level set to -1). - * As the interpreter doesn't use the Java stack but rather manages its own - * stack in the heap memory, a runaway recursion in interpreted code would - * eventually consume all available memory and cause OutOfMemoryError - * instead of a StackOverflowError limited to only a single thread. This + * Sets the maximum stack depth (in terms of number of call frames) allowed in a single + * invocation of interpreter. If the set depth would be exceeded, the interpreter will throw an + * EvaluatorException in the script. Defaults to Integer.MAX_VALUE. The setting only has effect + * for interpreted functions (those compiled with optimization level set to -1). As the + * interpreter doesn't use the Java stack but rather manages its own stack in the heap memory, a + * runaway recursion in interpreted code would eventually consume all available memory and cause + * OutOfMemoryError instead of a StackOverflowError limited to only a single thread. This * setting helps prevent such situations. * * @param max the new maximum interpreter stack depth - * @throws IllegalStateException if this context's optimization level is not - * -1 + * @throws IllegalStateException if this context's optimization level is not -1 * @throws IllegalArgumentException if the new depth is not at least 1 */ - public final void setMaximumInterpreterStackDepth(int max) - { - if(sealed) onSealedMutation(); - if(optimizationLevel != -1) { - throw new IllegalStateException("Cannot set maximumInterpreterStackDepth when optimizationLevel != -1"); + public final void setMaximumInterpreterStackDepth(int max) { + if (sealed) onSealedMutation(); + if (optimizationLevel != -1) { + throw new IllegalStateException( + "Cannot set maximumInterpreterStackDepth when optimizationLevel != -1"); } - if(max < 1) { - throw new IllegalArgumentException("Cannot set maximumInterpreterStackDepth to less than 1"); + if (max < 1) { + throw new IllegalArgumentException( + "Cannot set maximumInterpreterStackDepth to less than 1"); } maximumInterpreterStackDepth = max; } /** * Set the security controller for this context. - *

SecurityController may only be set if it is currently null - * and {@link SecurityController#hasGlobal()} is false. - * Otherwise a SecurityException is thrown. + * + *

SecurityController may only be set if it is currently null and {@link + * SecurityController#hasGlobal()} is false. Otherwise a SecurityException is + * thrown. + * * @param controller a SecurityController object - * @throws SecurityException if there is already a SecurityController - * object for this Context or globally installed. + * @throws SecurityException if there is already a SecurityController object for this Context or + * globally installed. * @see SecurityController#initGlobal(SecurityController controller) * @see SecurityController#hasGlobal() */ - public final void setSecurityController(SecurityController controller) - { + public final void setSecurityController(SecurityController controller) { if (sealed) onSealedMutation(); if (controller == null) throw new IllegalArgumentException(); if (securityController != null) { throw new SecurityException("Can not overwrite existing SecurityController object"); } if (SecurityController.hasGlobal()) { - throw new SecurityException("Can not overwrite existing global SecurityController object"); + throw new SecurityException( + "Can not overwrite existing global SecurityController object"); } securityController = controller; } /** * Set the LiveConnect access filter for this context. - *

{@link ClassShutter} may only be set if it is currently null. - * Otherwise a SecurityException is thrown. + * + *

{@link ClassShutter} may only be set if it is currently null. Otherwise a + * SecurityException is thrown. + * * @param shutter a ClassShutter object - * @throws SecurityException if there is already a ClassShutter - * object for this Context + * @throws SecurityException if there is already a ClassShutter object for this Context */ - public synchronized final void setClassShutter(ClassShutter shutter) - { + public final synchronized void setClassShutter(ClassShutter shutter) { if (sealed) onSealedMutation(); if (shutter == null) throw new IllegalArgumentException(); if (hasClassShutter) { - throw new SecurityException("Cannot overwrite existing " + - "ClassShutter object"); + throw new SecurityException("Cannot overwrite existing " + "ClassShutter object"); } classShutter = shutter; hasClassShutter = true; } - final synchronized ClassShutter getClassShutter() - { + final synchronized ClassShutter getClassShutter() { return classShutter; } public interface ClassShutterSetter { public void setClassShutter(ClassShutter shutter); + public ClassShutter getClassShutter(); } public final synchronized ClassShutterSetter getClassShutterSetter() { - if (hasClassShutter) - return null; + if (hasClassShutter) return null; hasClassShutter = true; return new ClassShutterSetter() { @@ -2154,50 +1931,46 @@ public ClassShutter getClassShutter() { /** * Get a value corresponding to a key. - *

- * Since the Context is associated with a thread it can be - * used to maintain values that can be later retrieved using - * the current thread. - *

- * Note that the values are maintained with the Context, so - * if the Context is disassociated from the thread the values - * cannot be retrieved. Also, if private data is to be maintained - * in this manner the key should be a java.lang.Object - * whose reference is not divulged to untrusted code. + * + *

Since the Context is associated with a thread it can be used to maintain values that can + * be later retrieved using the current thread. + * + *

Note that the values are maintained with the Context, so if the Context is disassociated + * from the thread the values cannot be retrieved. Also, if private data is to be maintained in + * this manner the key should be a java.lang.Object whose reference is not divulged to untrusted + * code. + * * @param key the key used to lookup the value * @return a value previously stored using putThreadLocal. */ - public final Object getThreadLocal(Object key) - { - if (threadLocalMap == null) - return null; + public final Object getThreadLocal(Object key) { + if (threadLocalMap == null) return null; return threadLocalMap.get(key); } /** * Put a value that can later be retrieved using a given key. + * *

+ * * @param key the key used to index the value * @param value the value to save */ - public synchronized final void putThreadLocal(Object key, Object value) - { + public final synchronized void putThreadLocal(Object key, Object value) { if (sealed) onSealedMutation(); - if (threadLocalMap == null) - threadLocalMap = new HashMap(); + if (threadLocalMap == null) threadLocalMap = new HashMap(); threadLocalMap.put(key, value); } /** * Remove values from thread-local storage. + * * @param key the key for the entry to remove. * @since 1.5 release 2 */ - public final void removeThreadLocal(Object key) - { + public final void removeThreadLocal(Object key) { if (sealed) onSealedMutation(); - if (threadLocalMap == null) - return; + if (threadLocalMap == null) return; threadLocalMap.remove(key); } @@ -2207,20 +1980,18 @@ public final void removeThreadLocal(Object key) * @see ClassCache#setCachingEnabled(boolean) */ @Deprecated - public static void setCachingEnabled(boolean cachingEnabled) - { - } + public static void setCachingEnabled(boolean cachingEnabled) {} /** * Set a WrapFactory for this Context. - *

- * The WrapFactory allows custom object wrapping behavior for - * Java object manipulated with JavaScript. + * + *

The WrapFactory allows custom object wrapping behavior for Java object manipulated with + * JavaScript. + * * @see WrapFactory * @since 1.5 Release 4 */ - public final void setWrapFactory(WrapFactory wrapFactory) - { + public final void setWrapFactory(WrapFactory wrapFactory) { if (sealed) onSealedMutation(); if (wrapFactory == null) throw new IllegalArgumentException(); this.wrapFactory = wrapFactory; @@ -2228,11 +1999,11 @@ public final void setWrapFactory(WrapFactory wrapFactory) /** * Return the current WrapFactory, or null if none is defined. + * * @see WrapFactory * @since 1.5 Release 4 */ - public final WrapFactory getWrapFactory() - { + public final WrapFactory getWrapFactory() { if (wrapFactory == null) { wrapFactory = new WrapFactory(); } @@ -2241,58 +2012,53 @@ public final WrapFactory getWrapFactory() /** * Return the current debugger. + * * @return the debugger, or null if none is attached. */ - public final Debugger getDebugger() - { + public final Debugger getDebugger() { return debugger; } /** * Return the debugger context data associated with current context. + * * @return the debugger data, or null if debugger is not attached */ - public final Object getDebuggerContextData() - { + public final Object getDebuggerContextData() { return debuggerData; } /** * Set the associated debugger. - * @param debugger the debugger to be used on callbacks from - * the engine. - * @param contextData arbitrary object that debugger can use to store - * per Context data. + * + * @param debugger the debugger to be used on callbacks from the engine. + * @param contextData arbitrary object that debugger can use to store per Context data. */ - public final void setDebugger(Debugger debugger, Object contextData) - { + public final void setDebugger(Debugger debugger, Object contextData) { if (sealed) onSealedMutation(); this.debugger = debugger; debuggerData = contextData; } /** - * Return DebuggableScript instance if any associated with the script. - * If callable supports DebuggableScript implementation, the method - * returns it. Otherwise null is returned. + * Return DebuggableScript instance if any associated with the script. If callable supports + * DebuggableScript implementation, the method returns it. Otherwise null is returned. */ - public static DebuggableScript getDebuggableView(Script script) - { + public static DebuggableScript getDebuggableView(Script script) { if (script instanceof NativeFunction) { - return ((NativeFunction)script).getDebuggableView(); + return ((NativeFunction) script).getDebuggableView(); } return null; } /** - * Controls certain aspects of script semantics. - * Should be overwritten to alter default behavior. - *

- * The default implementation calls - * {@link ContextFactory#hasFeature(Context cx, int featureIndex)} - * that allows to customize Context behavior without introducing - * Context subclasses. {@link ContextFactory} documentation gives - * an example of hasFeature implementation. + * Controls certain aspects of script semantics. Should be overwritten to alter default + * behavior. + * + *

The default implementation calls {@link ContextFactory#hasFeature(Context cx, int + * featureIndex)} that allows to customize Context behavior without introducing Context + * subclasses. {@link ContextFactory} documentation gives an example of hasFeature + * implementation. * * @param featureIndex feature index to check * @return true if the featureIndex feature is turned on @@ -2310,57 +2076,49 @@ public static DebuggableScript getDebuggableView(Script script) * @see #FEATURE_WARNING_AS_ERROR * @see #FEATURE_ENHANCED_JAVA_ACCESS */ - public boolean hasFeature(int featureIndex) - { + public boolean hasFeature(int featureIndex) { ContextFactory f = getFactory(); return f.hasFeature(this, featureIndex); } /** - * Returns an object which specifies an E4X implementation to use within - * this Context. Note that the XMLLib.Factory interface should - * be considered experimental. + * Returns an object which specifies an E4X implementation to use within this Context + * . Note that the XMLLib.Factory interface should be considered experimental. * - * The default implementation uses the implementation provided by this - * Context's {@link ContextFactory}. + *

The default implementation uses the implementation provided by this Context's + * {@link ContextFactory}. * - * @return An XMLLib.Factory. Should not return null if - * {@link #FEATURE_E4X} is enabled. See {@link #hasFeature}. + * @return An XMLLib.Factory. Should not return null if {@link #FEATURE_E4X} is + * enabled. See {@link #hasFeature}. */ public XMLLib.Factory getE4xImplementationFactory() { return getFactory().getE4xImplementationFactory(); } /** - * Get threshold of executed instructions counter that triggers call to - * observeInstructionCount(). - * When the threshold is zero, instruction counting is disabled, - * otherwise each time the run-time executes at least the threshold value - * of script instructions, observeInstructionCount() will - * be called. + * Get threshold of executed instructions counter that triggers call to + * observeInstructionCount(). When the threshold is zero, instruction counting is + * disabled, otherwise each time the run-time executes at least the threshold value of script + * instructions, observeInstructionCount() will be called. */ - public final int getInstructionObserverThreshold() - { + public final int getInstructionObserverThreshold() { return instructionThreshold; } /** - * Set threshold of executed instructions counter that triggers call to - * observeInstructionCount(). - * When the threshold is zero, instruction counting is disabled, - * otherwise each time the run-time executes at least the threshold value - * of script instructions, observeInstructionCount() will - * be called.
- * Note that the meaning of "instruction" is not guaranteed to be - * consistent between compiled and interpretive modes: executing a given - * script or function in the different modes will result in different - * instruction counts against the threshold. - * {@link #setGenerateObserverCount} is called with true if - * threshold is greater than zero, false otherwise. + * Set threshold of executed instructions counter that triggers call to + * observeInstructionCount(). When the threshold is zero, instruction counting is + * disabled, otherwise each time the run-time executes at least the threshold value of script + * instructions, observeInstructionCount() will be called.
+ * Note that the meaning of "instruction" is not guaranteed to be consistent between compiled + * and interpretive modes: executing a given script or function in the different modes will + * result in different instruction counts against the threshold. {@link + * #setGenerateObserverCount} is called with true if threshold is greater than + * zero, false otherwise. + * * @param threshold The instruction threshold */ - public final void setInstructionObserverThreshold(int threshold) - { + public final void setInstructionObserverThreshold(int threshold) { if (sealed) onSealedMutation(); if (threshold < 0) throw new IllegalArgumentException(); instructionThreshold = threshold; @@ -2368,67 +2126,55 @@ public final void setInstructionObserverThreshold(int threshold) } /** - * Turn on or off generation of code with callbacks to - * track the count of executed instructions. - * Currently only affects JVM byte code generation: this slows down the - * generated code, but code generated without the callbacks will not - * be counted toward instruction thresholds. Rhino's interpretive - * mode does instruction counting without inserting callbacks, so - * there is no requirement to compile code differently. - * @param generateObserverCount if true, generated code will contain - * calls to accumulate an estimate of the instructions executed. + * Turn on or off generation of code with callbacks to track the count of executed instructions. + * Currently only affects JVM byte code generation: this slows down the generated code, but code + * generated without the callbacks will not be counted toward instruction thresholds. Rhino's + * interpretive mode does instruction counting without inserting callbacks, so there is no + * requirement to compile code differently. + * + * @param generateObserverCount if true, generated code will contain calls to accumulate an + * estimate of the instructions executed. */ public void setGenerateObserverCount(boolean generateObserverCount) { this.generateObserverCount = generateObserverCount; } /** - * Allow application to monitor counter of executed script instructions - * in Context subclasses. - * Run-time calls this when instruction counting is enabled and the counter - * reaches limit set by setInstructionObserverThreshold(). - * The method is useful to observe long running scripts and if necessary - * to terminate them. - *

- * The default implementation calls - * {@link ContextFactory#observeInstructionCount(Context cx, - * int instructionCount)} - * that allows to customize Context behavior without introducing - * Context subclasses. - * - * @param instructionCount amount of script instruction executed since - * last call to observeInstructionCount + * Allow application to monitor counter of executed script instructions in Context subclasses. + * Run-time calls this when instruction counting is enabled and the counter reaches limit set by + * setInstructionObserverThreshold(). The method is useful to observe long running + * scripts and if necessary to terminate them. + * + *

The default implementation calls {@link ContextFactory#observeInstructionCount(Context cx, + * int instructionCount)} that allows to customize Context behavior without introducing Context + * subclasses. + * + * @param instructionCount amount of script instruction executed since last call to + * observeInstructionCount * @throws Error to terminate the script * @see #setOptimizationLevel(int) */ - protected void observeInstructionCount(int instructionCount) - { + protected void observeInstructionCount(int instructionCount) { ContextFactory f = getFactory(); f.observeInstructionCount(this, instructionCount); } /** - * Create class loader for generated classes. - * The method calls {@link ContextFactory#createClassLoader(ClassLoader)} - * using the result of {@link #getFactory()}. + * Create class loader for generated classes. The method calls {@link + * ContextFactory#createClassLoader(ClassLoader)} using the result of {@link #getFactory()}. */ - public GeneratedClassLoader createClassLoader(ClassLoader parent) - { + public GeneratedClassLoader createClassLoader(ClassLoader parent) { ContextFactory f = getFactory(); return f.createClassLoader(parent); } - public final ClassLoader getApplicationClassLoader() - { + public final ClassLoader getApplicationClassLoader() { if (applicationClassLoader == null) { ContextFactory f = getFactory(); ClassLoader loader = f.getApplicationClassLoader(); if (loader == null) { - ClassLoader threadLoader - = Thread.currentThread().getContextClassLoader(); - if (threadLoader != null - && Kit.testIfCanLoadRhinoClasses(threadLoader)) - { + ClassLoader threadLoader = Thread.currentThread().getContextClassLoader(); + if (threadLoader != null && Kit.testIfCanLoadRhinoClasses(threadLoader)) { // Thread.getContextClassLoader is not cached since // its caching prevents it from GC which may lead to // a memory leak and hides updates to @@ -2450,8 +2196,7 @@ public final ClassLoader getApplicationClassLoader() return applicationClassLoader; } - public final void setApplicationClassLoader(ClassLoader loader) - { + public final void setApplicationClassLoader(ClassLoader loader) { if (sealed) onSealedMutation(); if (loader == null) { // restore default behaviour @@ -2459,41 +2204,38 @@ public final void setApplicationClassLoader(ClassLoader loader) return; } if (!Kit.testIfCanLoadRhinoClasses(loader)) { - throw new IllegalArgumentException( - "Loader can not resolve Rhino classes"); + throw new IllegalArgumentException("Loader can not resolve Rhino classes"); } applicationClassLoader = loader; } - /********** end of API **********/ + /** ******** end of API ********* */ - /** - * Internal method that reports an error for missing calls to - * enter(). - */ - static Context getContext() - { + /** Internal method that reports an error for missing calls to enter(). */ + static Context getContext() { Context cx = getCurrentContext(); if (cx == null) { - throw new RuntimeException( - "No Context associated with current Thread"); + throw new RuntimeException("No Context associated with current Thread"); } return cx; } - private Object compileImpl(Scriptable scope, - String sourceString, String sourceName, int lineno, - Object securityDomain, boolean returnFunction, - Evaluator compiler, - ErrorReporter compilationErrorReporter) - throws IOException - { - if(sourceName == null) { + private Object compileImpl( + Scriptable scope, + String sourceString, + String sourceName, + int lineno, + Object securityDomain, + boolean returnFunction, + Evaluator compiler, + ErrorReporter compilationErrorReporter) + throws IOException { + if (sourceName == null) { sourceName = "unnamed script"; } if (securityDomain != null && getSecurityController() == null) { throw new IllegalArgumentException( - "securityDomain should be null if setSecurityController() was never called"); + "securityDomain should be null if setSecurityController() was never called"); } // scope should be given if and only if compiling function @@ -2505,8 +2247,14 @@ private Object compileImpl(Scriptable scope, compilationErrorReporter = compilerEnv.getErrorReporter(); } - ScriptNode tree = parse(sourceString, sourceName, lineno, - compilerEnv, compilationErrorReporter, returnFunction); + ScriptNode tree = + parse( + sourceString, + sourceName, + lineno, + compilerEnv, + compilationErrorReporter, + returnFunction); Object bytecode; try { @@ -2518,8 +2266,16 @@ private Object compileImpl(Scriptable scope, } catch (ClassFileFormatException e) { // we hit some class file limit, fall back to interpreter or report - // we have to recreate the tree because the compile call might have changed the tree already - tree = parse(sourceString, sourceName, lineno, compilerEnv, compilationErrorReporter, returnFunction); + // we have to recreate the tree because the compile call might have changed the tree + // already + tree = + parse( + sourceString, + sourceName, + lineno, + compilerEnv, + compilationErrorReporter, + returnFunction); compiler = createInterpreter(); bytecode = compiler.compile(compilerEnv, tree, tree.getEncodedSource(), returnFunction); @@ -2528,7 +2284,7 @@ private Object compileImpl(Scriptable scope, if (debugger != null) { if (sourceString == null) Kit.codeBug(); if (bytecode instanceof DebuggableScript) { - DebuggableScript dscript = (DebuggableScript)bytecode; + DebuggableScript dscript = (DebuggableScript) bytecode; notifyDebugger_r(this, dscript, sourceString); } else { throw new RuntimeException("NOT SUPPORTED"); @@ -2545,9 +2301,14 @@ private Object compileImpl(Scriptable scope, return result; } - private ScriptNode parse(String sourceString, String sourceName, int lineno, - CompilerEnvirons compilerEnv, ErrorReporter compilationErrorReporter, - boolean returnFunction) throws IOException { + private ScriptNode parse( + String sourceString, + String sourceName, + int lineno, + CompilerEnvirons compilerEnv, + ErrorReporter compilationErrorReporter, + boolean returnFunction) + throws IOException { Parser p = new Parser(compilerEnv, compilationErrorReporter); if (returnFunction) { p.calledByCompileFunction = true; @@ -2559,14 +2320,13 @@ private ScriptNode parse(String sourceString, String sourceName, int lineno, AstRoot ast = p.parse(sourceString, sourceName, lineno); if (returnFunction) { // parser no longer adds function to script node - if (!(ast.getFirstChild() != null - && ast.getFirstChild().getType() == Token.FUNCTION)) - { + if (!(ast.getFirstChild() != null && ast.getFirstChild().getType() == Token.FUNCTION)) { // XXX: the check just looks for the first child // and allows for more nodes after it for compatibility // with sources like function() {};;; throw new IllegalArgumentException( - "compileFunction only accepts source with single JS function: "+sourceString); + "compileFunction only accepts source with single JS function: " + + sourceString); } } @@ -2575,25 +2335,22 @@ private ScriptNode parse(String sourceString, String sourceName, int lineno, return tree; } - private static void notifyDebugger_r(Context cx, DebuggableScript dscript, - String debugSource) - { + private static void notifyDebugger_r(Context cx, DebuggableScript dscript, String debugSource) { cx.debugger.handleCompilationDone(cx, dscript, debugSource); for (int i = 0; i != dscript.getFunctionCount(); ++i) { notifyDebugger_r(cx, dscript.getFunction(i), debugSource); } } - private static Class codegenClass = Kit.classOrNull( - "org.mozilla.javascript.optimizer.Codegen"); - private static Class interpreterClass = Kit.classOrNull( - "org.mozilla.javascript.Interpreter"); + private static Class codegenClass = + Kit.classOrNull("org.mozilla.javascript.optimizer.Codegen"); + private static Class interpreterClass = + Kit.classOrNull("org.mozilla.javascript.Interpreter"); - private Evaluator createCompiler() - { + private Evaluator createCompiler() { Evaluator result = null; if (optimizationLevel >= 0 && codegenClass != null) { - result = (Evaluator)Kit.newInstanceOrNull(codegenClass); + result = (Evaluator) Kit.newInstanceOrNull(codegenClass); } if (result == null) { result = createInterpreter(); @@ -2601,24 +2358,20 @@ private Evaluator createCompiler() return result; } - static Evaluator createInterpreter() - { - return (Evaluator)Kit.newInstanceOrNull(interpreterClass); + static Evaluator createInterpreter() { + return (Evaluator) Kit.newInstanceOrNull(interpreterClass); } - static String getSourcePositionFromStack(int[] linep) - { + static String getSourcePositionFromStack(int[] linep) { Context cx = getCurrentContext(); - if (cx == null) - return null; + if (cx == null) return null; if (cx.lastInterpreterFrame != null) { Evaluator evaluator = createInterpreter(); - if (evaluator != null) - return evaluator.getSourcePositionFromStack(cx, linep); + if (evaluator != null) return evaluator.getSourcePositionFromStack(cx, linep); } /** - * A bit of a hack, but the only way to get filename and line - * number from an enclosing frame. + * A bit of a hack, but the only way to get filename and line number from an enclosing + * frame. */ StackTraceElement[] stackTrace = new Throwable().getStackTrace(); for (StackTraceElement st : stackTrace) { @@ -2635,26 +2388,22 @@ static String getSourcePositionFromStack(int[] linep) return null; } - RegExpProxy getRegExpProxy() - { + RegExpProxy getRegExpProxy() { if (regExpProxy == null) { - Class cl = Kit.classOrNull( - "org.mozilla.javascript.regexp.RegExpImpl"); + Class cl = Kit.classOrNull("org.mozilla.javascript.regexp.RegExpImpl"); if (cl != null) { - regExpProxy = (RegExpProxy)Kit.newInstanceOrNull(cl); + regExpProxy = (RegExpProxy) Kit.newInstanceOrNull(cl); } } return regExpProxy; } - final boolean isVersionECMA1() - { + final boolean isVersionECMA1() { return version == VERSION_DEFAULT || version >= VERSION_1_3; } -// The method must NOT be public or protected - SecurityController getSecurityController() - { + // The method must NOT be public or protected + SecurityController getSecurityController() { SecurityController global = SecurityController.global(); if (global != null) { return global; @@ -2662,53 +2411,47 @@ SecurityController getSecurityController() return securityController; } - public final boolean isGeneratingDebugChanged() - { + public final boolean isGeneratingDebugChanged() { return generatingDebugChanged; } /** - * Add a name to the list of names forcing the creation of real - * activation objects for functions. + * Add a name to the list of names forcing the creation of real activation objects for + * functions. * * @param name the name of the object to add to the list */ - public void addActivationName(String name) - { + public void addActivationName(String name) { if (sealed) onSealedMutation(); - if (activationNames == null) - activationNames = new HashSet(); + if (activationNames == null) activationNames = new HashSet(); activationNames.add(name); } /** - * Check whether the name is in the list of names of objects - * forcing the creation of activation objects. + * Check whether the name is in the list of names of objects forcing the creation of activation + * objects. * * @param name the name of the object to test - * * @return true if an function activation object is needed. */ - public final boolean isActivationNeeded(String name) - { + public final boolean isActivationNeeded(String name) { return activationNames != null && activationNames.contains(name); } /** - * Remove a name from the list of names forcing the creation of real - * activation objects for functions. + * Remove a name from the list of names forcing the creation of real activation objects for + * functions. * * @param name the name of the object to remove from the list */ - public void removeActivationName(String name) - { + public void removeActivationName(String name) { if (sealed) onSealedMutation(); - if (activationNames != null) - activationNames.remove(name); + if (activationNames != null) activationNames.remove(name); } public final boolean isStrictMode() { - return isTopLevelStrict || (currentActivationCall != null && currentActivationCall.isStrict); + return isTopLevelStrict + || (currentActivationCall != null && currentActivationCall.isStrict); } private final ContextFactory factory; @@ -2738,7 +2481,7 @@ public final boolean isStrictMode() { private Locale locale; private boolean generatingDebug; private boolean generatingDebugChanged; - private boolean generatingSource=true; + private boolean generatingSource = true; boolean useDynamicScope; private int optimizationLevel; private int maximumInterpreterStackDepth; @@ -2747,13 +2490,10 @@ public final boolean isStrictMode() { private Object debuggerData; private int enterCount; private Object propertyListeners; - private Map threadLocalMap; + private Map threadLocalMap; private ClassLoader applicationClassLoader; - /** - * This is the list of names of objects forcing the creation of - * function activation records. - */ + /** This is the list of names of objects forcing the creation of function activation records. */ Set activationNames; // For the interpreter to store the last frame for error reports etc. diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index e819ee6ffc..26cebeb7e8 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -12,20 +12,17 @@ import java.io.Serializable; /** - * Base class for native object implementation that uses IdFunctionObject to - * export its methods to script via <class-name>.prototype object. + * Base class for native object implementation that uses IdFunctionObject to export its methods to + * script via <class-name>.prototype object. * - * Any descendant should implement at least the following methods: - * findInstanceIdInfo getInstanceIdName execIdCall methodArity + *

Any descendant should implement at least the following methods: findInstanceIdInfo + * getInstanceIdName execIdCall methodArity * - * To define non-function properties, the descendant should override - * getInstanceIdValue setInstanceIdValue to get/set property value and provide - * its default attributes. - * - * - * To customize initialization of constructor and prototype objects, descendant - * may override scopeInit or fillConstructorProperties methods. + *

To define non-function properties, the descendant should override getInstanceIdValue + * setInstanceIdValue to get/set property value and provide its default attributes. * + *

To customize initialization of constructor and prototype objects, descendant may override + * scopeInit or fillConstructorProperties methods. */ public abstract class IdScriptableObject extends ScriptableObject implements IdFunctionCall { private static final long serialVersionUID = -3744239272168621609L; @@ -48,73 +45,60 @@ private static final class PrototypeValues implements Serializable { private IdFunctionObject constructor; private short constructorAttrs; - PrototypeValues(IdScriptableObject obj, int maxId) - { + PrototypeValues(IdScriptableObject obj, int maxId) { if (obj == null) throw new IllegalArgumentException(); if (maxId < 1) throw new IllegalArgumentException(); this.obj = obj; this.maxId = maxId; } - final int getMaxId() - { + final int getMaxId() { return maxId; } - final void initValue(int id, String name, Object value, int attributes) - { - if (!(1 <= id && id <= maxId)) - throw new IllegalArgumentException(); - if (name == null) - throw new IllegalArgumentException(); - if (value == NOT_FOUND) - throw new IllegalArgumentException(); + final void initValue(int id, String name, Object value, int attributes) { + if (!(1 <= id && id <= maxId)) throw new IllegalArgumentException(); + if (name == null) throw new IllegalArgumentException(); + if (value == NOT_FOUND) throw new IllegalArgumentException(); ScriptableObject.checkValidAttributes(attributes); - if (obj.findPrototypeId(name) != id) - throw new IllegalArgumentException(name); + if (obj.findPrototypeId(name) != id) throw new IllegalArgumentException(name); if (id == constructorId) { if (!(value instanceof IdFunctionObject)) { - throw new IllegalArgumentException("consructor should be initialized with IdFunctionObject"); + throw new IllegalArgumentException( + "consructor should be initialized with IdFunctionObject"); } - constructor = (IdFunctionObject)value; - constructorAttrs = (short)attributes; + constructor = (IdFunctionObject) value; + constructorAttrs = (short) attributes; return; } initSlot(id, name, value, attributes); } - final void initValue(int id, Symbol key, Object value, int attributes) - { - if (!(1 <= id && id <= maxId)) - throw new IllegalArgumentException(); - if (key == null) - throw new IllegalArgumentException(); - if (value == NOT_FOUND) - throw new IllegalArgumentException(); + final void initValue(int id, Symbol key, Object value, int attributes) { + if (!(1 <= id && id <= maxId)) throw new IllegalArgumentException(); + if (key == null) throw new IllegalArgumentException(); + if (value == NOT_FOUND) throw new IllegalArgumentException(); ScriptableObject.checkValidAttributes(attributes); - if (obj.findPrototypeId(key) != id) - throw new IllegalArgumentException(key.toString()); + if (obj.findPrototypeId(key) != id) throw new IllegalArgumentException(key.toString()); if (id == constructorId) { if (!(value instanceof IdFunctionObject)) { - throw new IllegalArgumentException("consructor should be initialized with IdFunctionObject"); + throw new IllegalArgumentException( + "consructor should be initialized with IdFunctionObject"); } - constructor = (IdFunctionObject)value; - constructorAttrs = (short)attributes; + constructor = (IdFunctionObject) value; + constructorAttrs = (short) attributes; return; } initSlot(id, key, value, attributes); } - private void initSlot(int id, Object name, Object value, - int attributes) - { + private void initSlot(int id, Object name, Object value, int attributes) { Object[] array = valueArray; - if (array == null) - throw new IllegalStateException(); + if (array == null) throw new IllegalStateException(); if (value == null) { value = UniqueTag.NULL_VALUE; @@ -125,52 +109,47 @@ private void initSlot(int id, Object name, Object value, if (value2 == null) { array[index] = value; array[index + NAME_SLOT] = name; - attributeArray[id - 1] = (short)attributes; + attributeArray[id - 1] = (short) attributes; } else { - if (!name.equals(array[index + NAME_SLOT])) - throw new IllegalStateException(); + if (!name.equals(array[index + NAME_SLOT])) throw new IllegalStateException(); } } } - final IdFunctionObject createPrecachedConstructor() - { + final IdFunctionObject createPrecachedConstructor() { if (constructorId != 0) throw new IllegalStateException(); constructorId = obj.findPrototypeId("constructor"); if (constructorId == 0) { - throw new IllegalStateException( - "No id for constructor property"); + throw new IllegalStateException("No id for constructor property"); } obj.initPrototypeId(constructorId); if (constructor == null) { throw new IllegalStateException( - obj.getClass().getName()+".initPrototypeId() did not " - +"initialize id="+constructorId); + obj.getClass().getName() + + ".initPrototypeId() did not " + + "initialize id=" + + constructorId); } - constructor.initFunction(obj.getClassName(), - ScriptableObject.getTopLevelScope(obj)); + constructor.initFunction(obj.getClassName(), ScriptableObject.getTopLevelScope(obj)); constructor.markAsConstructor(obj); return constructor; } - final int findId(String name) - { + final int findId(String name) { return obj.findPrototypeId(name); } - final int findId(Symbol key) - { + final int findId(Symbol key) { return obj.findPrototypeId(key); } - final boolean has(int id) - { + final boolean has(int id) { Object[] array = valueArray; if (array == null) { // Not yet initialized, assume all exists return true; } - int valueSlot = (id - 1) * SLOT_SPAN; + int valueSlot = (id - 1) * SLOT_SPAN; Object value = array[valueSlot]; if (value == null) { // The particular entry has not been yet initialized @@ -179,8 +158,7 @@ final boolean has(int id) return value != NOT_FOUND; } - final Object get(int id) - { + final Object get(int id) { Object value = ensureId(id); if (value == UniqueTag.NULL_VALUE) { value = null; @@ -188,8 +166,7 @@ final Object get(int id) return value; } - final void set(int id, Scriptable start, Object value) - { + final void set(int id, Scriptable start, Object value) { if (value == NOT_FOUND) throw new IllegalArgumentException(); ensureId(id); int attr = attributeArray[id - 1]; @@ -198,39 +175,38 @@ final void set(int id, Scriptable start, Object value) if (value == null) { value = UniqueTag.NULL_VALUE; } - int valueSlot = (id - 1) * SLOT_SPAN; + int valueSlot = (id - 1) * SLOT_SPAN; synchronized (this) { valueArray[valueSlot] = value; } - } - else { - int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + } else { + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; Object name = valueArray[nameSlot]; if (name instanceof Symbol) { if (start instanceof SymbolScriptable) { - ((SymbolScriptable)start).put((Symbol) name, start, value); + ((SymbolScriptable) start).put((Symbol) name, start, value); } } else { - start.put((String)name, start, value); + start.put((String) name, start, value); } } } } - final void delete(int id) - { + final void delete(int id) { ensureId(id); int attr = attributeArray[id - 1]; // non-configurable if ((attr & PERMANENT) != 0) { Context cx = Context.getContext(); if (cx.isStrictMode()) { - int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; - String name = (String)valueArray[nameSlot]; - throw ScriptRuntime.typeErrorById("msg.delete.property.with.configurable.false", name); + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + String name = (String) valueArray[nameSlot]; + throw ScriptRuntime.typeErrorById( + "msg.delete.property.with.configurable.false", name); } } else { - int valueSlot = (id - 1) * SLOT_SPAN; + int valueSlot = (id - 1) * SLOT_SPAN; synchronized (this) { valueArray[valueSlot] = NOT_FOUND; attributeArray[id - 1] = EMPTY; @@ -238,30 +214,27 @@ final void delete(int id) } } - final int getAttributes(int id) - { + final int getAttributes(int id) { ensureId(id); return attributeArray[id - 1]; } - final void setAttributes(int id, int attributes) - { + final void setAttributes(int id, int attributes) { ScriptableObject.checkValidAttributes(attributes); ensureId(id); synchronized (this) { - attributeArray[id - 1] = (short)attributes; + attributeArray[id - 1] = (short) attributes; } } - final Object[] getNames(boolean getAll, boolean getSymbols, Object[] extraEntries) - { + final Object[] getNames(boolean getAll, boolean getSymbols, Object[] extraEntries) { Object[] names = null; int count = 0; for (int id = 1; id <= maxId; ++id) { Object value = ensureId(id); if (getAll || (attributeArray[id - 1] & DONTENUM) == 0) { if (value != NOT_FOUND) { - int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; Object name = valueArray[nameSlot]; if (name instanceof String) { if (names == null) { @@ -295,8 +268,7 @@ final Object[] getNames(boolean getAll, boolean getSymbols, Object[] extraEntrie } } - private Object ensureId(int id) - { + private Object ensureId(int id) { Object[] array = valueArray; if (array == null) { synchronized (this) { @@ -308,12 +280,11 @@ private Object ensureId(int id) } } } - int valueSlot = (id - 1) * SLOT_SPAN; + int valueSlot = (id - 1) * SLOT_SPAN; Object value = array[valueSlot]; if (value == null) { if (id == constructorId) { - initSlot(constructorId, "constructor", - constructor, constructorAttrs); + initSlot(constructorId, "constructor", constructor, constructorAttrs); constructor = null; // no need to refer it any longer } else { obj.initPrototypeId(id); @@ -321,41 +292,36 @@ private Object ensureId(int id) value = array[valueSlot]; if (value == null) { throw new IllegalStateException( - obj.getClass().getName()+".initPrototypeId(int id) " - +"did not initialize id="+id); + obj.getClass().getName() + + ".initPrototypeId(int id) " + + "did not initialize id=" + + id); } } return value; } } - public IdScriptableObject() - { - } + public IdScriptableObject() {} - public IdScriptableObject(Scriptable scope, Scriptable prototype) - { + public IdScriptableObject(Scriptable scope, Scriptable prototype) { super(scope, prototype); } - protected final boolean defaultHas(String name) - { + protected final boolean defaultHas(String name) { return super.has(name, this); } - protected final Object defaultGet(String name) - { + protected final Object defaultGet(String name) { return super.get(name, this); } - protected final void defaultPut(String name, Object value) - { + protected final void defaultPut(String name, Object value) { super.put(name, this, value); } @Override - public boolean has(String name, Scriptable start) - { + public boolean has(String name, Scriptable start) { int info = findInstanceIdInfo(name); if (info != 0) { int attr = (info >>> 16); @@ -374,10 +340,8 @@ public boolean has(String name, Scriptable start) return super.has(name, start); } - @Override - public boolean has(Symbol key, Scriptable start) - { + public boolean has(Symbol key, Scriptable start) { int info = findInstanceIdInfo(key); if (info != 0) { int attr = (info >>> 16); @@ -397,8 +361,7 @@ public boolean has(Symbol key, Scriptable start) } @Override - public Object get(String name, Scriptable start) - { + public Object get(String name, Scriptable start) { // Check for slot first for performance. This is a very hot code // path that should be further optimized. Object value = super.get(name, start); @@ -422,8 +385,7 @@ public Object get(String name, Scriptable start) } @Override - public Object get(Symbol key, Scriptable start) - { + public Object get(Symbol key, Scriptable start) { Object value = super.get(key, start); if (value != NOT_FOUND) { return value; @@ -445,21 +407,18 @@ public Object get(Symbol key, Scriptable start) } @Override - public void put(String name, Scriptable start, Object value) - { + public void put(String name, Scriptable start, Object value) { int info = findInstanceIdInfo(name); if (info != 0) { if (start == this && isSealed()) { - throw Context.reportRuntimeErrorById("msg.modify.sealed", - name); + throw Context.reportRuntimeErrorById("msg.modify.sealed", name); } int attr = (info >>> 16); if ((attr & READONLY) == 0) { if (start == this) { int id = (info & 0xFFFF); setInstanceIdValue(id, value); - } - else { + } else { start.put(name, start, value); } } @@ -469,8 +428,7 @@ public void put(String name, Scriptable start, Object value) int id = prototypeValues.findId(name); if (id != 0) { if (start == this && isSealed()) { - throw Context.reportRuntimeErrorById("msg.modify.sealed", - name); + throw Context.reportRuntimeErrorById("msg.modify.sealed", name); } prototypeValues.set(id, start, value); return; @@ -480,8 +438,7 @@ public void put(String name, Scriptable start, Object value) } @Override - public void put(Symbol key, Scriptable start, Object value) - { + public void put(Symbol key, Scriptable start, Object value) { int info = findInstanceIdInfo(key); if (info != 0) { if (start == this && isSealed()) { @@ -492,8 +449,7 @@ public void put(Symbol key, Scriptable start, Object value) if (start == this) { int id = (info & 0xFFFF); setInstanceIdValue(id, value); - } - else { + } else { ensureSymbolScriptable(start).put(key, start, value); } } @@ -513,8 +469,7 @@ public void put(Symbol key, Scriptable start, Object value) } @Override - public void delete(String name) - { + public void delete(String name) { int info = findInstanceIdInfo(name); if (info != 0) { // Let the super class to throw exceptions for sealed objects @@ -524,7 +479,8 @@ public void delete(String name) if ((attr & PERMANENT) != 0) { Context cx = Context.getContext(); if (cx.isStrictMode()) { - throw ScriptRuntime.typeErrorById("msg.delete.property.with.configurable.false", name); + throw ScriptRuntime.typeErrorById( + "msg.delete.property.with.configurable.false", name); } } else { int id = (info & 0xFFFF); @@ -546,8 +502,7 @@ public void delete(String name) } @Override - public void delete(Symbol key) - { + public void delete(Symbol key) { int info = findInstanceIdInfo(key); if (info != 0) { // Let the super class to throw exceptions for sealed objects @@ -557,7 +512,8 @@ public void delete(Symbol key) if ((attr & PERMANENT) != 0) { Context cx = Context.getContext(); if (cx.isStrictMode()) { - throw ScriptRuntime.typeErrorById("msg.delete.property.with.configurable.false"); + throw ScriptRuntime.typeErrorById( + "msg.delete.property.with.configurable.false"); } } else { int id = (info & 0xFFFF); @@ -579,8 +535,7 @@ public void delete(Symbol key) } @Override - public int getAttributes(String name) - { + public int getAttributes(String name) { int info = findInstanceIdInfo(name); if (info != 0) { int attr = (info >>> 16); @@ -596,8 +551,7 @@ public int getAttributes(String name) } @Override - public int getAttributes(Symbol key) - { + public int getAttributes(Symbol key) { int info = findInstanceIdInfo(key); if (info != 0) { int attr = (info >>> 16); @@ -613,8 +567,7 @@ public int getAttributes(Symbol key) } @Override - public void setAttributes(String name, int attributes) - { + public void setAttributes(String name, int attributes) { ScriptableObject.checkValidAttributes(attributes); int info = findInstanceIdInfo(name); if (info != 0) { @@ -636,8 +589,7 @@ public void setAttributes(String name, int attributes) } @Override - Object[] getIds(boolean getNonEnumerable, boolean getSymbols) - { + Object[] getIds(boolean getNonEnumerable, boolean getSymbols) { Object[] result = super.getIds(getNonEnumerable, getSymbols); if (prototypeValues != null) { @@ -671,8 +623,7 @@ Object[] getIds(boolean getNonEnumerable, boolean getSymbols) if (count != 0) { if (result.length == 0 && ids.length == count) { result = ids; - } - else { + } else { Object[] tmp = new Object[result.length + count]; System.arraycopy(result, 0, tmp, 0, result.length); System.arraycopy(ids, 0, tmp, result.length, count); @@ -683,93 +634,83 @@ Object[] getIds(boolean getNonEnumerable, boolean getSymbols) return result; } - /** - * Get maximum id findInstanceIdInfo can generate. - */ - protected int getMaxInstanceId() - { + /** Get maximum id findInstanceIdInfo can generate. */ + protected int getMaxInstanceId() { return 0; } - protected static int instanceIdInfo(int attributes, int id) - { + protected static int instanceIdInfo(int attributes, int id) { return (attributes << 16) | id; } /** - * Map name to id of instance property. - * Should return 0 if not found or the result of - * {@link #instanceIdInfo(int, int)}. + * Map name to id of instance property. Should return 0 if not found or the result of {@link + * #instanceIdInfo(int, int)}. */ - protected int findInstanceIdInfo(String name) - { + protected int findInstanceIdInfo(String name) { return 0; } /** - * Map name to id of instance property. - * Should return 0 if not found or the result of - * {@link #instanceIdInfo(int, int)}. + * Map name to id of instance property. Should return 0 if not found or the result of {@link + * #instanceIdInfo(int, int)}. */ - protected int findInstanceIdInfo(Symbol key) - { + protected int findInstanceIdInfo(Symbol key) { return 0; } - /** Map id back to property name it defines. - */ - protected String getInstanceIdName(int id) - { + /** Map id back to property name it defines. */ + protected String getInstanceIdName(int id) { throw new IllegalArgumentException(String.valueOf(id)); } - /** Get id value. - ** If id value is constant, descendant can call cacheIdValue to store - ** value in the permanent cache. - ** Default implementation creates IdFunctionObject instance for given id - ** and cache its value + /** + * Get id value. * If id value is constant, descendant can call cacheIdValue to store * value in + * the permanent cache. * Default implementation creates IdFunctionObject instance for given id + * * and cache its value */ - protected Object getInstanceIdValue(int id) - { + protected Object getInstanceIdValue(int id) { throw new IllegalStateException(String.valueOf(id)); } /** - * Set or delete id value. If value == NOT_FOUND , the implementation - * should make sure that the following getInstanceIdValue return NOT_FOUND. + * Set or delete id value. If value == NOT_FOUND , the implementation should make sure that the + * following getInstanceIdValue return NOT_FOUND. */ - protected void setInstanceIdValue(int id, Object value) - { + protected void setInstanceIdValue(int id, Object value) { throw new IllegalStateException(String.valueOf(id)); } /** - * Update the attributes of the given instance property. Classes which - * want to support changing property attributes via Object.defineProperty - * must override this method. The default implementation throws - * InternalError. + * Update the attributes of the given instance property. Classes which want to support changing + * property attributes via Object.defineProperty must override this method. The default + * implementation throws InternalError. + * * @param id the instance property id * @param attr the new attribute bitset */ protected void setInstanceIdAttributes(int id, int attr) { - throw ScriptRuntime.constructError("InternalError", - "Changing attributes not supported for " + getClassName() - + " " + getInstanceIdName(id) + " property"); + throw ScriptRuntime.constructError( + "InternalError", + "Changing attributes not supported for " + + getClassName() + + " " + + getInstanceIdName(id) + + " property"); } - /** 'thisObj' will be null if invoked as constructor, in which case - ** instance of Scriptable should be returned. */ + /** + * 'thisObj' will be null if invoked as constructor, in which case * instance of Scriptable + * should be returned. + */ @Override - public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) - { + public Object execIdCall( + IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { throw f.unknown(); } - public final IdFunctionObject exportAsJSClass(int maxPrototypeId, - Scriptable scope, - boolean sealed) - { + public final IdFunctionObject exportAsJSClass( + int maxPrototypeId, Scriptable scope, boolean sealed) { // Set scope and prototype unless this is top level scope itself if (scope != this && scope != null) { setParentScope(scope); @@ -789,107 +730,90 @@ public final IdFunctionObject exportAsJSClass(int maxPrototypeId, return ctor; } - public final boolean hasPrototypeMap() - { + public final boolean hasPrototypeMap() { return prototypeValues != null; } - public final void activatePrototypeMap(int maxPrototypeId) - { + public final void activatePrototypeMap(int maxPrototypeId) { PrototypeValues values = new PrototypeValues(this, maxPrototypeId); synchronized (this) { - if (prototypeValues != null) - throw new IllegalStateException(); + if (prototypeValues != null) throw new IllegalStateException(); prototypeValues = values; } } - public final IdFunctionObject initPrototypeMethod(Object tag, int id, String name, - int arity) - { + public final IdFunctionObject initPrototypeMethod(Object tag, int id, String name, int arity) { return initPrototypeMethod(tag, id, name, name, arity); } - public final IdFunctionObject initPrototypeMethod(Object tag, int id, String propertyName, String functionName, - int arity) - { + public final IdFunctionObject initPrototypeMethod( + Object tag, int id, String propertyName, String functionName, int arity) { Scriptable scope = ScriptableObject.getTopLevelScope(this); - IdFunctionObject function = newIdFunction(tag, id, - functionName != null ? functionName : propertyName, - arity, scope); + IdFunctionObject function = + newIdFunction( + tag, id, functionName != null ? functionName : propertyName, arity, scope); prototypeValues.initValue(id, propertyName, function, DONTENUM); return function; } - public final IdFunctionObject initPrototypeMethod(Object tag, int id, Symbol key, String functionName, - int arity) - { + public final IdFunctionObject initPrototypeMethod( + Object tag, int id, Symbol key, String functionName, int arity) { Scriptable scope = ScriptableObject.getTopLevelScope(this); IdFunctionObject function = newIdFunction(tag, id, functionName, arity, scope); prototypeValues.initValue(id, key, function, DONTENUM); return function; } - public final void initPrototypeConstructor(IdFunctionObject f) - { + public final void initPrototypeConstructor(IdFunctionObject f) { int id = prototypeValues.constructorId; - if (id == 0) - throw new IllegalStateException(); - if (f.methodId() != id) - throw new IllegalArgumentException(); - if (isSealed()) { f.sealObject(); } + if (id == 0) throw new IllegalStateException(); + if (f.methodId() != id) throw new IllegalArgumentException(); + if (isSealed()) { + f.sealObject(); + } prototypeValues.initValue(id, "constructor", f, DONTENUM); } - public final void initPrototypeValue(int id, String name, Object value, - int attributes) - { + public final void initPrototypeValue(int id, String name, Object value, int attributes) { prototypeValues.initValue(id, name, value, attributes); } - public final void initPrototypeValue(int id, Symbol key, Object value, - int attributes) - { + public final void initPrototypeValue(int id, Symbol key, Object value, int attributes) { prototypeValues.initValue(id, key, value, attributes); } - protected void initPrototypeId(int id) - { + protected void initPrototypeId(int id) { throw new IllegalStateException(String.valueOf(id)); } - protected int findPrototypeId(String name) - { + protected int findPrototypeId(String name) { throw new IllegalStateException(name); } - protected int findPrototypeId(Symbol key) - { + protected int findPrototypeId(Symbol key) { return 0; } - protected void fillConstructorProperties(IdFunctionObject ctor) - { - } + protected void fillConstructorProperties(IdFunctionObject ctor) {} - protected void addIdFunctionProperty(Scriptable obj, Object tag, int id, - String name, int arity) - { + protected void addIdFunctionProperty( + Scriptable obj, Object tag, int id, String name, int arity) { Scriptable scope = ScriptableObject.getTopLevelScope(obj); IdFunctionObject f = newIdFunction(tag, id, name, arity, scope); f.addAsProperty(obj); } /** - * Utility method to check the type and do the cast or throw an incompatible call - * error. + * Utility method to check the type and do the cast or throw an incompatible call error. * Possible usage would be to have a private function like realThis: + * *

      *  private static NativeSomething realThis(Scriptable thisObj, IdFunctionObject f)
      *  {
      *      return ensureType(thisObj, NativeSomething.class, f);
      * }
      * 
+ * * @param obj the object to check/cast * @param clazz the target type * @param f function that is attempting to convert 'this' object. @@ -897,20 +821,23 @@ protected void addIdFunctionProperty(Scriptable obj, Object tag, int id, * @throws EcmaError if the cast failed. */ @SuppressWarnings("unchecked") - protected static T ensureType(Object obj, Class clazz, IdFunctionObject f) - { + protected static T ensureType(Object obj, Class clazz, IdFunctionObject f) { if (clazz.isInstance(obj)) { return (T) obj; } if (obj == null) { - throw ScriptRuntime.typeErrorById("msg.incompat.call.details", f.getFunctionName(), "null", clazz.getName()); + throw ScriptRuntime.typeErrorById( + "msg.incompat.call.details", f.getFunctionName(), "null", clazz.getName()); } - throw ScriptRuntime.typeErrorById("msg.incompat.call.details", f.getFunctionName(), obj.getClass().getName(), clazz.getName()); + throw ScriptRuntime.typeErrorById( + "msg.incompat.call.details", + f.getFunctionName(), + obj.getClass().getName(), + clazz.getName()); } - private IdFunctionObject newIdFunction(Object tag, int id, String name, - int arity, Scriptable scope) - { + private IdFunctionObject newIdFunction( + Object tag, int id, String name, int arity, Scriptable scope) { IdFunctionObject function = null; if (Context.getContext().getLanguageVersion() < Context.VERSION_ES6) { function = new IdFunctionObject(this, tag, id, name, arity, scope); @@ -918,132 +845,133 @@ private IdFunctionObject newIdFunction(Object tag, int id, String name, function = new IdFunctionObjectES6(this, tag, id, name, arity, scope); } - if (isSealed()) { function.sealObject(); } + if (isSealed()) { + function.sealObject(); + } return function; } @Override public void defineOwnProperty(Context cx, Object key, ScriptableObject desc) { - if (key instanceof String) { - String name = (String) key; - int info = findInstanceIdInfo(name); - if (info != 0) { - int id = (info & 0xFFFF); - if (isAccessorDescriptor(desc)) { - delete(id); // it will be replaced with a slot - } else { - checkPropertyDefinition(desc); - ScriptableObject current = getOwnPropertyDescriptor(cx, key); - checkPropertyChange(name, current, desc); - int attr = (info >>> 16); - Object value = getProperty(desc, "value"); - if (value != NOT_FOUND && (attr & READONLY) == 0) { - Object currentValue = getInstanceIdValue(id); - if (!sameValue(value, currentValue)) { - setInstanceIdValue(id, value); + if (key instanceof String) { + String name = (String) key; + int info = findInstanceIdInfo(name); + if (info != 0) { + int id = (info & 0xFFFF); + if (isAccessorDescriptor(desc)) { + delete(id); // it will be replaced with a slot + } else { + checkPropertyDefinition(desc); + ScriptableObject current = getOwnPropertyDescriptor(cx, key); + checkPropertyChange(name, current, desc); + int attr = (info >>> 16); + Object value = getProperty(desc, "value"); + if (value != NOT_FOUND && (attr & READONLY) == 0) { + Object currentValue = getInstanceIdValue(id); + if (!sameValue(value, currentValue)) { + setInstanceIdValue(id, value); + } + } + setAttributes(name, applyDescriptorToAttributeBitset(attr, desc)); + return; } - } - setAttributes(name, applyDescriptorToAttributeBitset(attr, desc)); - return; } - } - if (prototypeValues != null) { - int id = prototypeValues.findId(name); - if (id != 0) { - if (isAccessorDescriptor(desc)) { - prototypeValues.delete(id); // it will be replaced with a slot - } else { - checkPropertyDefinition(desc); - ScriptableObject current = getOwnPropertyDescriptor(cx, key); - checkPropertyChange(name, current, desc); - int attr = prototypeValues.getAttributes(id); - Object value = getProperty(desc, "value"); - if (value != NOT_FOUND && (attr & READONLY) == 0) { - Object currentValue = prototypeValues.get(id); - if (!sameValue(value, currentValue)) { - prototypeValues.set(id, this, value); - } - } - prototypeValues.setAttributes(id, applyDescriptorToAttributeBitset(attr, desc)); + if (prototypeValues != null) { + int id = prototypeValues.findId(name); + if (id != 0) { + if (isAccessorDescriptor(desc)) { + prototypeValues.delete(id); // it will be replaced with a slot + } else { + checkPropertyDefinition(desc); + ScriptableObject current = getOwnPropertyDescriptor(cx, key); + checkPropertyChange(name, current, desc); + int attr = prototypeValues.getAttributes(id); + Object value = getProperty(desc, "value"); + if (value != NOT_FOUND && (attr & READONLY) == 0) { + Object currentValue = prototypeValues.get(id); + if (!sameValue(value, currentValue)) { + prototypeValues.set(id, this, value); + } + } + prototypeValues.setAttributes( + id, applyDescriptorToAttributeBitset(attr, desc)); + + // Handle the regular slot that was created if this property was previously + // replaced + // with an accessor descriptor. + if (super.has(name, this)) { + super.delete(name); + } - // Handle the regular slot that was created if this property was previously replaced - // with an accessor descriptor. - if (super.has(name, this)) { - super.delete(name); + return; + } } - - return; - } } } - } - super.defineOwnProperty(cx, key, desc); + super.defineOwnProperty(cx, key, desc); } - @Override protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { - ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id); - if (desc == null) { - if (id instanceof String) { - desc = getBuiltInDescriptor((String) id); - } else if (ScriptRuntime.isSymbol(id)) { - desc = getBuiltInDescriptor(((NativeSymbol)id).getKey()); - } - } - return desc; + ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id); + if (desc == null) { + if (id instanceof String) { + desc = getBuiltInDescriptor((String) id); + } else if (ScriptRuntime.isSymbol(id)) { + desc = getBuiltInDescriptor(((NativeSymbol) id).getKey()); + } + } + return desc; } private ScriptableObject getBuiltInDescriptor(String name) { - Object value = null; - int attr = EMPTY; - - Scriptable scope = getParentScope(); - if (scope == null) { - scope = this; - } - - int info = findInstanceIdInfo(name); - if (info != 0) { - int id = (info & 0xFFFF); - value = getInstanceIdValue(id); - attr = (info >>> 16); - return buildDataDescriptor(scope, value, attr); - } - if (prototypeValues != null) { - int id = prototypeValues.findId(name); - if (id != 0) { - value = prototypeValues.get(id); - attr = prototypeValues.getAttributes(id); - return buildDataDescriptor(scope, value, attr); + Object value = null; + int attr = EMPTY; + + Scriptable scope = getParentScope(); + if (scope == null) { + scope = this; } - } - return null; + + int info = findInstanceIdInfo(name); + if (info != 0) { + int id = (info & 0xFFFF); + value = getInstanceIdValue(id); + attr = (info >>> 16); + return buildDataDescriptor(scope, value, attr); + } + if (prototypeValues != null) { + int id = prototypeValues.findId(name); + if (id != 0) { + value = prototypeValues.get(id); + attr = prototypeValues.getAttributes(id); + return buildDataDescriptor(scope, value, attr); + } + } + return null; } private ScriptableObject getBuiltInDescriptor(Symbol key) { - Object value = null; - int attr = EMPTY; - - Scriptable scope = getParentScope(); - if (scope == null) { - scope = this; - } - - if (prototypeValues != null) { - int id = prototypeValues.findId(key); - if (id != 0) { - value = prototypeValues.get(id); - attr = prototypeValues.getAttributes(id); - return buildDataDescriptor(scope, value, attr); + Object value = null; + int attr = EMPTY; + + Scriptable scope = getParentScope(); + if (scope == null) { + scope = this; } - } - return null; + + if (prototypeValues != null) { + int id = prototypeValues.findId(key); + if (id != 0) { + value = prototypeValues.get(id); + attr = prototypeValues.getAttributes(id); + return buildDataDescriptor(scope, value, attr); + } + } + return null; } - private void readObject(ObjectInputStream stream) - throws IOException, ClassNotFoundException - { + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); int maxPrototypeId = stream.readInt(); if (maxPrototypeId != 0) { @@ -1051,9 +979,7 @@ private void readObject(ObjectInputStream stream) } } - private void writeObject(ObjectOutputStream stream) - throws IOException - { + private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); int maxPrototypeId = 0; if (prototypeValues != null) { @@ -1061,6 +987,4 @@ private void writeObject(ObjectOutputStream stream) } stream.writeInt(maxPrototypeId); } - } - diff --git a/src/org/mozilla/javascript/JavaMembers.java b/src/org/mozilla/javascript/JavaMembers.java index b52967db81..29fbfaed5b 100644 --- a/src/org/mozilla/javascript/JavaMembers.java +++ b/src/org/mozilla/javascript/JavaMembers.java @@ -23,42 +23,35 @@ import java.util.Map; /** - * * @author Mike Shaver * @author Norris Boyd * @see NativeJavaObject * @see NativeJavaClass */ -class JavaMembers -{ - JavaMembers(Scriptable scope, Class cl) - { +class JavaMembers { + JavaMembers(Scriptable scope, Class cl) { this(scope, cl, false); } - JavaMembers(Scriptable scope, Class cl, boolean includeProtected) - { + JavaMembers(Scriptable scope, Class cl, boolean includeProtected) { try { Context cx = ContextFactory.getGlobal().enterContext(); ClassShutter shutter = cx.getClassShutter(); if (shutter != null && !shutter.visibleToScripts(cl.getName())) { - throw Context.reportRuntimeErrorById("msg.access.prohibited", - cl.getName()); + throw Context.reportRuntimeErrorById("msg.access.prohibited", cl.getName()); } - this.members = new HashMap(); - this.staticMembers = new HashMap(); + this.members = new HashMap(); + this.staticMembers = new HashMap(); this.cl = cl; - boolean includePrivate = cx.hasFeature( - Context.FEATURE_ENHANCED_JAVA_ACCESS); + boolean includePrivate = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS); reflect(scope, includeProtected, includePrivate); } finally { Context.exit(); } } - boolean has(String name, boolean isStatic) - { - Map ht = isStatic ? staticMembers : members; + boolean has(String name, boolean isStatic) { + Map ht = isStatic ? staticMembers : members; Object obj = ht.get(name); if (obj != null) { return true; @@ -66,20 +59,19 @@ boolean has(String name, boolean isStatic) return findExplicitFunction(name, isStatic) != null; } - Object get(Scriptable scope, String name, Object javaObject, - boolean isStatic) - { - Map ht = isStatic ? staticMembers : members; + Object get(Scriptable scope, String name, Object javaObject, boolean isStatic) { + Map ht = isStatic ? staticMembers : members; Object member = ht.get(name); if (!isStatic && member == null) { // Try to get static member from instance (LC3) member = staticMembers.get(name); } if (member == null) { - member = this.getExplicitFunction(scope, name, - javaObject, isStatic); - if (member == null) - return Scriptable.NOT_FOUND; + member = + this.getExplicitFunction( + scope, name, + javaObject, isStatic); + if (member == null) return Scriptable.NOT_FOUND; } if (member instanceof Scriptable) { return member; @@ -90,8 +82,7 @@ Object get(Scriptable scope, String name, Object javaObject, try { if (member instanceof BeanProperty) { BeanProperty bp = (BeanProperty) member; - if (bp.getter == null) - return Scriptable.NOT_FOUND; + if (bp.getter == null) return Scriptable.NOT_FOUND; rval = bp.getter.invoke(javaObject, Context.emptyArgs); type = bp.getter.method().getGenericReturnType(); } else { @@ -107,17 +98,14 @@ Object get(Scriptable scope, String name, Object javaObject, return cx.getWrapFactory().wrap(cx, scope, rval, type); } - void put(Scriptable scope, String name, Object javaObject, - Object value, boolean isStatic) - { - Map ht = isStatic ? staticMembers : members; + void put(Scriptable scope, String name, Object javaObject, Object value, boolean isStatic) { + Map ht = isStatic ? staticMembers : members; Object member = ht.get(name); if (!isStatic && member == null) { // Try to get static member from instance (LC3) member = staticMembers.get(name); } - if (member == null) - throw reportMemberNotFound(name); + if (member == null) throw reportMemberNotFound(name); if (member instanceof FieldAndMethods) { FieldAndMethods fam = (FieldAndMethods) ht.get(name); member = fam.field; @@ -125,7 +113,7 @@ void put(Scriptable scope, String name, Object javaObject, // Is this a bean property "set"? if (member instanceof BeanProperty) { - BeanProperty bp = (BeanProperty)member; + BeanProperty bp = (BeanProperty) member; if (bp.setter == null) { throw reportMemberNotFound(name); } @@ -134,26 +122,27 @@ void put(Scriptable scope, String name, Object javaObject, // setter to use: if (bp.setters == null || value == null) { Class setType = bp.setter.argTypes[0]; - Object[] args = { Context.jsToJava(value, setType) }; + Object[] args = {Context.jsToJava(value, setType)}; try { bp.setter.invoke(javaObject, args); } catch (Exception ex) { - throw Context.throwAsScriptRuntimeEx(ex); + throw Context.throwAsScriptRuntimeEx(ex); } } else { - Object[] args = { value }; - bp.setters.call(Context.getContext(), - ScriptableObject.getTopLevelScope(scope), - scope, args); + Object[] args = {value}; + bp.setters.call( + Context.getContext(), + ScriptableObject.getTopLevelScope(scope), + scope, + args); } - } - else { + } else { if (!(member instanceof Field)) { - String str = (member == null) ? "msg.java.internal.private" - : "msg.java.method.assign"; + String str = + (member == null) ? "msg.java.internal.private" : "msg.java.method.assign"; throw Context.reportRuntimeErrorById(str, name); } - Field field = (Field)member; + Field field = (Field) member; Object javaValue = Context.jsToJava(value, field.getType()); try { field.set(javaObject, javaValue); @@ -165,21 +154,20 @@ void put(Scriptable scope, String name, Object javaObject, throw Context.throwAsScriptRuntimeEx(accessEx); } catch (IllegalArgumentException argEx) { throw Context.reportRuntimeErrorById( - "msg.java.internal.field.type", - value.getClass().getName(), field, - javaObject.getClass().getName()); + "msg.java.internal.field.type", + value.getClass().getName(), + field, + javaObject.getClass().getName()); } } } - Object[] getIds(boolean isStatic) - { - Map map = isStatic ? staticMembers : members; + Object[] getIds(boolean isStatic) { + Map map = isStatic ? staticMembers : members; return map.keySet().toArray(new Object[map.size()]); } - static String javaSignature(Class type) - { + static String javaSignature(Class type) { if (!type.isArray()) { return type.getName(); } @@ -203,10 +191,11 @@ static String javaSignature(Class type) return sb.toString(); } - static String liveConnectSignature(Class[] argTypes) - { + static String liveConnectSignature(Class[] argTypes) { int N = argTypes.length; - if (N == 0) { return "()"; } + if (N == 0) { + return "()"; + } StringBuilder sb = new StringBuilder(); sb.append('('); for (int i = 0; i != N; ++i) { @@ -219,12 +208,13 @@ static String liveConnectSignature(Class[] argTypes) return sb.toString(); } - private MemberBox findExplicitFunction(String name, boolean isStatic) - { + private MemberBox findExplicitFunction(String name, boolean isStatic) { int sigStart = name.indexOf('('); - if (sigStart < 0) { return null; } + if (sigStart < 0) { + return null; + } - Map ht = isStatic ? staticMembers : members; + Map ht = isStatic ? staticMembers : members; MemberBox[] methodsOrCtors = null; boolean isCtor = (isStatic && sigStart == 0); @@ -233,14 +223,14 @@ private MemberBox findExplicitFunction(String name, boolean isStatic) methodsOrCtors = ctors.methods; } else { // Explicit request for an overloaded method - String trueName = name.substring(0,sigStart); + String trueName = name.substring(0, sigStart); Object obj = ht.get(trueName); if (!isStatic && obj == null) { // Try to get static member from instance (LC3) obj = staticMembers.get(trueName); } if (obj instanceof NativeJavaMethod) { - NativeJavaMethod njm = (NativeJavaMethod)obj; + NativeJavaMethod njm = (NativeJavaMethod) obj; methodsOrCtors = njm.methods; } } @@ -250,8 +240,7 @@ private MemberBox findExplicitFunction(String name, boolean isStatic) Class[] type = methodsOrCtor.argTypes; String sig = liveConnectSignature(type); if (sigStart + sig.length() == name.length() - && name.regionMatches(sigStart, sig, 0, sig.length())) - { + && name.regionMatches(sigStart, sig, 0, sig.length())) { return methodsOrCtor; } } @@ -260,20 +249,17 @@ private MemberBox findExplicitFunction(String name, boolean isStatic) return null; } - private Object getExplicitFunction(Scriptable scope, String name, - Object javaObject, boolean isStatic) - { - Map ht = isStatic ? staticMembers : members; + private Object getExplicitFunction( + Scriptable scope, String name, Object javaObject, boolean isStatic) { + Map ht = isStatic ? staticMembers : members; Object member = null; MemberBox methodOrCtor = findExplicitFunction(name, isStatic); if (methodOrCtor != null) { - Scriptable prototype = - ScriptableObject.getFunctionPrototype(scope); + Scriptable prototype = ScriptableObject.getFunctionPrototype(scope); if (methodOrCtor.isCtor()) { - NativeJavaConstructor fun = - new NativeJavaConstructor(methodOrCtor); + NativeJavaConstructor fun = new NativeJavaConstructor(methodOrCtor); fun.setPrototype(prototype); member = fun; ht.put(name, fun); @@ -281,10 +267,9 @@ private Object getExplicitFunction(Scriptable scope, String name, String trueName = methodOrCtor.getName(); member = ht.get(trueName); - if (member instanceof NativeJavaMethod && - ((NativeJavaMethod)member).methods.length > 1 ) { - NativeJavaMethod fun = - new NativeJavaMethod(methodOrCtor, name); + if (member instanceof NativeJavaMethod + && ((NativeJavaMethod) member).methods.length > 1) { + NativeJavaMethod fun = new NativeJavaMethod(methodOrCtor, name); fun.setPrototype(prototype); ht.put(name, fun); member = fun; @@ -296,25 +281,23 @@ private Object getExplicitFunction(Scriptable scope, String name, } /** - * Retrieves mapping of methods to accessible methods for a class. - * In case the class is not public, retrieves methods with same - * signature as its public methods from public superclasses and - * interfaces (if they exist). Basically upcasts every method to the - * nearest accessible method. + * Retrieves mapping of methods to accessible methods for a class. In case the class is not + * public, retrieves methods with same signature as its public methods from public superclasses + * and interfaces (if they exist). Basically upcasts every method to the nearest accessible + * method. */ - private static Method[] discoverAccessibleMethods(Class clazz, - boolean includeProtected, - boolean includePrivate) - { - Map map = new HashMap(); + private static Method[] discoverAccessibleMethods( + Class clazz, boolean includeProtected, boolean includePrivate) { + Map map = new HashMap(); discoverAccessibleMethods(clazz, map, includeProtected, includePrivate); return map.values().toArray(new Method[map.size()]); } - private static void discoverAccessibleMethods(Class clazz, - Map map, boolean includeProtected, - boolean includePrivate) - { + private static void discoverAccessibleMethods( + Class clazz, + Map map, + boolean includeProtected, + boolean includePrivate) { if (isPublic(clazz.getModifiers()) || includePrivate) { try { if (includeProtected || includePrivate) { @@ -324,9 +307,7 @@ private static void discoverAccessibleMethods(Class clazz, for (Method method : methods) { int mods = method.getModifiers(); - if (isPublic(mods) - || isProtected(mods) - || includePrivate) { + if (isPublic(mods) || isProtected(mods) || includePrivate) { MethodSignature sig = new MethodSignature(method); if (!map.containsKey(sig)) { if (includePrivate && !method.isAccessible()) @@ -337,8 +318,8 @@ private static void discoverAccessibleMethods(Class clazz, } Class[] interfaces = clazz.getInterfaces(); for (Class intface : interfaces) { - discoverAccessibleMethods(intface, map, includeProtected, - includePrivate); + discoverAccessibleMethods( + intface, map, includeProtected, includePrivate); } clazz = clazz.getSuperclass(); } catch (SecurityException e) { @@ -348,11 +329,10 @@ private static void discoverAccessibleMethods(Class clazz, Method[] methods = clazz.getMethods(); for (Method method : methods) { MethodSignature sig = new MethodSignature(method); - if (!map.containsKey(sig)) - map.put(sig, method); + if (!map.containsKey(sig)) map.put(sig, method); } break; // getMethods gets superclass methods, no - // need to loop any more + // need to loop any more } } } else { @@ -360,16 +340,16 @@ private static void discoverAccessibleMethods(Class clazz, for (Method method : methods) { MethodSignature sig = new MethodSignature(method); // Array may contain methods with same signature but different return value! - if (!map.containsKey(sig)) - map.put(sig, method); + if (!map.containsKey(sig)) map.put(sig, method); } } return; } catch (SecurityException e) { Context.reportWarning( - "Could not discover accessible methods of class " + - clazz.getName() + " due to lack of privileges, " + - "attemping superclasses/interfaces."); + "Could not discover accessible methods of class " + + clazz.getName() + + " due to lack of privileges, " + + "attemping superclasses/interfaces."); // Fall through and attempt to discover superclass/interface // methods } @@ -377,64 +357,52 @@ private static void discoverAccessibleMethods(Class clazz, Class[] interfaces = clazz.getInterfaces(); for (Class intface : interfaces) { - discoverAccessibleMethods(intface, map, includeProtected, - includePrivate); + discoverAccessibleMethods(intface, map, includeProtected, includePrivate); } Class superclass = clazz.getSuperclass(); if (superclass != null) { - discoverAccessibleMethods(superclass, map, includeProtected, - includePrivate); + discoverAccessibleMethods(superclass, map, includeProtected, includePrivate); } } - private static final class MethodSignature - { + private static final class MethodSignature { private final String name; private final Class[] args; - private MethodSignature(String name, Class[] args) - { + private MethodSignature(String name, Class[] args) { this.name = name; this.args = args; } - MethodSignature(Method method) - { + MethodSignature(Method method) { this(method.getName(), method.getParameterTypes()); } @Override - public boolean equals(Object o) - { - if(o instanceof MethodSignature) - { - MethodSignature ms = (MethodSignature)o; + public boolean equals(Object o) { + if (o instanceof MethodSignature) { + MethodSignature ms = (MethodSignature) o; return ms.name.equals(name) && Arrays.equals(args, ms.args); } return false; } @Override - public int hashCode() - { + public int hashCode() { return name.hashCode() ^ args.length; } } - private void reflect(Scriptable scope, - boolean includeProtected, - boolean includePrivate) - { + private void reflect(Scriptable scope, boolean includeProtected, boolean includePrivate) { // We reflect methods first, because we want overloaded field/method // names to be allocated to the NativeJavaMethod before the field // gets in the way. - Method[] methods = discoverAccessibleMethods(cl, includeProtected, - includePrivate); + Method[] methods = discoverAccessibleMethods(cl, includeProtected, includePrivate); for (Method method : methods) { int mods = method.getModifiers(); boolean isStatic = Modifier.isStatic(mods); - Map ht = isStatic ? staticMembers : members; + Map ht = isStatic ? staticMembers : members; String name = method.getName(); Object value = ht.get(name); if (value == null) { @@ -442,7 +410,7 @@ private void reflect(Scriptable scope, } else { ObjArray overloadedMethods; if (value instanceof ObjArray) { - overloadedMethods = (ObjArray)value; + overloadedMethods = (ObjArray) value; } else { if (!(value instanceof Method)) Kit.codeBug(); // value should be instance of Method as at this stage @@ -459,20 +427,20 @@ private void reflect(Scriptable scope, // first in staticMembers and then in members for (int tableCursor = 0; tableCursor != 2; ++tableCursor) { boolean isStatic = (tableCursor == 0); - Map ht = isStatic ? staticMembers : members; - for (Map.Entry entry: ht.entrySet()) { + Map ht = isStatic ? staticMembers : members; + for (Map.Entry entry : ht.entrySet()) { MemberBox[] methodBoxes; Object value = entry.getValue(); if (value instanceof Method) { methodBoxes = new MemberBox[1]; - methodBoxes[0] = new MemberBox((Method)value); + methodBoxes[0] = new MemberBox((Method) value); } else { - ObjArray overloadedMethods = (ObjArray)value; + ObjArray overloadedMethods = (ObjArray) value; int N = overloadedMethods.size(); if (N < 2) Kit.codeBug(); methodBoxes = new MemberBox[N]; for (int i = 0; i != N; ++i) { - Method method = (Method)overloadedMethods.get(i); + Method method = (Method) overloadedMethods.get(i); methodBoxes[i] = new MemberBox(method); } } @@ -491,18 +459,17 @@ private void reflect(Scriptable scope, int mods = field.getModifiers(); try { boolean isStatic = Modifier.isStatic(mods); - Map ht = isStatic ? staticMembers : members; + Map ht = isStatic ? staticMembers : members; Object member = ht.get(name); if (member == null) { ht.put(name, field); } else if (member instanceof NativeJavaMethod) { NativeJavaMethod method = (NativeJavaMethod) member; - FieldAndMethods fam - = new FieldAndMethods(scope, method.methods, field); - Map fmht = isStatic ? staticFieldAndMethods - : fieldAndMethods; + FieldAndMethods fam = new FieldAndMethods(scope, method.methods, field); + Map fmht = + isStatic ? staticFieldAndMethods : fieldAndMethods; if (fmht == null) { - fmht = new HashMap(); + fmht = new HashMap(); if (isStatic) { staticFieldAndMethods = fmht; } else { @@ -519,9 +486,7 @@ private void reflect(Scriptable scope, // reflected. // For now, the first field found wins, unless another field // explicitly shadows it. - if (oldField.getDeclaringClass(). - isAssignableFrom(field.getDeclaringClass())) - { + if (oldField.getDeclaringClass().isAssignableFrom(field.getDeclaringClass())) { ht.put(name, field); } } else { @@ -530,9 +495,12 @@ private void reflect(Scriptable scope, } } catch (SecurityException e) { // skip this field - Context.reportWarning("Could not access field " - + name + " of class " + cl.getName() + - " due to lack of privileges."); + Context.reportWarning( + "Could not access field " + + name + + " of class " + + cl.getName() + + " due to lack of privileges."); } } @@ -540,23 +508,20 @@ private void reflect(Scriptable scope, // static members and then for instance members for (int tableCursor = 0; tableCursor != 2; ++tableCursor) { boolean isStatic = (tableCursor == 0); - Map ht = isStatic ? staticMembers : members; + Map ht = isStatic ? staticMembers : members; - Map toAdd = new HashMap(); + Map toAdd = new HashMap(); // Now, For each member, make "bean" properties. - for (String name: ht.keySet()) { + for (String name : ht.keySet()) { // Is this a getter? boolean memberIsGetMethod = name.startsWith("get"); boolean memberIsSetMethod = name.startsWith("set"); boolean memberIsIsMethod = name.startsWith("is"); - if (memberIsGetMethod || memberIsIsMethod - || memberIsSetMethod) { + if (memberIsGetMethod || memberIsIsMethod || memberIsSetMethod) { // Double check name component. - String nameComponent - = name.substring(memberIsIsMethod ? 2 : 3); - if (nameComponent.length() == 0) - continue; + String nameComponent = name.substring(memberIsIsMethod ? 2 : 3); + if (nameComponent.length() == 0) continue; // Make the bean property name. String beanPropertyName = nameComponent; @@ -567,23 +532,22 @@ private void reflect(Scriptable scope, } else { char ch1 = nameComponent.charAt(1); if (!Character.isUpperCase(ch1)) { - beanPropertyName = Character.toLowerCase(ch0) - +nameComponent.substring(1); + beanPropertyName = + Character.toLowerCase(ch0) + nameComponent.substring(1); } } } // If we already have a member by this name, don't do this // property. - if (toAdd.containsKey(beanPropertyName)) - continue; + if (toAdd.containsKey(beanPropertyName)) continue; Object v = ht.get(beanPropertyName); if (v != null) { // A private field shouldn't mask a public getter/setter - if (!includePrivate || !(v instanceof Member) || - !Modifier.isPrivate(((Member)v).getModifiers())) + if (!includePrivate + || !(v instanceof Member) + || !Modifier.isPrivate(((Member) v).getModifiers())) { - { continue; } } @@ -606,17 +570,15 @@ private void reflect(Scriptable scope, // Is this value a method? Object member = ht.get(setterName); if (member instanceof NativeJavaMethod) { - NativeJavaMethod njmSet = (NativeJavaMethod)member; + NativeJavaMethod njmSet = (NativeJavaMethod) member; if (getter != null) { // We have a getter. Now, do we have a matching // setter? Class type = getter.method().getReturnType(); - setter = extractSetMethod(type, njmSet.methods, - isStatic); + setter = extractSetMethod(type, njmSet.methods, isStatic); } else { // No getter, find any set method - setter = extractSetMethod(njmSet.methods, - isStatic); + setter = extractSetMethod(njmSet.methods, isStatic); } if (njmSet.methods.length > 1) { setters = njmSet; @@ -624,8 +586,7 @@ private void reflect(Scriptable scope, } } // Make the property. - BeanProperty bp = new BeanProperty(getter, setter, - setters); + BeanProperty bp = new BeanProperty(getter, setter, setters); toAdd.put(beanPropertyName, bp); } } @@ -639,13 +600,12 @@ private void reflect(Scriptable scope, // 'for each (value in o)' loops if (Map.class.isAssignableFrom(cl)) { // Add Map iterator - members.put(NativeIterator.ITERATOR_PROPERTY_NAME, - NativeIterator.JAVA_MAP_ITERATOR); - + members.put(NativeIterator.ITERATOR_PROPERTY_NAME, NativeIterator.JAVA_MAP_ITERATOR); + } else if (Iterable.class.isAssignableFrom(cl)) { // Add Iterable/Collection iterator - members.put(NativeIterator.ITERATOR_PROPERTY_NAME, - NativeIterator.JAVA_COLLECTION_ITERATOR); + members.put( + NativeIterator.ITERATOR_PROPERTY_NAME, NativeIterator.JAVA_COLLECTION_ITERATOR); // look for size() method and register as length property Object member = members.get("size"); if (member instanceof NativeJavaMethod) { @@ -667,28 +627,28 @@ private void reflect(Scriptable scope, ctors = new NativeJavaMethod(ctorMembers, cl.getSimpleName()); } - private Constructor[] getAccessibleConstructors(boolean includePrivate) - { - // The JVM currently doesn't allow changing access on java.lang.Class - // constructors, so don't try - if (includePrivate && cl != ScriptRuntime.ClassClass) { - try { - Constructor[] cons = cl.getDeclaredConstructors(); - AccessibleObject.setAccessible(cons, true); - - return cons; - } catch (SecurityException e) { - // Fall through to !includePrivate case - Context.reportWarning("Could not access constructor " + - " of class " + cl.getName() + - " due to lack of privileges."); - } - } - return cl.getConstructors(); + private Constructor[] getAccessibleConstructors(boolean includePrivate) { + // The JVM currently doesn't allow changing access on java.lang.Class + // constructors, so don't try + if (includePrivate && cl != ScriptRuntime.ClassClass) { + try { + Constructor[] cons = cl.getDeclaredConstructors(); + AccessibleObject.setAccessible(cons, true); + + return cons; + } catch (SecurityException e) { + // Fall through to !includePrivate case + Context.reportWarning( + "Could not access constructor " + + " of class " + + cl.getName() + + " due to lack of privileges."); + } + } + return cl.getConstructors(); } - private Field[] getAccessibleFields(boolean includeProtected, - boolean includePrivate) { + private Field[] getAccessibleFields(boolean includeProtected, boolean includePrivate) { if (includePrivate || includeProtected) { try { List fieldsList = new ArrayList(); @@ -701,8 +661,7 @@ private Field[] getAccessibleFields(boolean includeProtected, for (Field field : declared) { int mod = field.getModifiers(); if (includePrivate || isPublic(mod) || isProtected(mod)) { - if (!field.isAccessible()) - field.setAccessible(true); + if (!field.isAccessible()) field.setAccessible(true); fieldsList.add(field); } } @@ -719,9 +678,8 @@ private Field[] getAccessibleFields(boolean includeProtected, return cl.getFields(); } - private static MemberBox findGetter(boolean isStatic, Map ht, String prefix, - String propertyName) - { + private static MemberBox findGetter( + boolean isStatic, Map ht, String prefix, String propertyName) { String getterName = prefix.concat(propertyName); if (ht.containsKey(getterName)) { // Check that the getter is a method. @@ -734,9 +692,7 @@ private static MemberBox findGetter(boolean isStatic, Map ht, Str return null; } - private static MemberBox extractGetMethod(MemberBox[] methods, - boolean isStatic) - { + private static MemberBox extractGetMethod(MemberBox[] methods, boolean isStatic) { // Inspect the list of all MemberBox for the only one having no // parameters for (MemberBox method : methods) { @@ -753,9 +709,8 @@ private static MemberBox extractGetMethod(MemberBox[] methods, return null; } - private static MemberBox extractSetMethod(Class type, MemberBox[] methods, - boolean isStatic) - { + private static MemberBox extractSetMethod( + Class type, MemberBox[] methods, boolean isStatic) { // // Note: it may be preferable to allow NativeJavaMethod.findFunction() // to find the appropriate setter; unfortunately, it requires an @@ -786,9 +741,7 @@ private static MemberBox extractSetMethod(Class type, MemberBox[] methods, return null; } - private static MemberBox extractSetMethod(MemberBox[] methods, - boolean isStatic) - { + private static MemberBox extractSetMethod(MemberBox[] methods, boolean isStatic) { for (MemberBox method : methods) { if (!isStatic || method.isStatic()) { @@ -802,32 +755,28 @@ private static MemberBox extractSetMethod(MemberBox[] methods, return null; } - Map getFieldAndMethodsObjects(Scriptable scope, - Object javaObject, boolean isStatic) - { - Map ht = isStatic ? staticFieldAndMethods : fieldAndMethods; - if (ht == null) - return null; + Map getFieldAndMethodsObjects( + Scriptable scope, Object javaObject, boolean isStatic) { + Map ht = isStatic ? staticFieldAndMethods : fieldAndMethods; + if (ht == null) return null; int len = ht.size(); - Map result = new HashMap(len); - for (FieldAndMethods fam: ht.values()) { - FieldAndMethods famNew = new FieldAndMethods(scope, fam.methods, - fam.field); + Map result = new HashMap(len); + for (FieldAndMethods fam : ht.values()) { + FieldAndMethods famNew = new FieldAndMethods(scope, fam.methods, fam.field); famNew.javaObject = javaObject; result.put(fam.field.getName(), famNew); } return result; } - static JavaMembers lookupClass(Scriptable scope, Class dynamicType, - Class staticType, boolean includeProtected) - { + static JavaMembers lookupClass( + Scriptable scope, Class dynamicType, Class staticType, boolean includeProtected) { JavaMembers members; ClassCache cache = ClassCache.get(scope); - Map,JavaMembers> ct = cache.getClassCacheMap(); + Map, JavaMembers> ct = cache.getClassCacheMap(); Class cl = dynamicType; - for (;;) { + for (; ; ) { members = ct.get(cl); if (members != null) { if (cl != dynamicType) { @@ -838,8 +787,7 @@ static JavaMembers lookupClass(Scriptable scope, Class dynamicType, return members; } try { - members = new JavaMembers(cache.getAssociatedScope(), cl, - includeProtected); + members = new JavaMembers(cache.getAssociatedScope(), cl, includeProtected); break; } catch (SecurityException e) { // Reflection may fail for objects that are in a restricted @@ -875,24 +823,21 @@ static JavaMembers lookupClass(Scriptable scope, Class dynamicType, return members; } - RuntimeException reportMemberNotFound(String memberName) - { + RuntimeException reportMemberNotFound(String memberName) { return Context.reportRuntimeErrorById( - "msg.java.member.not.found", cl.getName(), memberName); + "msg.java.member.not.found", cl.getName(), memberName); } private Class cl; - private Map members; - private Map fieldAndMethods; - private Map staticMembers; - private Map staticFieldAndMethods; + private Map members; + private Map fieldAndMethods; + private Map staticMembers; + private Map staticFieldAndMethods; NativeJavaMethod ctors; // we use NativeJavaMethod for ctor overload resolution } -class BeanProperty -{ - BeanProperty(MemberBox getter, MemberBox setter, NativeJavaMethod setters) - { +class BeanProperty { + BeanProperty(MemberBox getter, MemberBox setter, NativeJavaMethod setters) { this.getter = getter; this.setter = setter; this.setters = setters; @@ -903,12 +848,10 @@ class BeanProperty NativeJavaMethod setters; } -class FieldAndMethods extends NativeJavaMethod -{ +class FieldAndMethods extends NativeJavaMethod { private static final long serialVersionUID = -9222428244284796755L; - FieldAndMethods(Scriptable scope, MemberBox[] methods, Field field) - { + FieldAndMethods(Scriptable scope, MemberBox[] methods, Field field) { super(methods); this.field = field; setParentScope(scope); @@ -916,20 +859,17 @@ class FieldAndMethods extends NativeJavaMethod } @Override - public Object getDefaultValue(Class hint) - { - if (hint == ScriptRuntime.FunctionClass) - return this; + public Object getDefaultValue(Class hint) { + if (hint == ScriptRuntime.FunctionClass) return this; Object rval; Type type; try { rval = field.get(javaObject); type = field.getGenericType(); } catch (IllegalAccessException accEx) { - throw Context.reportRuntimeErrorById( - "msg.java.internal.private", field.getName()); + throw Context.reportRuntimeErrorById("msg.java.internal.private", field.getName()); } - Context cx = Context.getContext(); + Context cx = Context.getContext(); rval = cx.getWrapFactory().wrap(cx, this, rval, type); if (rval instanceof Scriptable) { rval = ((Scriptable) rval).getDefaultValue(hint); diff --git a/src/org/mozilla/javascript/NativeIterator.java b/src/org/mozilla/javascript/NativeIterator.java index e6b5521735..8b9e898095 100644 --- a/src/org/mozilla/javascript/NativeIterator.java +++ b/src/org/mozilla/javascript/NativeIterator.java @@ -20,7 +20,7 @@ public final class NativeIterator extends IdScriptableObject { private static final long serialVersionUID = -4136968203581667681L; private static final Object ITERATOR_TAG = "Iterator"; - // Functions are registered as '__iterator__' for Iterables and Maps + // Functions are registered as '__iterator__' for Iterables and Maps public static final BaseFunction JAVA_COLLECTION_ITERATOR = new CollectionIteratorFunction(); public static final BaseFunction JAVA_MAP_ITERATOR = new MapIteratorFunction(); @@ -40,30 +40,28 @@ static void init(Context cx, ScriptableObject scope, boolean sealed) { NativeObject obj = new StopIteration(); obj.setPrototype(getObjectPrototype(scope)); obj.setParentScope(scope); - if (sealed) { obj.sealObject(); } - ScriptableObject.defineProperty(scope, STOP_ITERATION, obj, - ScriptableObject.DONTENUM); + if (sealed) { + obj.sealObject(); + } + ScriptableObject.defineProperty(scope, STOP_ITERATION, obj, ScriptableObject.DONTENUM); // Use "associateValue" so that generators can continue to // throw StopIteration even if the property of the global // scope is replaced or deleted. scope.associateValue(ITERATOR_TAG, obj); } - /** - * Only for constructing the prototype object. - */ - private NativeIterator() { - } + /** Only for constructing the prototype object. */ + private NativeIterator() {} private NativeIterator(Object objectIterator) { - this.objectIterator = objectIterator; + this.objectIterator = objectIterator; } /** - * Get the value of the "StopIteration" object. Note that this value - * is stored in the top-level scope using "associateValue" so the - * value can still be found even if a script overwrites or deletes - * the global "StopIteration" property. + * Get the value of the "StopIteration" object. Note that this value is stored in the top-level + * scope using "associateValue" so the value can still be found even if a script overwrites or + * deletes the global "StopIteration" property. + * * @param scope a scope whose parent chain reaches a top-level scope * @return the StopIteration object */ @@ -114,18 +112,27 @@ protected void initPrototypeId(int id) { String s; int arity; switch (id) { - case Id_constructor: arity=2; s="constructor"; break; - case Id_next: arity=0; s="next"; break; - case Id___iterator__: arity=1; s=ITERATOR_PROPERTY_NAME; break; - default: throw new IllegalArgumentException(String.valueOf(id)); + case Id_constructor: + arity = 2; + s = "constructor"; + break; + case Id_next: + arity = 0; + s = "next"; + break; + case Id___iterator__: + arity = 1; + s = ITERATOR_PROPERTY_NAME; + break; + default: + throw new IllegalArgumentException(String.valueOf(id)); } initPrototypeMethod(ITERATOR_TAG, id, s, arity); } @Override - public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) - { + public Object execIdCall( + IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (!f.hasTag(ITERATOR_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); } @@ -138,29 +145,25 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, NativeIterator iterator = ensureType(thisObj, NativeIterator.class, f); switch (id) { + case Id_next: + return iterator.next(cx, scope); - case Id_next: - return iterator.next(cx, scope); - - case Id___iterator__: - /// XXX: what about argument? SpiderMonkey apparently ignores it - return thisObj; + case Id___iterator__: + /// XXX: what about argument? SpiderMonkey apparently ignores it + return thisObj; - default: - throw new IllegalArgumentException(String.valueOf(id)); + default: + throw new IllegalArgumentException(String.valueOf(id)); } } /* The JavaScript constructor */ - private static Object jsConstructor(Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) - { - if (args.length == 0 || args[0] == null || - args[0] == Undefined.instance) - { + private static Object jsConstructor( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + if (args.length == 0 || args[0] == null || args[0] == Undefined.instance) { Object argument = args.length == 0 ? Undefined.instance : args[0]; - throw ScriptRuntime.typeErrorById("msg.no.properties", - ScriptRuntime.toString(argument)); + throw ScriptRuntime.typeErrorById( + "msg.no.properties", ScriptRuntime.toString(argument)); } Scriptable obj = ScriptRuntime.toObject(cx, scope, args[0]); boolean keyOnly = args.length > 1 && ScriptRuntime.toBoolean(args[1]); @@ -173,14 +176,16 @@ private static Object jsConstructor(Context cx, Scriptable scope, Iterator iterator = getJavaIterator(obj); if (iterator != null) { scope = ScriptableObject.getTopLevelScope(scope); - return cx.getWrapFactory().wrap(cx, scope, - new WrappedJavaIterator(iterator, scope), - WrappedJavaIterator.class); + return cx.getWrapFactory() + .wrap( + cx, + scope, + new WrappedJavaIterator(iterator, scope), + WrappedJavaIterator.class); } // Otherwise, just call the runtime routine - Scriptable jsIterator = ScriptRuntime.toIterator(cx, scope, obj, - keyOnly); + Scriptable jsIterator = ScriptRuntime.toIterator(cx, scope, obj, keyOnly); if (jsIterator != null) { return jsIterator; } @@ -188,13 +193,17 @@ private static Object jsConstructor(Context cx, Scriptable scope, // Otherwise, just set up to iterate over the properties of the object. // Do not call __iterator__ method. - Object objectIterator = ScriptRuntime.enumInit(obj, cx, scope, - keyOnly ? ScriptRuntime.ENUMERATE_KEYS_NO_ITERATOR - : ScriptRuntime.ENUMERATE_ARRAY_NO_ITERATOR); + Object objectIterator = + ScriptRuntime.enumInit( + obj, + cx, + scope, + keyOnly + ? ScriptRuntime.ENUMERATE_KEYS_NO_ITERATOR + : ScriptRuntime.ENUMERATE_ARRAY_NO_ITERATOR); ScriptRuntime.setEnumNumbers(objectIterator, true); NativeIterator result = new NativeIterator(objectIterator); - result.setPrototype(ScriptableObject.getClassPrototype(scope, - result.getClassName())); + result.setPrototype(ScriptableObject.getClassPrototype(scope, result.getClassName())); result.setParentScope(scope); return result; } @@ -203,25 +212,22 @@ private Object next(Context cx, Scriptable scope) { Boolean b = ScriptRuntime.enumNext(this.objectIterator); if (!b.booleanValue()) { // Out of values. Throw StopIteration. - throw new JavaScriptException( - NativeIterator.getStopIterationObject(scope), null, 0); + throw new JavaScriptException(NativeIterator.getStopIterationObject(scope), null, 0); } return ScriptRuntime.enumId(this.objectIterator, cx); } /** - * If "obj" is a java.util.Iterator or a java.lang.Iterable, return a - * wrapping as a JavaScript Iterator. Otherwise, return null. - * This method is in VMBridge since Iterable is a JDK 1.5 addition. + * If "obj" is a java.util.Iterator or a java.lang.Iterable, return a wrapping as a JavaScript + * Iterator. Otherwise, return null. This method is in VMBridge since Iterable is a JDK 1.5 + * addition. */ - static private Iterator getJavaIterator(Object obj) { + private static Iterator getJavaIterator(Object obj) { if (obj instanceof Wrapper) { Object unwrapped = ((Wrapper) obj).unwrap(); Iterator iterator = null; - if (unwrapped instanceof Iterator) - iterator = (Iterator) unwrapped; - if (unwrapped instanceof Iterable) - iterator = ((Iterable)unwrapped).iterator(); + if (unwrapped instanceof Iterator) iterator = (Iterator) unwrapped; + if (unwrapped instanceof Iterable) iterator = ((Iterable) unwrapped).iterator(); return iterator; } return null; @@ -229,28 +235,32 @@ static private Iterator getJavaIterator(Object obj) { static class CollectionIteratorFunction extends BaseFunction { @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, - Object[] args) { + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { Object wrapped = ((NativeJavaObject) thisObj).javaObject; if (Boolean.TRUE.equals(args[0])) { // key only iterator, we will return an iterator // for the sequence of the collection length. int length = ((Collection) wrapped).size(); - return cx.getWrapFactory().wrap(cx, scope, - new SequenceIterator(length, scope), - WrappedJavaIterator.class); + return cx.getWrapFactory() + .wrap( + cx, + scope, + new SequenceIterator(length, scope), + WrappedJavaIterator.class); } else { Iterator iter = ((Iterable) wrapped).iterator(); - return cx.getWrapFactory().wrap(cx, scope, - new WrappedJavaIterator(iter, scope), - WrappedJavaIterator.class); + return cx.getWrapFactory() + .wrap( + cx, + scope, + new WrappedJavaIterator(iter, scope), + WrappedJavaIterator.class); } } } - - static public class SequenceIterator - { + + public static class SequenceIterator { SequenceIterator(int size, Scriptable scope) { this.size = size; this.scope = scope; @@ -260,7 +270,7 @@ public Object next() { if (pos >= size) { // Out of values. Throw StopIteration. throw new JavaScriptException( - NativeIterator.getStopIterationObject(scope), null, 0); + NativeIterator.getStopIterationObject(scope), null, 0); } return pos++; } @@ -273,11 +283,10 @@ public Object __iterator__(boolean b) { private int pos; private Scriptable scope; } - + static class MapIteratorFunction extends BaseFunction { @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, - Object[] args) { + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { Map map = (Map) ((NativeJavaObject) thisObj).javaObject; Iterator iter; @@ -286,14 +295,16 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, } else { iter = map.values().iterator(); } - return cx.getWrapFactory().wrap(cx, scope, - new WrappedJavaIterator(iter, scope), - WrappedJavaIterator.class); + return cx.getWrapFactory() + .wrap( + cx, + scope, + new WrappedJavaIterator(iter, scope), + WrappedJavaIterator.class); } } - - static public class WrappedJavaIterator - { + + public static class WrappedJavaIterator { WrappedJavaIterator(Iterator iterator, Scriptable scope) { this.iterator = iterator; this.scope = scope; @@ -303,7 +314,7 @@ public Object next() { if (!iterator.hasNext()) { // Out of values. Throw StopIteration. throw new JavaScriptException( - NativeIterator.getStopIterationObject(scope), null, 0); + NativeIterator.getStopIterationObject(scope), null, 0); } return iterator.next(); } @@ -316,32 +327,40 @@ public Object __iterator__(boolean b) { private Scriptable scope; } -// #string_id_map# + // #string_id_map# @Override protected int findPrototypeId(String s) { int id; -// #generated# Last update: 2007-06-11 09:43:19 EDT - L0: { id = 0; String X = null; + // #generated# Last update: 2007-06-11 09:43:19 EDT + L0: + { + id = 0; + String X = null; int s_length = s.length(); - if (s_length==4) { X="next";id=Id_next; } - else if (s_length==11) { X="constructor";id=Id_constructor; } - else if (s_length==12) { X="__iterator__";id=Id___iterator__; } - if (X!=null && X!=s && !X.equals(s)) id = 0; + if (s_length == 4) { + X = "next"; + id = Id_next; + } else if (s_length == 11) { + X = "constructor"; + id = Id_constructor; + } else if (s_length == 12) { + X = "__iterator__"; + id = Id___iterator__; + } + if (X != null && X != s && !X.equals(s)) id = 0; break L0; } -// #/generated# + // #/generated# return id; } - private static final int - Id_constructor = 1, - Id_next = 2, - Id___iterator__ = 3, - MAX_PROTOTYPE_ID = 3; + private static final int Id_constructor = 1, + Id_next = 2, + Id___iterator__ = 3, + MAX_PROTOTYPE_ID = 3; -// #/string_id_map# + // #/string_id_map# private Object objectIterator; } - diff --git a/src/org/mozilla/javascript/NativeJavaList.java b/src/org/mozilla/javascript/NativeJavaList.java index 6f5b5e7bff..7a64b3dc2d 100644 --- a/src/org/mozilla/javascript/NativeJavaList.java +++ b/src/org/mozilla/javascript/NativeJavaList.java @@ -15,7 +15,7 @@ public class NativeJavaList extends NativeJavaObject { private static final long serialVersionUID = 6403865639690547921L; private List list; - + private Class valueType; @SuppressWarnings("unchecked") @@ -40,7 +40,6 @@ public String getClassName() { return "JavaList"; } - @Override public boolean has(int index, Scriptable start) { if (isWithValidIndex(index)) { @@ -91,7 +90,7 @@ private void ensureCapacity(int minCapacity) { ((ArrayList) list).ensureCapacity(minCapacity); } while (minCapacity > list.size()) { - list.add(null); + list.add(null); } } } @@ -108,6 +107,6 @@ public Object[] getIds() { } private boolean isWithValidIndex(int index) { - return index >= 0 && index < list.size(); + return index >= 0 && index < list.size(); } } diff --git a/src/org/mozilla/javascript/NativeJavaMap.java b/src/org/mozilla/javascript/NativeJavaMap.java index 624e7e661a..c4d3d12a0d 100644 --- a/src/org/mozilla/javascript/NativeJavaMap.java +++ b/src/org/mozilla/javascript/NativeJavaMap.java @@ -11,9 +11,9 @@ import java.util.Map; public class NativeJavaMap extends NativeJavaObject { - + private static final long serialVersionUID = 46513864372878618L; - + private Map map; private Class keyType; private Class valueType; @@ -42,7 +42,6 @@ public String getClassName() { return "JavaMap"; } - @Override public boolean has(String name, Scriptable start) { if (map.containsKey(toKey(name, false))) { @@ -75,7 +74,7 @@ public Object get(String name, Scriptable start) { @Override public Object get(int index, Scriptable start) { - Object key = toKey(Integer.valueOf(index), false); + Object key = toKey(Integer.valueOf(index), false); if (map.containsKey(key)) { Context cx = Context.getContext(); Object obj = map.get(key); @@ -86,7 +85,7 @@ public Object get(int index, Scriptable start) { } return super.get(index, start); } - + @SuppressWarnings("unchecked") private Object toKey(Object key, boolean translateNew) { if (keyType == String.class || map.containsKey(key)) { @@ -129,7 +128,7 @@ private Object toKey(Object key, boolean translateNew) { } return ret; } - + private Object toValue(Object value) { if (valueType == Object.class) { return value; @@ -154,19 +153,18 @@ public Object unwrap() { keyTranslationMap = null; return super.unwrap(); } - + @Override public Object[] getIds() { Object[] ids = new Object[map.size()]; int i = 0; for (Object key : map.keySet()) { if (key instanceof Number) { - ids[i++] = (Number)key; + ids[i++] = (Number) key; } else { ids[i++] = ScriptRuntime.toString(key); } } return ids; } - } diff --git a/src/org/mozilla/javascript/NativeJavaMethod.java b/src/org/mozilla/javascript/NativeJavaMethod.java index 2d839f0cd5..4773df6595 100644 --- a/src/org/mozilla/javascript/NativeJavaMethod.java +++ b/src/org/mozilla/javascript/NativeJavaMethod.java @@ -13,50 +13,42 @@ import java.util.concurrent.CopyOnWriteArrayList; /** - * This class reflects Java methods into the JavaScript environment and - * handles overloading of methods. + * This class reflects Java methods into the JavaScript environment and handles overloading of + * methods. * * @author Mike Shaver * @see NativeJavaArray * @see NativeJavaPackage * @see NativeJavaClass */ - -public class NativeJavaMethod extends BaseFunction -{ +public class NativeJavaMethod extends BaseFunction { private static final long serialVersionUID = -3440381785576412928L; - NativeJavaMethod(MemberBox[] methods) - { + NativeJavaMethod(MemberBox[] methods) { this.functionName = methods[0].getName(); this.methods = methods; } - NativeJavaMethod(MemberBox[] methods, String name) - { + NativeJavaMethod(MemberBox[] methods, String name) { this.functionName = name; this.methods = methods; } - NativeJavaMethod(MemberBox method, String name) - { + NativeJavaMethod(MemberBox method, String name) { this.functionName = name; - this.methods = new MemberBox[] { method }; + this.methods = new MemberBox[] {method}; } - public NativeJavaMethod(Method method, String name) - { + public NativeJavaMethod(Method method, String name) { this(new MemberBox(method), name); } @Override - public String getFunctionName() - { + public String getFunctionName() { return functionName; } - static String scriptSignature(Object[] values) - { + static String scriptSignature(Object[] values) { StringBuilder sig = new StringBuilder(); for (int i = 0; i != values.length; ++i) { Object value = values[i]; @@ -74,7 +66,7 @@ static String scriptSignature(Object[] values) if (value instanceof Undefined) { s = "undefined"; } else if (value instanceof Wrapper) { - Object wrapped = ((Wrapper)value).unwrap(); + Object wrapped = ((Wrapper) value).unwrap(); s = wrapped.getClass().getName(); } else if (value instanceof Function) { s = "function"; @@ -94,8 +86,7 @@ static String scriptSignature(Object[] values) } @Override - String decompile(int indent, int flags) - { + String decompile(int indent, int flags) { StringBuilder sb = new StringBuilder(); boolean justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); if (!justbody) { @@ -110,8 +101,7 @@ String decompile(int indent, int flags) } @Override - public String toString() - { + public String toString() { StringBuilder sb = new StringBuilder(); for (int i = 0, N = methods.length; i != N; ++i) { // Check member type, we also use this for overloaded constructors @@ -130,9 +120,7 @@ public String toString() } @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, - Object[] args) - { + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { // Find a method that matches the types given. if (methods.length == 0) { throw new RuntimeException("No methods defined for call"); @@ -141,8 +129,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, int index = findCachedFunction(cx, args); if (index < 0) { Class c = methods[0].method().getDeclaringClass(); - String sig = c.getName() + '.' + getFunctionName() + '(' + - scriptSignature(args) + ')'; + String sig = c.getName() + '.' + getFunctionName() + '(' + scriptSignature(args) + ')'; throw Context.reportRuntimeErrorById("msg.java.no_such_method", sig); } @@ -152,7 +139,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, if (meth.vararg) { // marshall the explicit parameters Object[] newArgs = new Object[argTypes.length]; - for (int i = 0; i < argTypes.length-1; i++) { + for (int i = 0; i < argTypes.length - 1; i++) { newArgs[i] = Context.jsToJava(args[i], argTypes[i]); } @@ -160,29 +147,24 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, // Handle special situation where a single variable parameter // is given and it is a Java or ECMA array or is null. - if (args.length == argTypes.length && - (args[args.length-1] == null || - args[args.length-1] instanceof NativeArray || - args[args.length-1] instanceof NativeJavaArray)) - { + if (args.length == argTypes.length + && (args[args.length - 1] == null + || args[args.length - 1] instanceof NativeArray + || args[args.length - 1] instanceof NativeJavaArray)) { // convert the ECMA array into a native array - varArgs = Context.jsToJava(args[args.length-1], - argTypes[argTypes.length - 1]); + varArgs = Context.jsToJava(args[args.length - 1], argTypes[argTypes.length - 1]); } else { // marshall the variable parameters - Class componentType = argTypes[argTypes.length - 1]. - getComponentType(); - varArgs = Array.newInstance(componentType, - args.length - argTypes.length + 1); + Class componentType = argTypes[argTypes.length - 1].getComponentType(); + varArgs = Array.newInstance(componentType, args.length - argTypes.length + 1); for (int i = 0; i < Array.getLength(varArgs); i++) { - Object value = Context.jsToJava(args[argTypes.length-1 + i], - componentType); + Object value = Context.jsToJava(args[argTypes.length - 1 + i], componentType); Array.set(varArgs, i, value); } } // add varargs - newArgs[argTypes.length-1] = varArgs; + newArgs[argTypes.length - 1] = varArgs; // replace the original args with the new one args = newArgs; } else { @@ -201,18 +183,20 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, } Object javaObject; if (meth.isStatic()) { - javaObject = null; // don't need an object + javaObject = null; // don't need an object } else { Scriptable o = thisObj; Class c = meth.getDeclaringClass(); - for (;;) { + for (; ; ) { if (o == null) { throw Context.reportRuntimeErrorById( - "msg.nonjava.method", getFunctionName(), - ScriptRuntime.toString(thisObj), c.getName()); + "msg.nonjava.method", + getFunctionName(), + ScriptRuntime.toString(thisObj), + c.getName()); } if (o instanceof Wrapper) { - javaObject = ((Wrapper)o).unwrap(); + javaObject = ((Wrapper) o).unwrap(); if (c.isInstance(javaObject)) { break; } @@ -228,20 +212,24 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, Type staticType = meth.method().getGenericReturnType(); if (debug) { - Class actualType = (retval == null) ? null - : retval.getClass(); - System.err.println(" ----- Returned " + retval + - " actual = " + actualType + - " expect = " + staticType); + Class actualType = (retval == null) ? null : retval.getClass(); + System.err.println( + " ----- Returned " + + retval + + " actual = " + + actualType + + " expect = " + + staticType); } - Object wrapped = cx.getWrapFactory().wrap(cx, scope, - retval, staticType); + Object wrapped = + cx.getWrapFactory() + .wrap( + cx, scope, + retval, staticType); if (debug) { - Class actualType = (wrapped == null) ? null - : wrapped.getClass(); - System.err.println(" ----- Wrapped as " + wrapped + - " class = " + actualType); + Class actualType = (wrapped == null) ? null : wrapped.getClass(); + System.err.println(" ----- Wrapped as " + wrapped + " class = " + actualType); } if (wrapped == null && staticType == Void.TYPE) { @@ -270,13 +258,10 @@ int findCachedFunction(Context cx, Object[] args) { } /** - * Find the index of the correct function to call given the set of methods - * or constructors and the arguments. - * If no function can be found to call, return -1. + * Find the index of the correct function to call given the set of methods or constructors and + * the arguments. If no function can be found to call, return -1. */ - static int findFunction(Context cx, - MemberBox[] methodsOrCtors, Object[] args) - { + static int findFunction(Context cx, MemberBox[] methodsOrCtors, Object[] args) { if (methodsOrCtors.length == 0) { return -1; } else if (methodsOrCtors.length == 1) { @@ -286,7 +271,7 @@ static int findFunction(Context cx, if (member.vararg) { alength--; - if ( alength > args.length) { + if (alength > args.length) { return -1; } } else { @@ -296,8 +281,7 @@ static int findFunction(Context cx, } for (int j = 0; j != alength; ++j) { if (!NativeJavaObject.canConvert(args[j], argTypes[j])) { - if (debug) printDebug("Rejecting (args can't convert) ", - member, args); + if (debug) printDebug("Rejecting (args can't convert) ", member, args); return -1; } } @@ -309,14 +293,14 @@ static int findFunction(Context cx, int[] extraBestFits = null; int extraBestFitsCount = 0; - search: + search: for (int i = 0; i < methodsOrCtors.length; i++) { MemberBox member = methodsOrCtors[i]; Class[] argTypes = member.argTypes; int alength = argTypes.length; if (member.vararg) { alength--; - if ( alength > args.length) { + if (alength > args.length) { continue search; } } else { @@ -326,8 +310,7 @@ static int findFunction(Context cx, } for (int j = 0; j < alength; j++) { if (!NativeJavaObject.canConvert(args[j], argTypes[j])) { - if (debug) printDebug("Rejecting (args can't convert) ", - member, args); + if (debug) printDebug("Rejecting (args can't convert) ", member, args); continue search; } } @@ -340,9 +323,9 @@ static int findFunction(Context cx, // until extraBestFitsCount to avoid extraBestFits allocation // in the most common case of no ambiguity int betterCount = 0; // number of times member was prefered over - // best fits - int worseCount = 0; // number of times best fits were prefered - // over member + // best fits + int worseCount = 0; // number of times best fits were prefered + // over member for (int j = -1; j != extraBestFitsCount; ++j) { int bestFitIndex; if (j == -1) { @@ -351,21 +334,21 @@ static int findFunction(Context cx, bestFitIndex = extraBestFits[j]; } MemberBox bestFit = methodsOrCtors[bestFitIndex]; - if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) && - bestFit.isPublic() != member.isPublic()) - { + if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) + && bestFit.isPublic() != member.isPublic()) { // When FEATURE_ENHANCED_JAVA_ACCESS gives us access // to non-public members, continue to prefer public // methods in overloading - if (!bestFit.isPublic()) - ++betterCount; - else - ++worseCount; + if (!bestFit.isPublic()) ++betterCount; + else ++worseCount; } else { - int preference = preferSignature(args, argTypes, - member.vararg, - bestFit.argTypes, - bestFit.vararg ); + int preference = + preferSignature( + args, + argTypes, + member.vararg, + bestFit.argTypes, + bestFit.vararg); if (preference == PREFERENCE_AMBIGUOUS) { break; } else if (preference == PREFERENCE_FIRST_ARG) { @@ -379,26 +362,23 @@ static int findFunction(Context cx, // static methods of the class hierarchy, even if // a derived class's parameters match exactly. // We want to call the derived class's method. - if (bestFit.isStatic() && - bestFit.getDeclaringClass().isAssignableFrom( - member.getDeclaringClass())) - { + if (bestFit.isStatic() + && bestFit.getDeclaringClass() + .isAssignableFrom(member.getDeclaringClass())) { // On some JVMs, Class.getMethods will return all // static methods of the class hierarchy, even if // a derived class's parameters match exactly. // We want to call the derived class's method. - if (debug) printDebug( - "Substituting (overridden static)", - member, args); + if (debug) + printDebug("Substituting (overridden static)", member, args); if (j == -1) { firstBestFit = i; } else { extraBestFits[j] = i; } } else { - if (debug) printDebug( - "Ignoring same signature member ", - member, args); + if (debug) + printDebug("Ignoring same signature member ", member, args); } continue search; } @@ -406,18 +386,15 @@ static int findFunction(Context cx, } if (betterCount == 1 + extraBestFitsCount) { // member was prefered over all best fits - if (debug) printDebug( - "New first applicable ", member, args); + if (debug) printDebug("New first applicable ", member, args); firstBestFit = i; extraBestFitsCount = 0; } else if (worseCount == 1 + extraBestFitsCount) { // all best fits were prefered over member, ignore it - if (debug) printDebug( - "Rejecting (all current bests better) ", member, args); + if (debug) printDebug("Rejecting (all current bests better) ", member, args); } else { // some ambiguity was present, add member to best fit set - if (debug) printDebug( - "Added to best fit set ", member, args); + if (debug) printDebug("Added to best fit set ", member, args); if (extraBestFits == null) { // Allocate maximum possible array extraBestFits = new int[methodsOrCtors.length - 1]; @@ -455,36 +432,34 @@ static int findFunction(Context cx, if (methodsOrCtors[0].isCtor()) { throw Context.reportRuntimeErrorById( - "msg.constructor.ambiguous", - memberName, scriptSignature(args), buf.toString()); + "msg.constructor.ambiguous", memberName, scriptSignature(args), buf.toString()); } throw Context.reportRuntimeErrorById( - "msg.method.ambiguous", memberClass, - memberName, scriptSignature(args), buf.toString()); + "msg.method.ambiguous", + memberClass, + memberName, + scriptSignature(args), + buf.toString()); } /** Types are equal */ - private static final int PREFERENCE_EQUAL = 0; - private static final int PREFERENCE_FIRST_ARG = 1; + private static final int PREFERENCE_EQUAL = 0; + + private static final int PREFERENCE_FIRST_ARG = 1; private static final int PREFERENCE_SECOND_ARG = 2; /** No clear "easy" conversion */ - private static final int PREFERENCE_AMBIGUOUS = 3; + private static final int PREFERENCE_AMBIGUOUS = 3; /** - * Determine which of two signatures is the closer fit. - * Returns one of PREFERENCE_EQUAL, PREFERENCE_FIRST_ARG, - * PREFERENCE_SECOND_ARG, or PREFERENCE_AMBIGUOUS. + * Determine which of two signatures is the closer fit. Returns one of PREFERENCE_EQUAL, + * PREFERENCE_FIRST_ARG, PREFERENCE_SECOND_ARG, or PREFERENCE_AMBIGUOUS. */ - private static int preferSignature(Object[] args, - Class[] sig1, - boolean vararg1, - Class[] sig2, - boolean vararg2 ) - { + private static int preferSignature( + Object[] args, Class[] sig1, boolean vararg1, Class[] sig2, boolean vararg2) { int totalPreference = 0; for (int j = 0; j < args.length; j++) { - Class type1 = vararg1 && j >= sig1.length ? sig1[sig1.length-1] : sig1[j]; - Class type2 = vararg2 && j >= sig2.length ? sig2[sig2.length-1] : sig2[j]; + Class type1 = vararg1 && j >= sig1.length ? sig1[sig1.length - 1] : sig1[j]; + Class type2 = vararg2 && j >= sig2.length ? sig2[sig2.length - 1] : sig2[j]; if (type1 == type2) { continue; } @@ -524,12 +499,9 @@ private static int preferSignature(Object[] args, return totalPreference; } - private static final boolean debug = false; - private static void printDebug(String msg, MemberBox member, - Object[] args) - { + private static void printDebug(String msg, MemberBox member, Object[] args) { if (debug) { StringBuilder sb = new StringBuilder(); sb.append(" ----- "); @@ -549,7 +521,8 @@ private static void printDebug(String msg, MemberBox member, MemberBox[] methods; private String functionName; - private transient final CopyOnWriteArrayList overloadCache = new CopyOnWriteArrayList<>(); + private final transient CopyOnWriteArrayList overloadCache = + new CopyOnWriteArrayList<>(); } class ResolvedOverload { @@ -561,8 +534,7 @@ class ResolvedOverload { types = new Class[args.length]; for (int i = 0, l = args.length; i < l; i++) { Object arg = args[i]; - if (arg instanceof Wrapper) - arg = ((Wrapper)arg).unwrap(); + if (arg instanceof Wrapper) arg = ((Wrapper) arg).unwrap(); types[i] = arg == null ? null : arg.getClass(); } } @@ -573,8 +545,7 @@ boolean matches(Object[] args) { } for (int i = 0, l = args.length; i < l; i++) { Object arg = args[i]; - if (arg instanceof Wrapper) - arg = ((Wrapper)arg).unwrap(); + if (arg instanceof Wrapper) arg = ((Wrapper) arg).unwrap(); if (arg == null) { if (types[i] != null) return false; } else if (arg.getClass() != types[i]) { diff --git a/src/org/mozilla/javascript/NativeJavaObject.java b/src/org/mozilla/javascript/NativeJavaObject.java index 8583cdfd33..3b59e5e090 100644 --- a/src/org/mozilla/javascript/NativeJavaObject.java +++ b/src/org/mozilla/javascript/NativeJavaObject.java @@ -18,32 +18,27 @@ import java.util.Map; /** - * This class reflects non-Array Java objects into the JavaScript environment. It - * reflect fields directly, and uses NativeJavaMethod objects to reflect (possibly - * overloaded) methods.

+ * This class reflects non-Array Java objects into the JavaScript environment. It reflect fields + * directly, and uses NativeJavaMethod objects to reflect (possibly overloaded) methods. + * + *

* * @author Mike Shaver * @see NativeJavaArray * @see NativeJavaPackage * @see NativeJavaClass */ - -public class NativeJavaObject - implements Scriptable, SymbolScriptable, Wrapper, Serializable -{ +public class NativeJavaObject implements Scriptable, SymbolScriptable, Wrapper, Serializable { private static final long serialVersionUID = -6948590651130498591L; - public NativeJavaObject() { } + public NativeJavaObject() {} - public NativeJavaObject(Scriptable scope, Object javaObject, - Type staticType) - { + public NativeJavaObject(Scriptable scope, Object javaObject, Type staticType) { this(scope, javaObject, staticType, false); } - public NativeJavaObject(Scriptable scope, Object javaObject, - Type staticType, boolean isAdapter) - { + public NativeJavaObject( + Scriptable scope, Object javaObject, Type staticType, boolean isAdapter) { this.parent = scope; this.javaObject = javaObject; this.staticType = ScriptRuntime.getRawType(staticType); @@ -58,10 +53,8 @@ protected void initMembers() { } else { dynamicType = staticType; } - members = JavaMembers.lookupClass(parent, dynamicType, staticType, - isAdapter); - fieldAndMethods - = members.getFieldAndMethodsObjects(this, javaObject, false); + members = JavaMembers.lookupClass(parent, dynamicType, staticType, isAdapter); + fieldAndMethods = members.getFieldAndMethodsObjects(this, javaObject, false); } @Override @@ -110,8 +103,7 @@ public void put(String name, Scriptable start, Object value) { // we modify it in the prototype rather than copy it down. if (prototype == null || members.has(name, false)) members.put(this, name, javaObject, value, false); - else - prototype.put(name, prototype, value); + else prototype.put(name, prototype, value); } @Override @@ -123,7 +115,7 @@ public void put(Symbol symbol, Scriptable start, Object value) { if (prototype == null || members.has(name, false)) { members.put(this, name, javaObject, value, false); } else if (prototype instanceof SymbolScriptable) { - ((SymbolScriptable)prototype).put(symbol, prototype, value); + ((SymbolScriptable) prototype).put(symbol, prototype, value); } } @@ -139,46 +131,36 @@ public boolean hasInstance(Scriptable value) { } @Override - public void delete(String name) { - } + public void delete(String name) {} @Override - public void delete(Symbol key) { - } + public void delete(Symbol key) {} @Override - public void delete(int index) { - } + public void delete(int index) {} @Override public Scriptable getPrototype() { if (prototype == null && javaObject instanceof String) { return TopLevel.getBuiltinPrototype( - ScriptableObject.getTopLevelScope(parent), - TopLevel.Builtins.String); + ScriptableObject.getTopLevelScope(parent), TopLevel.Builtins.String); } return prototype; } - /** - * Sets the prototype of the object. - */ + /** Sets the prototype of the object. */ @Override public void setPrototype(Scriptable m) { prototype = m; } - /** - * Returns the parent (enclosing) scope of the object. - */ + /** Returns the parent (enclosing) scope of the object. */ @Override public Scriptable getParentScope() { return parent; } - /** - * Sets the parent (enclosing) scope of the object. - */ + /** Sets the parent (enclosing) scope of the object. */ @Override public void setParentScope(Scriptable m) { parent = m; @@ -191,7 +173,7 @@ public Object[] getIds() { /** * @deprecated Use {@link Context#getWrapFactory()} together with calling {@link - * WrapFactory#wrap(Context, Scriptable, Object, Type)} + * WrapFactory#wrap(Context, Scriptable, Object, Type)} */ @Deprecated public static Object wrap(Scriptable scope, Object obj, Class staticType) { @@ -211,8 +193,7 @@ public String getClassName() { } @Override - public Object getDefaultValue(Class hint) - { + public Object getDefaultValue(Class hint) { Object value; if (hint == null) { if (javaObject instanceof Boolean) { @@ -235,14 +216,16 @@ public Object getDefaultValue(Class hint) } Object converterObject = get(converterName, this); if (converterObject instanceof Function) { - Function f = (Function)converterObject; - value = f.call(Context.getContext(), f.getParentScope(), - this, ScriptRuntime.emptyArgs); + Function f = (Function) converterObject; + value = + f.call( + Context.getContext(), + f.getParentScope(), + this, + ScriptRuntime.emptyArgs); } else { - if (hint == ScriptRuntime.NumberClass - && javaObject instanceof Boolean) - { - boolean b = ((Boolean)javaObject).booleanValue(); + if (hint == ScriptRuntime.NumberClass && javaObject instanceof Boolean) { + boolean b = ((Boolean) javaObject).booleanValue(); value = b ? ScriptRuntime.wrapNumber(1.0) : ScriptRuntime.zeroObj; } else { value = javaObject.toString(); @@ -253,9 +236,9 @@ public Object getDefaultValue(Class hint) } /** - * Determine whether we can/should convert between the given type and the - * desired one. This should be superceded by a conversion-cost calculation - * function, but for now I'll hide behind precedent. + * Determine whether we can/should convert between the given type and the desired one. This + * should be superceded by a conversion-cost calculation function, but for now I'll hide behind + * precedent. */ public static boolean canConvert(Object fromObj, Class to) { int weight = getConversionWeight(fromObj, to); @@ -263,174 +246,152 @@ public static boolean canConvert(Object fromObj, Class to) { return (weight < CONVERSION_NONE); } - private static final int JSTYPE_UNDEFINED = 0; // undefined type - private static final int JSTYPE_NULL = 1; // null - private static final int JSTYPE_BOOLEAN = 2; // boolean - private static final int JSTYPE_NUMBER = 3; // number - private static final int JSTYPE_STRING = 4; // string - private static final int JSTYPE_JAVA_CLASS = 5; // JavaClass + private static final int JSTYPE_UNDEFINED = 0; // undefined type + private static final int JSTYPE_NULL = 1; // null + private static final int JSTYPE_BOOLEAN = 2; // boolean + private static final int JSTYPE_NUMBER = 3; // number + private static final int JSTYPE_STRING = 4; // string + private static final int JSTYPE_JAVA_CLASS = 5; // JavaClass private static final int JSTYPE_JAVA_OBJECT = 6; // JavaObject - private static final int JSTYPE_JAVA_ARRAY = 7; // JavaArray - private static final int JSTYPE_OBJECT = 8; // Scriptable + private static final int JSTYPE_JAVA_ARRAY = 7; // JavaArray + private static final int JSTYPE_OBJECT = 8; // Scriptable - static final byte CONVERSION_TRIVIAL = 1; - static final byte CONVERSION_NONTRIVIAL = 0; - static final byte CONVERSION_NONE = 99; + static final byte CONVERSION_TRIVIAL = 1; + static final byte CONVERSION_NONTRIVIAL = 0; + static final byte CONVERSION_NONE = 99; /** - * Derive a ranking based on how "natural" the conversion is. - * The special value CONVERSION_NONE means no conversion is possible, - * and CONVERSION_NONTRIVIAL signals that more type conformance testing - * is required. - * Based on - * - * "preferred method conversions" from Live Connect 3 + * Derive a ranking based on how "natural" the conversion is. The special value CONVERSION_NONE + * means no conversion is possible, and CONVERSION_NONTRIVIAL signals that more type conformance + * testing is required. Based on "preferred method + * conversions" from Live Connect 3 */ static int getConversionWeight(Object fromObj, Class to) { int fromCode = getJSTypeCode(fromObj); switch (fromCode) { + case JSTYPE_UNDEFINED: + if (to == ScriptRuntime.StringClass || to == ScriptRuntime.ObjectClass) { + return 1; + } + break; - case JSTYPE_UNDEFINED: - if (to == ScriptRuntime.StringClass || - to == ScriptRuntime.ObjectClass) { - return 1; - } - break; - - case JSTYPE_NULL: - if (!to.isPrimitive()) { - return 1; - } - break; - - case JSTYPE_BOOLEAN: - // "boolean" is #1 - if (to == Boolean.TYPE) { - return 1; - } - else if (to == ScriptRuntime.BooleanClass) { - return 2; - } - else if (to == ScriptRuntime.ObjectClass) { - return 3; - } - else if (to == ScriptRuntime.StringClass) { - return 4; - } - break; + case JSTYPE_NULL: + if (!to.isPrimitive()) { + return 1; + } + break; - case JSTYPE_NUMBER: - if (to.isPrimitive()) { - if (to == Double.TYPE) { + case JSTYPE_BOOLEAN: + // "boolean" is #1 + if (to == Boolean.TYPE) { return 1; + } else if (to == ScriptRuntime.BooleanClass) { + return 2; + } else if (to == ScriptRuntime.ObjectClass) { + return 3; + } else if (to == ScriptRuntime.StringClass) { + return 4; } - else if (to != Boolean.TYPE) { - return 1 + getSizeRank(to); + break; + + case JSTYPE_NUMBER: + if (to.isPrimitive()) { + if (to == Double.TYPE) { + return 1; + } else if (to != Boolean.TYPE) { + return 1 + getSizeRank(to); + } + } else { + if (to == ScriptRuntime.StringClass) { + // native numbers are #1-8 + return 9; + } else if (to == ScriptRuntime.ObjectClass) { + return 10; + } else if (ScriptRuntime.NumberClass.isAssignableFrom(to)) { + // "double" is #1 + return 2; + } } - } - else { + break; + + case JSTYPE_STRING: if (to == ScriptRuntime.StringClass) { - // native numbers are #1-8 - return 9; - } - else if (to == ScriptRuntime.ObjectClass) { - return 10; - } - else if (ScriptRuntime.NumberClass.isAssignableFrom(to)) { - // "double" is #1 + return 1; + } else if (to.isInstance(fromObj)) { return 2; + } else if (to.isPrimitive()) { + if (to == Character.TYPE) { + return 3; + } else if (to != Boolean.TYPE) { + return 4; + } } - } - break; + break; - case JSTYPE_STRING: - if (to == ScriptRuntime.StringClass) { - return 1; - } - else if (to.isInstance(fromObj)) { - return 2; - } - else if (to.isPrimitive()) { - if (to == Character.TYPE) { + case JSTYPE_JAVA_CLASS: + if (to == ScriptRuntime.ClassClass) { + return 1; + } else if (to == ScriptRuntime.ObjectClass) { return 3; - } else if (to != Boolean.TYPE) { + } else if (to == ScriptRuntime.StringClass) { return 4; } - } - break; - - case JSTYPE_JAVA_CLASS: - if (to == ScriptRuntime.ClassClass) { - return 1; - } - else if (to == ScriptRuntime.ObjectClass) { - return 3; - } - else if (to == ScriptRuntime.StringClass) { - return 4; - } - break; - - case JSTYPE_JAVA_OBJECT: - case JSTYPE_JAVA_ARRAY: - Object javaObj = fromObj; - if (javaObj instanceof Wrapper) { - javaObj = ((Wrapper)javaObj).unwrap(); - } - if (to.isInstance(javaObj)) { - return CONVERSION_NONTRIVIAL; - } - if (to == ScriptRuntime.StringClass) { - return 2; - } - else if (to.isPrimitive() && to != Boolean.TYPE) { - return (fromCode == JSTYPE_JAVA_ARRAY) - ? CONVERSION_NONE : 2 + getSizeRank(to); - } - break; + break; - case JSTYPE_OBJECT: - // Other objects takes #1-#3 spots - if (to != ScriptRuntime.ObjectClass && to.isInstance(fromObj)) { - // No conversion required, but don't apply for java.lang.Object - return 1; - } - if (to.isArray()) { - if (fromObj instanceof NativeArray) { - // This is a native array conversion to a java array - // Array conversions are all equal, and preferable to object - // and string conversion, per LC3. - return 2; + case JSTYPE_JAVA_OBJECT: + case JSTYPE_JAVA_ARRAY: + Object javaObj = fromObj; + if (javaObj instanceof Wrapper) { + javaObj = ((Wrapper) javaObj).unwrap(); } - } - else if (to == ScriptRuntime.ObjectClass) { - return 3; - } - else if (to == ScriptRuntime.StringClass) { - return 4; - } - else if (to == ScriptRuntime.DateClass) { - if (fromObj instanceof NativeDate) { - // This is a native date to java date conversion - return 1; + if (to.isInstance(javaObj)) { + return CONVERSION_NONTRIVIAL; } - } - else if (to.isInterface()) { + if (to == ScriptRuntime.StringClass) { + return 2; + } else if (to.isPrimitive() && to != Boolean.TYPE) { + return (fromCode == JSTYPE_JAVA_ARRAY) ? CONVERSION_NONE : 2 + getSizeRank(to); + } + break; - if (fromObj instanceof NativeFunction) { - // See comments in createInterfaceAdapter + case JSTYPE_OBJECT: + // Other objects takes #1-#3 spots + if (to != ScriptRuntime.ObjectClass && to.isInstance(fromObj)) { + // No conversion required, but don't apply for java.lang.Object return 1; } - if (fromObj instanceof NativeObject) { - return 2; + if (to.isArray()) { + if (fromObj instanceof NativeArray) { + // This is a native array conversion to a java array + // Array conversions are all equal, and preferable to object + // and string conversion, per LC3. + return 2; + } + } else if (to == ScriptRuntime.ObjectClass) { + return 3; + } else if (to == ScriptRuntime.StringClass) { + return 4; + } else if (to == ScriptRuntime.DateClass) { + if (fromObj instanceof NativeDate) { + // This is a native date to java date conversion + return 1; + } + } else if (to.isInterface()) { + + if (fromObj instanceof NativeFunction) { + // See comments in createInterfaceAdapter + return 1; + } + if (fromObj instanceof NativeObject) { + return 2; + } + return 12; + } else if (to.isPrimitive() && to != Boolean.TYPE) { + return 4 + getSizeRank(to); } - return 12; - } - else if (to.isPrimitive() && to != Boolean.TYPE) { - return 4 + getSizeRank(to); - } - break; + break; } return CONVERSION_NONE; @@ -439,29 +400,21 @@ else if (to.isPrimitive() && to != Boolean.TYPE) { static int getSizeRank(Class aType) { if (aType == Double.TYPE) { return 1; - } - else if (aType == Float.TYPE) { + } else if (aType == Float.TYPE) { return 2; - } - else if (aType == Long.TYPE) { + } else if (aType == Long.TYPE) { return 3; - } - else if (aType == Integer.TYPE) { + } else if (aType == Integer.TYPE) { return 4; - } - else if (aType == Short.TYPE) { + } else if (aType == Short.TYPE) { return 5; - } - else if (aType == Character.TYPE) { + } else if (aType == Character.TYPE) { return 6; - } - else if (aType == Byte.TYPE) { + } else if (aType == Byte.TYPE) { return 7; - } - else if (aType == Boolean.TYPE) { + } else if (aType == Boolean.TYPE) { return CONVERSION_NONE; - } - else { + } else { return 8; } } @@ -469,37 +422,27 @@ else if (aType == Boolean.TYPE) { private static int getJSTypeCode(Object value) { if (value == null) { return JSTYPE_NULL; - } - else if (value == Undefined.instance) { + } else if (value == Undefined.instance) { return JSTYPE_UNDEFINED; - } - else if (value instanceof CharSequence) { + } else if (value instanceof CharSequence) { return JSTYPE_STRING; - } - else if (value instanceof Number) { + } else if (value instanceof Number) { return JSTYPE_NUMBER; - } - else if (value instanceof Boolean) { + } else if (value instanceof Boolean) { return JSTYPE_BOOLEAN; - } - else if (value instanceof Scriptable) { + } else if (value instanceof Scriptable) { if (value instanceof NativeJavaClass) { return JSTYPE_JAVA_CLASS; - } - else if (value instanceof NativeJavaArray) { + } else if (value instanceof NativeJavaArray) { return JSTYPE_JAVA_ARRAY; - } - else if (value instanceof Wrapper) { + } else if (value instanceof Wrapper) { return JSTYPE_JAVA_OBJECT; - } - else { + } else { return JSTYPE_OBJECT; } - } - else if (value instanceof Class) { + } else if (value instanceof Class) { return JSTYPE_JAVA_CLASS; - } - else { + } else { Class valueClass = value.getClass(); if (valueClass.isArray()) { return JSTYPE_JAVA_ARRAY; @@ -509,207 +452,176 @@ else if (value instanceof Class) { } /** - * Not intended for public use. Callers should use the - * public API Context.toType. + * Not intended for public use. Callers should use the public API Context.toType. + * * @deprecated as of 1.5 Release 4 * @see org.mozilla.javascript.Context#jsToJava(Object, Class) */ @Deprecated - public static Object coerceType(Class type, Object value) - { + public static Object coerceType(Class type, Object value) { return coerceTypeImpl(type, value); } - /** - * Type-munging for field setting and method invocation. - * Conforms to LC3 specification - */ - static Object coerceTypeImpl(Class type, Object value) - { + /** Type-munging for field setting and method invocation. Conforms to LC3 specification */ + static Object coerceTypeImpl(Class type, Object value) { if (value != null && value.getClass() == type) { return value; } switch (getJSTypeCode(value)) { + case JSTYPE_NULL: + // raise error if type.isPrimitive() + if (type.isPrimitive()) { + reportConversionError(value, type); + } + return null; - case JSTYPE_NULL: - // raise error if type.isPrimitive() - if (type.isPrimitive()) { - reportConversionError(value, type); - } - return null; - - case JSTYPE_UNDEFINED: - if (type == ScriptRuntime.StringClass || - type == ScriptRuntime.ObjectClass) { - return "undefined"; - } - reportConversionError("undefined", type); - break; - - case JSTYPE_BOOLEAN: - // Under LC3, only JS Booleans can be coerced into a Boolean value - if (type == Boolean.TYPE || - type == ScriptRuntime.BooleanClass || - type == ScriptRuntime.ObjectClass) { - return value; - } - else if (type == ScriptRuntime.StringClass) { - return value.toString(); - } - else { - reportConversionError(value, type); - } - break; - - case JSTYPE_NUMBER: - if (type == ScriptRuntime.StringClass) { - return ScriptRuntime.toString(value); - } - else if (type == ScriptRuntime.ObjectClass) { - Context context = Context.getCurrentContext(); - if ((context != null) && - context.hasFeature(Context.FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE)) { - //to process numbers like 2.0 as 2 without decimal place - long roundedValue = Math.round(toDouble(value)); - if(roundedValue == toDouble(value)) { - return coerceToNumber(Long.TYPE, value); + case JSTYPE_UNDEFINED: + if (type == ScriptRuntime.StringClass || type == ScriptRuntime.ObjectClass) { + return "undefined"; + } + reportConversionError("undefined", type); + break; + + case JSTYPE_BOOLEAN: + // Under LC3, only JS Booleans can be coerced into a Boolean value + if (type == Boolean.TYPE + || type == ScriptRuntime.BooleanClass + || type == ScriptRuntime.ObjectClass) { + return value; + } else if (type == ScriptRuntime.StringClass) { + return value.toString(); + } else { + reportConversionError(value, type); + } + break; + + case JSTYPE_NUMBER: + if (type == ScriptRuntime.StringClass) { + return ScriptRuntime.toString(value); + } else if (type == ScriptRuntime.ObjectClass) { + Context context = Context.getCurrentContext(); + if ((context != null) + && context.hasFeature(Context.FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE)) { + // to process numbers like 2.0 as 2 without decimal place + long roundedValue = Math.round(toDouble(value)); + if (roundedValue == toDouble(value)) { + return coerceToNumber(Long.TYPE, value); + } } + return coerceToNumber(Double.TYPE, value); + } else if ((type.isPrimitive() && type != Boolean.TYPE) + || ScriptRuntime.NumberClass.isAssignableFrom(type)) { + return coerceToNumber(type, value); + } else { + reportConversionError(value, type); } - return coerceToNumber(Double.TYPE, value); - } - else if ((type.isPrimitive() && type != Boolean.TYPE) || - ScriptRuntime.NumberClass.isAssignableFrom(type)) { - return coerceToNumber(type, value); - } - else { - reportConversionError(value, type); - } - break; - - case JSTYPE_STRING: - if (type == ScriptRuntime.StringClass || type.isInstance(value)) { - return value.toString(); - } - else if (type == Character.TYPE - || type == ScriptRuntime.CharacterClass) - { - // Special case for converting a single char string to a - // character - // Placed here because it applies *only* to JS strings, - // not other JS objects converted to strings - if (((CharSequence)value).length() == 1) { - return Character.valueOf(((CharSequence)value).charAt(0)); + break; + + case JSTYPE_STRING: + if (type == ScriptRuntime.StringClass || type.isInstance(value)) { + return value.toString(); + } else if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) { + // Special case for converting a single char string to a + // character + // Placed here because it applies *only* to JS strings, + // not other JS objects converted to strings + if (((CharSequence) value).length() == 1) { + return Character.valueOf(((CharSequence) value).charAt(0)); + } + return coerceToNumber(type, value); + } else if ((type.isPrimitive() && type != Boolean.TYPE) + || ScriptRuntime.NumberClass.isAssignableFrom(type)) { + return coerceToNumber(type, value); + } else { + reportConversionError(value, type); } - return coerceToNumber(type, value); - } - else if ((type.isPrimitive() && type != Boolean.TYPE) - || ScriptRuntime.NumberClass.isAssignableFrom(type)) - { - return coerceToNumber(type, value); - } - else { - reportConversionError(value, type); - } - break; - - case JSTYPE_JAVA_CLASS: - if (value instanceof Wrapper) { - value = ((Wrapper)value).unwrap(); - } + break; - if (type == ScriptRuntime.ClassClass || - type == ScriptRuntime.ObjectClass) { - return value; - } - else if (type == ScriptRuntime.StringClass) { - return value.toString(); - } - else { - reportConversionError(value, type); - } - break; + case JSTYPE_JAVA_CLASS: + if (value instanceof Wrapper) { + value = ((Wrapper) value).unwrap(); + } - case JSTYPE_JAVA_OBJECT: - case JSTYPE_JAVA_ARRAY: - if (value instanceof Wrapper) { - value = ((Wrapper)value).unwrap(); - } - if (type.isPrimitive()) { - if (type == Boolean.TYPE) { + if (type == ScriptRuntime.ClassClass || type == ScriptRuntime.ObjectClass) { + return value; + } else if (type == ScriptRuntime.StringClass) { + return value.toString(); + } else { reportConversionError(value, type); } - return coerceToNumber(type, value); - } - if (type == ScriptRuntime.StringClass) { - return value.toString(); - } - if (type.isInstance(value)) { - return value; - } - reportConversionError(value, type); - break; + break; - case JSTYPE_OBJECT: - if (type == ScriptRuntime.StringClass) { - return ScriptRuntime.toString(value); - } - else if (type.isPrimitive()) { - if (type == Boolean.TYPE) { - reportConversionError(value, type); + case JSTYPE_JAVA_OBJECT: + case JSTYPE_JAVA_ARRAY: + if (value instanceof Wrapper) { + value = ((Wrapper) value).unwrap(); } - return coerceToNumber(type, value); - } - else if (type.isInstance(value)) { - return value; - } - else if (type == ScriptRuntime.DateClass - && value instanceof NativeDate) - { - double time = ((NativeDate)value).getJSTimeValue(); - // XXX: This will replace NaN by 0 - return new Date((long)time); - } - else if (type.isArray() && value instanceof NativeArray) { - // Make a new java array, and coerce the JS array components - // to the target (component) type. - NativeArray array = (NativeArray) value; - long length = array.getLength(); - Class arrayType = type.getComponentType(); - Object Result = Array.newInstance(arrayType, (int)length); - for (int i = 0 ; i < length ; ++i) { - try { - Array.set(Result, i, coerceTypeImpl( - arrayType, array.get(i, array))); - } - catch (EvaluatorException ee) { + if (type.isPrimitive()) { + if (type == Boolean.TYPE) { reportConversionError(value, type); } + return coerceToNumber(type, value); } - - return Result; - } - else if (value instanceof Wrapper) { - value = ((Wrapper)value).unwrap(); - if (type.isInstance(value)) + if (type == ScriptRuntime.StringClass) { + return value.toString(); + } + if (type.isInstance(value)) { return value; + } reportConversionError(value, type); - } - else if (type.isInterface() - && (value instanceof NativeObject - || (value instanceof Callable && value instanceof ScriptableObject))) { - // Try to use function/object as implementation of Java interface. - return createInterfaceAdapter(type, (ScriptableObject) value); - } else { - reportConversionError(value, type); - } - break; + break; + + case JSTYPE_OBJECT: + if (type == ScriptRuntime.StringClass) { + return ScriptRuntime.toString(value); + } else if (type.isPrimitive()) { + if (type == Boolean.TYPE) { + reportConversionError(value, type); + } + return coerceToNumber(type, value); + } else if (type.isInstance(value)) { + return value; + } else if (type == ScriptRuntime.DateClass && value instanceof NativeDate) { + double time = ((NativeDate) value).getJSTimeValue(); + // XXX: This will replace NaN by 0 + return new Date((long) time); + } else if (type.isArray() && value instanceof NativeArray) { + // Make a new java array, and coerce the JS array components + // to the target (component) type. + NativeArray array = (NativeArray) value; + long length = array.getLength(); + Class arrayType = type.getComponentType(); + Object Result = Array.newInstance(arrayType, (int) length); + for (int i = 0; i < length; ++i) { + try { + Array.set(Result, i, coerceTypeImpl(arrayType, array.get(i, array))); + } catch (EvaluatorException ee) { + reportConversionError(value, type); + } + } + + return Result; + } else if (value instanceof Wrapper) { + value = ((Wrapper) value).unwrap(); + if (type.isInstance(value)) return value; + reportConversionError(value, type); + } else if (type.isInterface() + && (value instanceof NativeObject + || (value instanceof Callable + && value instanceof ScriptableObject))) { + // Try to use function/object as implementation of Java interface. + return createInterfaceAdapter(type, (ScriptableObject) value); + } else { + reportConversionError(value, type); + } + break; } return value; } - protected static Object createInterfaceAdapter(Classtype, ScriptableObject so) { + protected static Object createInterfaceAdapter(Class type, ScriptableObject so) { // XXX: Currently only instances of ScriptableObject are // supported since the resulting interface proxies should // be reused next time conversion is made and generic @@ -729,8 +641,7 @@ protected static Object createInterfaceAdapter(Classtype, ScriptableObject so return glue; } - private static Object coerceToNumber(Class type, Object value) - { + private static Object coerceToNumber(Class type, Object value) { Class valueClass = value.getClass(); // Character @@ -738,18 +649,22 @@ private static Object coerceToNumber(Class type, Object value) if (valueClass == ScriptRuntime.CharacterClass) { return value; } - return Character.valueOf((char)toInteger(value, - ScriptRuntime.CharacterClass, - Character.MIN_VALUE, - Character.MAX_VALUE)); + return Character.valueOf( + (char) + toInteger( + value, + ScriptRuntime.CharacterClass, + Character.MIN_VALUE, + Character.MAX_VALUE)); } // Double, Float - if (type == ScriptRuntime.ObjectClass || - type == ScriptRuntime.DoubleClass || type == Double.TYPE) { + if (type == ScriptRuntime.ObjectClass + || type == ScriptRuntime.DoubleClass + || type == Double.TYPE) { return valueClass == ScriptRuntime.DoubleClass - ? value - : Double.valueOf(toDouble(value)); + ? value + : Double.valueOf(toDouble(value)); } if (type == ScriptRuntime.FloatClass || type == Float.TYPE) { @@ -757,22 +672,18 @@ private static Object coerceToNumber(Class type, Object value) return value; } double number = toDouble(value); - if (Double.isInfinite(number) || Double.isNaN(number) - || number == 0.0) { - return Float.valueOf((float)number); + if (Double.isInfinite(number) || Double.isNaN(number) || number == 0.0) { + return Float.valueOf((float) number); } double absNumber = Math.abs(number); if (absNumber < Float.MIN_VALUE) { return Float.valueOf((number > 0.0) ? +0.0f : -0.0f); - } - else if (absNumber > Float.MAX_VALUE) { - return Float.valueOf((number > 0.0) ? - Float.POSITIVE_INFINITY : - Float.NEGATIVE_INFINITY); - } - else { - return Float.valueOf((float)number); + } else if (absNumber > Float.MAX_VALUE) { + return Float.valueOf( + (number > 0.0) ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY); + } else { + return Float.valueOf((float) number); } } @@ -781,10 +692,13 @@ else if (absNumber > Float.MAX_VALUE) { if (valueClass == ScriptRuntime.IntegerClass) { return value; } - return Integer.valueOf((int)toInteger(value, - ScriptRuntime.IntegerClass, - Integer.MIN_VALUE, - Integer.MAX_VALUE)); + return Integer.valueOf( + (int) + toInteger( + value, + ScriptRuntime.IntegerClass, + Integer.MIN_VALUE, + Integer.MAX_VALUE)); } if (type == ScriptRuntime.LongClass || type == Long.TYPE) { @@ -800,71 +714,65 @@ else if (absNumber > Float.MAX_VALUE) { */ final double max = Double.longBitsToDouble(0x43dfffffffffffffL); final double min = Double.longBitsToDouble(0xc3e0000000000000L); - return Long.valueOf(toInteger(value, - ScriptRuntime.LongClass, - min, - max)); + return Long.valueOf(toInteger(value, ScriptRuntime.LongClass, min, max)); } if (type == ScriptRuntime.ShortClass || type == Short.TYPE) { if (valueClass == ScriptRuntime.ShortClass) { return value; } - return Short.valueOf((short)toInteger(value, - ScriptRuntime.ShortClass, - Short.MIN_VALUE, - Short.MAX_VALUE)); + return Short.valueOf( + (short) + toInteger( + value, + ScriptRuntime.ShortClass, + Short.MIN_VALUE, + Short.MAX_VALUE)); } if (type == ScriptRuntime.ByteClass || type == Byte.TYPE) { if (valueClass == ScriptRuntime.ByteClass) { return value; } - return Byte.valueOf((byte)toInteger(value, - ScriptRuntime.ByteClass, - Byte.MIN_VALUE, - Byte.MAX_VALUE)); + return Byte.valueOf( + (byte) + toInteger( + value, + ScriptRuntime.ByteClass, + Byte.MIN_VALUE, + Byte.MAX_VALUE)); } return Double.valueOf(toDouble(value)); } - - private static double toDouble(Object value) - { + private static double toDouble(Object value) { if (value instanceof Number) { - return ((Number)value).doubleValue(); - } - else if (value instanceof String) { - return ScriptRuntime.toNumber((String)value); - } - else if (value instanceof Scriptable) { + return ((Number) value).doubleValue(); + } else if (value instanceof String) { + return ScriptRuntime.toNumber((String) value); + } else if (value instanceof Scriptable) { if (value instanceof Wrapper) { // XXX: optimize tail-recursion? - return toDouble(((Wrapper)value).unwrap()); + return toDouble(((Wrapper) value).unwrap()); } return ScriptRuntime.toNumber(value); - } - else { + } else { Method meth; try { - meth = value.getClass().getMethod("doubleValue", (Class [])null); - } - catch (NoSuchMethodException e) { + meth = value.getClass().getMethod("doubleValue", (Class[]) null); + } catch (NoSuchMethodException e) { meth = null; - } - catch (SecurityException e) { + } catch (SecurityException e) { meth = null; } if (meth != null) { try { - return ((Number)meth.invoke(value, (Object [])null)).doubleValue(); - } - catch (IllegalAccessException e) { + return ((Number) meth.invoke(value, (Object[]) null)).doubleValue(); + } catch (IllegalAccessException e) { // XXX: ignore, or error message? reportConversionError(value, Double.TYPE); - } - catch (InvocationTargetException e) { + } catch (InvocationTargetException e) { // XXX: ignore, or error message? reportConversionError(value, Double.TYPE); } @@ -873,9 +781,7 @@ else if (value instanceof Scriptable) { } } - private static long toInteger(Object value, Class type, - double min, double max) - { + private static long toInteger(Object value, Class type, double min, double max) { double d = toDouble(value); if (Double.isInfinite(d) || Double.isNaN(d)) { @@ -885,8 +791,7 @@ private static long toInteger(Object value, Class type, if (d > 0.0) { d = Math.floor(d); - } - else { + } else { d = Math.ceil(d); } @@ -894,22 +799,19 @@ private static long toInteger(Object value, Class type, // Convert to string first, for more readable message reportConversionError(ScriptRuntime.toString(value), type); } - return (long)d; + return (long) d; } - static void reportConversionError(Object value, Class type) - { + static void reportConversionError(Object value, Class type) { // It uses String.valueOf(value), not value.toString() since // value can be null, bug 282447. throw Context.reportRuntimeErrorById( - "msg.conversion.not.allowed", - String.valueOf(value), - JavaMembers.javaSignature(type)); + "msg.conversion.not.allowed", + String.valueOf(value), + JavaMembers.javaSignature(type)); } - private void writeObject(ObjectOutputStream out) - throws IOException - { + private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeBoolean(isAdapter); @@ -917,7 +819,7 @@ private void writeObject(ObjectOutputStream out) if (adapter_writeAdapterObject == null) { throw new IOException(); } - Object[] args = { javaObject, out }; + Object[] args = {javaObject, out}; try { adapter_writeAdapterObject.invoke(null, args); } catch (Exception ex) { @@ -934,16 +836,13 @@ private void writeObject(ObjectOutputStream out) } } - private void readObject(ObjectInputStream in) - throws IOException, ClassNotFoundException - { + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); isAdapter = in.readBoolean(); if (isAdapter) { - if (adapter_readAdapterObject == null) - throw new ClassNotFoundException(); - Object[] args = { this, in }; + if (adapter_readAdapterObject == null) throw new ClassNotFoundException(); + Object[] args = {this, in}; try { javaObject = adapter_readAdapterObject.invoke(null, args); } catch (Exception ex) { @@ -953,7 +852,7 @@ private void readObject(ObjectInputStream in) javaObject = in.readObject(); } - String className = (String)in.readObject(); + String className = (String) in.readObject(); if (className != null) { staticType = Class.forName(className); } else { @@ -963,21 +862,17 @@ private void readObject(ObjectInputStream in) initMembers(); } - /** - * The prototype of this object. - */ + /** The prototype of this object. */ protected Scriptable prototype; - /** - * The parent scope of this object. - */ + /** The parent scope of this object. */ protected Scriptable parent; protected transient Object javaObject; protected transient Class staticType; protected transient JavaMembers members; - private transient Map fieldAndMethods; + private transient Map fieldAndMethods; protected transient boolean isAdapter; private static final Object COERCED_INTERFACE_KEY = "Coerced Interface"; @@ -992,13 +887,11 @@ private void readObject(ObjectInputStream in) try { sig2[0] = ScriptRuntime.ObjectClass; sig2[1] = Kit.classOrNull("java.io.ObjectOutputStream"); - adapter_writeAdapterObject = cl.getMethod("writeAdapterObject", - sig2); + adapter_writeAdapterObject = cl.getMethod("writeAdapterObject", sig2); sig2[0] = ScriptRuntime.ScriptableClass; sig2[1] = Kit.classOrNull("java.io.ObjectInputStream"); - adapter_readAdapterObject = cl.getMethod("readAdapterObject", - sig2); + adapter_readAdapterObject = cl.getMethod("readAdapterObject", sig2); } catch (NoSuchMethodException e) { adapter_writeAdapterObject = null; @@ -1006,5 +899,4 @@ private void readObject(ObjectInputStream in) } } } - } diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 0d7a2f3f01..4c02951427 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -18,7 +18,6 @@ import java.util.Arrays; import java.util.Locale; import java.util.ResourceBundle; - import org.mozilla.javascript.ast.FunctionNode; import org.mozilla.javascript.v8dtoa.DoubleConversion; import org.mozilla.javascript.v8dtoa.FastDtoa; @@ -30,58 +29,51 @@ * * @author Norris Boyd */ - public class ScriptRuntime { - /** - * No instances should be created. - */ - protected ScriptRuntime() { - } - + /** No instances should be created. */ + protected ScriptRuntime() {} /** - * Returns representation of the [[ThrowTypeError]] object. - * See ECMA 5 spec, 13.2.3 + * Returns representation of the [[ThrowTypeError]] object. See ECMA 5 spec, 13.2.3 * * @deprecated {@link #typeErrorThrower(Context)} */ @Deprecated public static BaseFunction typeErrorThrower() { - return typeErrorThrower(Context.getCurrentContext()); + return typeErrorThrower(Context.getCurrentContext()); } - /** - * Returns representation of the [[ThrowTypeError]] object. - * See ECMA 5 spec, 13.2.3 - */ + /** Returns representation of the [[ThrowTypeError]] object. See ECMA 5 spec, 13.2.3 */ public static BaseFunction typeErrorThrower(Context cx) { - if (cx.typeErrorThrower == null) { - BaseFunction thrower = new BaseFunction() { - private static final long serialVersionUID = -5891740962154902286L; - - @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - throw typeErrorById("msg.op.not.allowed"); - } - @Override - public int getLength() { - return 0; - } - }; - ScriptRuntime.setFunctionProtoAndParent(thrower, cx.topCallScope); - thrower.preventExtensions(); - cx.typeErrorThrower = thrower; - } - return cx.typeErrorThrower; + if (cx.typeErrorThrower == null) { + BaseFunction thrower = + new BaseFunction() { + private static final long serialVersionUID = -5891740962154902286L; + + @Override + public Object call( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + throw typeErrorById("msg.op.not.allowed"); + } + + @Override + public int getLength() { + return 0; + } + }; + ScriptRuntime.setFunctionProtoAndParent(thrower, cx.topCallScope); + thrower.preventExtensions(); + cx.typeErrorThrower = thrower; + } + return cx.typeErrorThrower; } static class NoSuchMethodShim implements Callable { String methodName; Callable noSuchMethodMethod; - NoSuchMethodShim(Callable noSuchMethodMethod, String methodName) - { + NoSuchMethodShim(Callable noSuchMethodMethod, String methodName) { this.noSuchMethodMethod = noSuchMethodMethod; this.methodName = methodName; } @@ -95,16 +87,13 @@ static class NoSuchMethodShim implements Callable { * @return the result of the call */ @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, - Object[] args) - { + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { Object[] nestedArgs = new Object[2]; nestedArgs[0] = methodName; nestedArgs[1] = newArrayLiteral(args, null, cx, scope); return noSuchMethodMethod.call(cx, scope, thisObj, nestedArgs); } - } /* * There's such a huge space (and some time) waste for the Foo.class @@ -116,49 +105,39 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, * that they won't cause problems by being loaded early. */ - public final static Class - BooleanClass = Kit.classOrNull("java.lang.Boolean"), - ByteClass = Kit.classOrNull("java.lang.Byte"), - CharacterClass = Kit.classOrNull("java.lang.Character"), - ClassClass = Kit.classOrNull("java.lang.Class"), - DoubleClass = Kit.classOrNull("java.lang.Double"), - FloatClass = Kit.classOrNull("java.lang.Float"), - IntegerClass = Kit.classOrNull("java.lang.Integer"), - LongClass = Kit.classOrNull("java.lang.Long"), - NumberClass = Kit.classOrNull("java.lang.Number"), - ObjectClass = Kit.classOrNull("java.lang.Object"), - ShortClass = Kit.classOrNull("java.lang.Short"), - StringClass = Kit.classOrNull("java.lang.String"), - DateClass = Kit.classOrNull("java.util.Date"); - - public final static Class - ContextClass - = Kit.classOrNull("org.mozilla.javascript.Context"), - ContextFactoryClass - = Kit.classOrNull("org.mozilla.javascript.ContextFactory"), - FunctionClass - = Kit.classOrNull("org.mozilla.javascript.Function"), - ScriptableObjectClass - = Kit.classOrNull("org.mozilla.javascript.ScriptableObject"); - public static final Class ScriptableClass = - Scriptable.class; + public static final Class BooleanClass = Kit.classOrNull("java.lang.Boolean"), + ByteClass = Kit.classOrNull("java.lang.Byte"), + CharacterClass = Kit.classOrNull("java.lang.Character"), + ClassClass = Kit.classOrNull("java.lang.Class"), + DoubleClass = Kit.classOrNull("java.lang.Double"), + FloatClass = Kit.classOrNull("java.lang.Float"), + IntegerClass = Kit.classOrNull("java.lang.Integer"), + LongClass = Kit.classOrNull("java.lang.Long"), + NumberClass = Kit.classOrNull("java.lang.Number"), + ObjectClass = Kit.classOrNull("java.lang.Object"), + ShortClass = Kit.classOrNull("java.lang.Short"), + StringClass = Kit.classOrNull("java.lang.String"), + DateClass = Kit.classOrNull("java.util.Date"); + public static final Class ContextClass = Kit.classOrNull("org.mozilla.javascript.Context"), + ContextFactoryClass = Kit.classOrNull("org.mozilla.javascript.ContextFactory"), + FunctionClass = Kit.classOrNull("org.mozilla.javascript.Function"), + ScriptableObjectClass = Kit.classOrNull("org.mozilla.javascript.ScriptableObject"); + public static final Class ScriptableClass = Scriptable.class; private static final Object LIBRARY_SCOPE_KEY = "LIBRARY_SCOPE"; - public static boolean isRhinoRuntimeType(Class cl) - { + public static boolean isRhinoRuntimeType(Class cl) { if (cl.isPrimitive()) { return (cl != Character.TYPE); } - return (cl == StringClass || cl == BooleanClass + return (cl == StringClass + || cl == BooleanClass || NumberClass.isAssignableFrom(cl) || ScriptableClass.isAssignableFrom(cl)); } - public static ScriptableObject initSafeStandardObjects(Context cx, - ScriptableObject scope, - boolean sealed) - { + public static ScriptableObject initSafeStandardObjects( + Context cx, ScriptableObject scope, boolean sealed) { if (scope == null) { scope = new NativeObject(); } @@ -175,8 +154,7 @@ public static ScriptableObject initSafeStandardObjects(Context cx, functionProto.setPrototype(objectProto); // Set the prototype of the object passed in if need be - if (scope.getPrototype() == null) - scope.setPrototype(objectProto); + if (scope.getPrototype() == null) scope.setPrototype(objectProto); // must precede NativeGlobal since it's needed therein NativeError.init(scope, sealed); @@ -205,14 +183,14 @@ public static ScriptableObject initSafeStandardObjects(Context cx, NativeArrayIterator.init(scope, sealed); NativeStringIterator.init(scope, sealed); - boolean withXml = cx.hasFeature(Context.FEATURE_E4X) && - cx.getE4xImplementationFactory() != null; + boolean withXml = + cx.hasFeature(Context.FEATURE_E4X) && cx.getE4xImplementationFactory() != null; // define lazy-loaded properties using their class name - new LazilyLoadedCtor(scope, "RegExp", - "org.mozilla.javascript.regexp.NativeRegExp", sealed, true); - new LazilyLoadedCtor(scope, "Continuation", - "org.mozilla.javascript.NativeContinuation", sealed, true); + new LazilyLoadedCtor( + scope, "RegExp", "org.mozilla.javascript.regexp.NativeRegExp", sealed, true); + new LazilyLoadedCtor( + scope, "Continuation", "org.mozilla.javascript.NativeContinuation", sealed, true); if (withXml) { String xmlImpl = cx.getE4xImplementationFactory().getImplementationClassName(); @@ -222,43 +200,75 @@ public static ScriptableObject initSafeStandardObjects(Context cx, new LazilyLoadedCtor(scope, "QName", xmlImpl, sealed, true); } - if (((cx.getLanguageVersion() >= Context.VERSION_1_8) && - cx.hasFeature(Context.FEATURE_V8_EXTENSIONS)) || - (cx.getLanguageVersion() >= Context.VERSION_ES6)) - { - new LazilyLoadedCtor(scope, "ArrayBuffer", - "org.mozilla.javascript.typedarrays.NativeArrayBuffer", - sealed, true); - new LazilyLoadedCtor(scope, "Int8Array", - "org.mozilla.javascript.typedarrays.NativeInt8Array", - sealed, true); - new LazilyLoadedCtor(scope, "Uint8Array", - "org.mozilla.javascript.typedarrays.NativeUint8Array", - sealed, true); - new LazilyLoadedCtor(scope, "Uint8ClampedArray", - "org.mozilla.javascript.typedarrays.NativeUint8ClampedArray", - sealed, true); - new LazilyLoadedCtor(scope, "Int16Array", - "org.mozilla.javascript.typedarrays.NativeInt16Array", - sealed, true); - new LazilyLoadedCtor(scope, "Uint16Array", - "org.mozilla.javascript.typedarrays.NativeUint16Array", - sealed, true); - new LazilyLoadedCtor(scope, "Int32Array", - "org.mozilla.javascript.typedarrays.NativeInt32Array", - sealed, true); - new LazilyLoadedCtor(scope, "Uint32Array", - "org.mozilla.javascript.typedarrays.NativeUint32Array", - sealed, true); - new LazilyLoadedCtor(scope, "Float32Array", - "org.mozilla.javascript.typedarrays.NativeFloat32Array", - sealed, true); - new LazilyLoadedCtor(scope, "Float64Array", - "org.mozilla.javascript.typedarrays.NativeFloat64Array", - sealed, true); - new LazilyLoadedCtor(scope, "DataView", - "org.mozilla.javascript.typedarrays.NativeDataView", - sealed, true); + if (((cx.getLanguageVersion() >= Context.VERSION_1_8) + && cx.hasFeature(Context.FEATURE_V8_EXTENSIONS)) + || (cx.getLanguageVersion() >= Context.VERSION_ES6)) { + new LazilyLoadedCtor( + scope, + "ArrayBuffer", + "org.mozilla.javascript.typedarrays.NativeArrayBuffer", + sealed, + true); + new LazilyLoadedCtor( + scope, + "Int8Array", + "org.mozilla.javascript.typedarrays.NativeInt8Array", + sealed, + true); + new LazilyLoadedCtor( + scope, + "Uint8Array", + "org.mozilla.javascript.typedarrays.NativeUint8Array", + sealed, + true); + new LazilyLoadedCtor( + scope, + "Uint8ClampedArray", + "org.mozilla.javascript.typedarrays.NativeUint8ClampedArray", + sealed, + true); + new LazilyLoadedCtor( + scope, + "Int16Array", + "org.mozilla.javascript.typedarrays.NativeInt16Array", + sealed, + true); + new LazilyLoadedCtor( + scope, + "Uint16Array", + "org.mozilla.javascript.typedarrays.NativeUint16Array", + sealed, + true); + new LazilyLoadedCtor( + scope, + "Int32Array", + "org.mozilla.javascript.typedarrays.NativeInt32Array", + sealed, + true); + new LazilyLoadedCtor( + scope, + "Uint32Array", + "org.mozilla.javascript.typedarrays.NativeUint32Array", + sealed, + true); + new LazilyLoadedCtor( + scope, + "Float32Array", + "org.mozilla.javascript.typedarrays.NativeFloat32Array", + sealed, + true); + new LazilyLoadedCtor( + scope, + "Float64Array", + "org.mozilla.javascript.typedarrays.NativeFloat64Array", + sealed, + true); + new LazilyLoadedCtor( + scope, + "DataView", + "org.mozilla.javascript.typedarrays.NativeDataView", + sealed, + true); } if (cx.getLanguageVersion() >= Context.VERSION_ES6) { @@ -272,30 +282,27 @@ public static ScriptableObject initSafeStandardObjects(Context cx, } if (scope instanceof TopLevel) { - ((TopLevel)scope).cacheBuiltins(scope, sealed); + ((TopLevel) scope).cacheBuiltins(scope, sealed); } return scope; } - public static ScriptableObject initStandardObjects(Context cx, - ScriptableObject scope, - boolean sealed) - { + public static ScriptableObject initStandardObjects( + Context cx, ScriptableObject scope, boolean sealed) { ScriptableObject s = initSafeStandardObjects(cx, scope, sealed); - new LazilyLoadedCtor(s, "Packages", - "org.mozilla.javascript.NativeJavaTopPackage", sealed, true); - new LazilyLoadedCtor(s, "getClass", - "org.mozilla.javascript.NativeJavaTopPackage", sealed, true); - new LazilyLoadedCtor(s, "JavaAdapter", - "org.mozilla.javascript.JavaAdapter", sealed, true); - new LazilyLoadedCtor(s, "JavaImporter", - "org.mozilla.javascript.ImporterTopLevel", sealed, true); + new LazilyLoadedCtor( + s, "Packages", "org.mozilla.javascript.NativeJavaTopPackage", sealed, true); + new LazilyLoadedCtor( + s, "getClass", "org.mozilla.javascript.NativeJavaTopPackage", sealed, true); + new LazilyLoadedCtor(s, "JavaAdapter", "org.mozilla.javascript.JavaAdapter", sealed, true); + new LazilyLoadedCtor( + s, "JavaImporter", "org.mozilla.javascript.ImporterTopLevel", sealed, true); for (String packageName : getTopPackageNames()) { - new LazilyLoadedCtor(s, packageName, - "org.mozilla.javascript.NativeJavaTopPackage", sealed, true); + new LazilyLoadedCtor( + s, packageName, "org.mozilla.javascript.NativeJavaTopPackage", sealed, true); } return s; @@ -303,22 +310,19 @@ public static ScriptableObject initStandardObjects(Context cx, static String[] getTopPackageNames() { // Include "android" top package if running on Android - return "Dalvik".equals(System.getProperty("java.vm.name")) ? - new String[] { "java", "javax", "org", "com", "edu", "net", "android" } : - new String[] { "java", "javax", "org", "com", "edu", "net" }; + return "Dalvik".equals(System.getProperty("java.vm.name")) + ? new String[] {"java", "javax", "org", "com", "edu", "net", "android"} + : new String[] {"java", "javax", "org", "com", "edu", "net"}; } - public static ScriptableObject getLibraryScopeOrNull(Scriptable scope) - { + public static ScriptableObject getLibraryScopeOrNull(Scriptable scope) { ScriptableObject libScope; - libScope = (ScriptableObject)ScriptableObject. - getTopScopeValue(scope, LIBRARY_SCOPE_KEY); + libScope = (ScriptableObject) ScriptableObject.getTopScopeValue(scope, LIBRARY_SCOPE_KEY); return libScope; } // It is public so NativeRegExp can access it. - public static boolean isJSLineTerminator(int c) - { + public static boolean isJSLineTerminator(int c) { // Optimization for faster check for eol character: // they do not have 0xDFD0 bits set if ((c & 0xDFD0) != 0) { @@ -328,26 +332,14 @@ public static boolean isJSLineTerminator(int c) } public static boolean isJSWhitespaceOrLineTerminator(int c) { - return (isStrWhiteSpaceChar(c) || isJSLineTerminator(c)); + return (isStrWhiteSpaceChar(c) || isJSLineTerminator(c)); } /** - * Indicates if the character is a Str whitespace char according to ECMA spec: - * StrWhiteSpaceChar ::: - - - - - - - - - - - + * Indicates if the character is a Str whitespace char according to ECMA spec: StrWhiteSpaceChar + * ::: */ - static boolean isStrWhiteSpaceChar(int c) - { + static boolean isStrWhiteSpaceChar(int c) { switch (c) { case ' ': // case '\n': // @@ -365,18 +357,15 @@ static boolean isStrWhiteSpaceChar(int c) } } - public static Boolean wrapBoolean(boolean b) - { + public static Boolean wrapBoolean(boolean b) { return Boolean.valueOf(b); } - public static Integer wrapInt(int i) - { + public static Integer wrapInt(int i) { return Integer.valueOf(i); } - public static Number wrapNumber(double x) - { + public static Number wrapNumber(double x) { if (Double.isNaN(x)) { return ScriptRuntime.NaNobj; } @@ -386,25 +375,20 @@ public static Number wrapNumber(double x) /** * Convert the value to a boolean. * - * See ECMA 9.2. + *

See ECMA 9.2. */ - public static boolean toBoolean(Object val) - { - for (;;) { - if (val instanceof Boolean) - return ((Boolean) val).booleanValue(); - if (val == null || val == Undefined.instance) - return false; - if (val instanceof CharSequence) - return ((CharSequence) val).length() != 0; + public static boolean toBoolean(Object val) { + for (; ; ) { + if (val instanceof Boolean) return ((Boolean) val).booleanValue(); + if (val == null || val == Undefined.instance) return false; + if (val instanceof CharSequence) return ((CharSequence) val).length() != 0; if (val instanceof Number) { double d = ((Number) val).doubleValue(); return (!Double.isNaN(d) && d != 0.0); } if (val instanceof Scriptable) { - if (val instanceof ScriptableObject && - ((ScriptableObject) val).avoidObjectDetection()) - { + if (val instanceof ScriptableObject + && ((ScriptableObject) val).avoidObjectDetection()) { return false; } if (Context.getContext().isVersionECMA1()) { @@ -425,25 +409,17 @@ public static boolean toBoolean(Object val) /** * Convert the value to a number. * - * See ECMA 9.3. + *

See ECMA 9.3. */ - public static double toNumber(Object val) - { - for (;;) { - if (val instanceof Number) - return ((Number) val).doubleValue(); - if (val == null) - return +0.0; - if (val == Undefined.instance) - return NaN; - if (val instanceof String) - return toNumber((String) val); - if (val instanceof CharSequence) - return toNumber(val.toString()); - if (val instanceof Boolean) - return ((Boolean) val).booleanValue() ? 1 : +0.0; - if (val instanceof Symbol) - throw typeErrorById("msg.not.a.number"); + public static double toNumber(Object val) { + for (; ; ) { + if (val instanceof Number) return ((Number) val).doubleValue(); + if (val == null) return +0.0; + if (val == Undefined.instance) return NaN; + if (val instanceof String) return toNumber((String) val); + if (val instanceof CharSequence) return toNumber(val.toString()); + if (val instanceof Boolean) return ((Boolean) val).booleanValue() ? 1 : +0.0; + if (val instanceof Symbol) throw typeErrorById("msg.not.a.number"); if (val instanceof Scriptable) { val = ((Scriptable) val).getDefaultValue(NumberClass); if ((val instanceof Scriptable) && !isSymbol(val)) @@ -479,7 +455,8 @@ static double stringToNumber(String s, int start, int end, int radix) { /* * Helper function for toNumber, parseInt, and TokenStream.getToken. */ - private static double stringToNumber(String source, int sourceStart, int sourceEnd, int radix, boolean isPrefix) { + private static double stringToNumber( + String source, int sourceStart, int sourceEnd, int radix, boolean isPrefix) { char digitMax = '9'; char lowerCaseBound = 'a'; char upperCaseBound = 'A'; @@ -495,17 +472,12 @@ private static double stringToNumber(String source, int sourceStart, int sourceE for (end = sourceStart; end <= sourceEnd; end++) { char c = source.charAt(end); int newDigit; - if ('0' <= c && c <= digitMax) - newDigit = c - '0'; - else if ('a' <= c && c < lowerCaseBound) - newDigit = c - 'a' + 10; - else if ('A' <= c && c < upperCaseBound) - newDigit = c - 'A' + 10; - else if (!isPrefix) - return NaN; // isn't a prefix but found unexpected char - else - break; // unexpected char - sum = sum*radix + newDigit; + if ('0' <= c && c <= digitMax) newDigit = c - '0'; + else if ('a' <= c && c < lowerCaseBound) newDigit = c - 'a' + 10; + else if ('A' <= c && c < upperCaseBound) newDigit = c - 'A' + 10; + else if (!isPrefix) return NaN; // isn't a prefix but found unexpected char + else break; // unexpected char + sum = sum * radix + newDigit; } if (sourceStart == end) { // stopped right at the beginning return NaN; @@ -522,9 +494,7 @@ else if (!isPrefix) } catch (NumberFormatException nfe) { return NaN; } - } else if (radix == 2 || radix == 4 || radix == 8 || - radix == 16 || radix == 32) - { + } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) { /* The number may also be inaccurate for one of these bases. * This happens if the addition in value*radix + digit causes * a round-down to an even least significant mantissa bit @@ -540,7 +510,7 @@ else if (!isPrefix) final int SKIP_LEADING_ZEROS = 0; final int FIRST_EXACT_53_BITS = 1; - final int AFTER_BIT_53 = 2; + final int AFTER_BIT_53 = 2; final int ZEROS_AFTER_54 = 3; final int MIXED_AFTER_54 = 4; @@ -552,77 +522,70 @@ else if (!isPrefix) boolean bit54 = false; int pos = sourceStart; - for (;;) { + for (; ; ) { if (bitShiftInChar == 1) { - if (pos == end) - break; + if (pos == end) break; digit = source.charAt(pos++); - if ('0' <= digit && digit <= '9') - digit -= '0'; - else if ('a' <= digit && digit <= 'z') - digit -= 'a' - 10; - else - digit -= 'A' - 10; + if ('0' <= digit && digit <= '9') digit -= '0'; + else if ('a' <= digit && digit <= 'z') digit -= 'a' - 10; + else digit -= 'A' - 10; bitShiftInChar = radix; } bitShiftInChar >>= 1; boolean bit = (digit & bitShiftInChar) != 0; switch (state) { - case SKIP_LEADING_ZEROS: - if (bit) { + case SKIP_LEADING_ZEROS: + if (bit) { + --exactBitsLimit; + sum = 1.0; + state = FIRST_EXACT_53_BITS; + } + break; + case FIRST_EXACT_53_BITS: + sum *= 2.0; + if (bit) sum += 1.0; --exactBitsLimit; - sum = 1.0; - state = FIRST_EXACT_53_BITS; - } + if (exactBitsLimit == 0) { + bit53 = bit; + state = AFTER_BIT_53; + } + break; + case AFTER_BIT_53: + bit54 = bit; + factor = 2.0; + state = ZEROS_AFTER_54; + break; + case ZEROS_AFTER_54: + if (bit) { + state = MIXED_AFTER_54; + } + // fallthrough + case MIXED_AFTER_54: + factor *= 2; + break; + } + } + switch (state) { + case SKIP_LEADING_ZEROS: + sum = 0.0; break; - case FIRST_EXACT_53_BITS: - sum *= 2.0; - if (bit) - sum += 1.0; - --exactBitsLimit; - if (exactBitsLimit == 0) { - bit53 = bit; - state = AFTER_BIT_53; - } + case FIRST_EXACT_53_BITS: + case AFTER_BIT_53: + // do nothing break; - case AFTER_BIT_53: - bit54 = bit; - factor = 2.0; - state = ZEROS_AFTER_54; + case ZEROS_AFTER_54: + // x1.1 -> x1 + 1 (round up) + // x0.1 -> x0 (round down) + if (bit54 & bit53) sum += 1.0; + sum *= factor; break; - case ZEROS_AFTER_54: - if (bit) { - state = MIXED_AFTER_54; - } - // fallthrough - case MIXED_AFTER_54: - factor *= 2; + case MIXED_AFTER_54: + // x.100...1.. -> x + 1 (round up) + // x.0anything -> x (round down) + if (bit54) sum += 1.0; + sum *= factor; break; - } - } - switch (state) { - case SKIP_LEADING_ZEROS: - sum = 0.0; - break; - case FIRST_EXACT_53_BITS: - case AFTER_BIT_53: - // do nothing - break; - case ZEROS_AFTER_54: - // x1.1 -> x1 + 1 (round up) - // x0.1 -> x0 (round down) - if (bit54 & bit53) - sum += 1.0; - sum *= factor; - break; - case MIXED_AFTER_54: - // x.100...1.. -> x + 1 (round up) - // x.0anything -> x (round down) - if (bit54) - sum += 1.0; - sum *= factor; - break; } } /* We don't worry about inaccurate numbers for any other base. */ @@ -633,7 +596,7 @@ else if ('a' <= digit && digit <= 'z') /** * ToNumber applied to the String type * - * See the #sec-tonumber-applied-to-the-string-type section of ECMA + *

See the #sec-tonumber-applied-to-the-string-type section of ECMA */ public static double toNumber(String s) { final int len = s.length(); @@ -641,7 +604,7 @@ public static double toNumber(String s) { // Skip whitespace at the start int start = 0; char startChar; - for (;;) { + for (; ; ) { if (start == len) { // empty or contains only whitespace return +0.0; @@ -671,8 +634,7 @@ public static double toNumber(String s) { // ToNumber('0b1') => NaN // ToNumber('0o5') => NaN final Context cx = Context.getCurrentContext(); - final boolean oldParsingMode = - cx == null || cx.getLanguageVersion() < Context.VERSION_ES6; + final boolean oldParsingMode = cx == null || cx.getLanguageVersion() < Context.VERSION_ES6; // Handle non-base10 numbers if (startChar == '0') { @@ -710,22 +672,18 @@ public static double toNumber(String s) { start++; } if (start + 7 == end && s.regionMatches(start, "Infinity", 0, 8)) { - return startChar == '-' - ? Double.NEGATIVE_INFINITY - : Double.POSITIVE_INFINITY; + return startChar == '-' ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; } return NaN; } // A base10, non-infinity number: // just try a normal floating point conversion - String sub = s.substring(start, end+1); + String sub = s.substring(start, end + 1); // Quick test to check string contains only valid characters because // Double.parseDouble() can be slow and accept input we want to reject for (int i = sub.length() - 1; i >= 0; i--) { char c = sub.charAt(i); - if (('0' <= c && c <= '9') || c == '.' || - c == 'e' || c == 'E' || - c == '+' || c == '-') + if (('0' <= c && c <= '9') || c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-') continue; return NaN; } @@ -737,14 +695,12 @@ public static double toNumber(String s) { } /** - * Helper function for builtin objects that use the varargs form. - * ECMA function formal arguments are undefined if not supplied; - * this function pads the argument array out to the expected + * Helper function for builtin objects that use the varargs form. ECMA function formal arguments + * are undefined if not supplied; this function pads the argument array out to the expected * length, if necessary. */ public static Object[] padArguments(Object[] args, int count) { - if (count < args.length) - return args; + if (count < args.length) return args; Object[] result = new Object[count]; System.arraycopy(args, 0, result, 0, args.length); @@ -754,28 +710,25 @@ public static Object[] padArguments(Object[] args, int count) { return result; } - public static String escapeString(String s) - { + public static String escapeString(String s) { return escapeString(s, '"'); } /** - * For escaping strings printed by object and array literals; not quite - * the same as 'escape.' + * For escaping strings printed by object and array literals; not quite the same as 'escape.' */ - public static String escapeString(String s, char escapeQuote) - { + public static String escapeString(String s, char escapeQuote) { if (!(escapeQuote == '"' || escapeQuote == '\'' || escapeQuote == '`')) Kit.codeBug(); StringBuilder sb = null; - for(int i = 0, L = s.length(); i != L; ++i) { + for (int i = 0, L = s.length(); i != L; ++i) { int c = s.charAt(i); if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') { // an ordinary print character (like C isprint()) and not " // or \ . if (sb != null) { - sb.append((char)c); + sb.append((char) c); } continue; } @@ -787,19 +740,35 @@ public static String escapeString(String s, char escapeQuote) int escape = -1; switch (c) { - case '\b': escape = 'b'; break; - case '\f': escape = 'f'; break; - case '\n': escape = 'n'; break; - case '\r': escape = 'r'; break; - case '\t': escape = 't'; break; - case 0xb: escape = 'v'; break; // Java lacks \v. - case ' ': escape = ' '; break; - case '\\': escape = '\\'; break; + case '\b': + escape = 'b'; + break; + case '\f': + escape = 'f'; + break; + case '\n': + escape = 'n'; + break; + case '\r': + escape = 'r'; + break; + case '\t': + escape = 't'; + break; + case 0xb: + escape = 'v'; + break; // Java lacks \v. + case ' ': + escape = ' '; + break; + case '\\': + escape = '\\'; + break; } if (escape >= 0) { // an \escaped sort of character sb.append('\\'); - sb.append((char)escape); + sb.append((char) escape); } else if (c == escapeQuote) { sb.append('\\'); sb.append(escapeQuote); @@ -818,30 +787,26 @@ public static String escapeString(String s, char escapeQuote) for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) { int digit = 0xf & (c >> shift); int hc = (digit < 10) ? '0' + digit : 'a' - 10 + digit; - sb.append((char)hc); + sb.append((char) hc); } } } return (sb == null) ? s : sb.toString(); } - static boolean isValidIdentifierName(String s, Context cx, boolean isStrict) - { + static boolean isValidIdentifierName(String s, Context cx, boolean isStrict) { int L = s.length(); - if (L == 0) - return false; - if (!Character.isJavaIdentifierStart(s.charAt(0))) - return false; + if (L == 0) return false; + if (!Character.isJavaIdentifierStart(s.charAt(0))) return false; for (int i = 1; i != L; ++i) { - if (!Character.isJavaIdentifierPart(s.charAt(i))) - return false; + if (!Character.isJavaIdentifierPart(s.charAt(i))) return false; } return !TokenStream.isKeyword(s, cx.getLanguageVersion(), isStrict); } public static CharSequence toCharSequence(Object val) { if (val instanceof NativeString) { - return ((NativeString)val).toCharSequence(); + return ((NativeString) val).toCharSequence(); } return val instanceof CharSequence ? (CharSequence) val : toString(val); } @@ -849,10 +814,10 @@ public static CharSequence toCharSequence(Object val) { /** * Convert the value to a string. * - * See ECMA 9.8. + *

See ECMA 9.8. */ public static String toString(Object val) { - for (;;) { + for (; ; ) { if (val == null) { return "null"; } @@ -860,7 +825,7 @@ public static String toString(Object val) { return "undefined"; } if (val instanceof String) { - return (String)val; + return (String) val; } if (val instanceof CharSequence) { return val.toString(); @@ -868,7 +833,7 @@ public static String toString(Object val) { if (val instanceof Number) { // XXX should we just teach NativeNumber.stringValue() // about Numbers? - return numberToString(((Number)val).doubleValue(), 10); + return numberToString(((Number) val).doubleValue(), 10); } if (val instanceof Symbol) { throw typeErrorById("msg.not.a.string"); @@ -884,41 +849,30 @@ public static String toString(Object val) { } } - static String defaultObjectToString(Scriptable obj) - { - if (obj == null) - return "[object Null]"; - if (Undefined.isUndefined(obj)) - return "[object Undefined]"; + static String defaultObjectToString(Scriptable obj) { + if (obj == null) return "[object Null]"; + if (Undefined.isUndefined(obj)) return "[object Undefined]"; return "[object " + obj.getClassName() + ']'; } - public static String toString(Object[] args, int index) - { + public static String toString(Object[] args, int index) { return (index < args.length) ? toString(args[index]) : "undefined"; } - /** - * Optimized version of toString(Object) for numbers. - */ + /** Optimized version of toString(Object) for numbers. */ public static String toString(double val) { return numberToString(val, 10); } public static String numberToString(double d, int base) { if ((base < 2) || (base > 36)) { - throw Context.reportRuntimeErrorById( - "msg.bad.radix", Integer.toString(base)); + throw Context.reportRuntimeErrorById("msg.bad.radix", Integer.toString(base)); } - if (Double.isNaN(d)) - return "NaN"; - if (d == Double.POSITIVE_INFINITY) - return "Infinity"; - if (d == Double.NEGATIVE_INFINITY) - return "-Infinity"; - if (d == 0.0) - return "0"; + if (Double.isNaN(d)) return "NaN"; + if (d == Double.POSITIVE_INFINITY) return "Infinity"; + if (d == Double.NEGATIVE_INFINITY) return "-Infinity"; + if (d == 0.0) return "0"; if (base != 10) { return DToA.JS_dtobasestr(base, d); @@ -934,8 +888,7 @@ public static String numberToString(double d, int base) { return buffer.toString(); } - static String uneval(Context cx, Scriptable scope, Object value) - { + static String uneval(Context cx, Scriptable scope, Object value) { if (value == null) { return "null"; } @@ -951,7 +904,7 @@ static String uneval(Context cx, Scriptable scope, Object value) return sb.toString(); } if (value instanceof Number) { - double d = ((Number)value).doubleValue(); + double d = ((Number) value).doubleValue(); if (d == 0 && 1 / d < 0) { return "-0"; } @@ -961,13 +914,13 @@ static String uneval(Context cx, Scriptable scope, Object value) return toString(value); } if (value instanceof Scriptable) { - Scriptable obj = (Scriptable)value; + Scriptable obj = (Scriptable) value; // Wrapped Java objects won't have "toSource" and will report // errors for get()s of nonexistent name, so use has() first if (ScriptableObject.hasProperty(obj, "toSource")) { Object v = ScriptableObject.getProperty(obj, "toSource"); if (v instanceof Function) { - Function f = (Function)v; + Function f = (Function) v; return toString(f.call(cx, scope, obj, emptyArgs)); } } @@ -977,9 +930,8 @@ static String uneval(Context cx, Scriptable scope, Object value) return value.toString(); } - static String defaultObjectToSource(Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) - { + static String defaultObjectToSource( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { boolean toplevel, iterating; if (cx.iterating == null) { toplevel = true; @@ -1002,30 +954,25 @@ static String defaultObjectToSource(Context cx, Scriptable scope, if (!iterating) { cx.iterating.intern(thisObj); // stop recursion. Object[] ids = thisObj.getIds(); - for (int i=0; i < ids.length; i++) { + for (int i = 0; i < ids.length; i++) { Object id = ids[i]; Object value; if (id instanceof Integer) { - int intId = ((Integer)id).intValue(); + int intId = ((Integer) id).intValue(); value = thisObj.get(intId, thisObj); - if (value == Scriptable.NOT_FOUND) - continue; // a property has been removed - if (i > 0) - result.append(", "); + if (value == Scriptable.NOT_FOUND) continue; // a property has been removed + if (i > 0) result.append(", "); result.append(intId); } else { - String strId = (String)id; + String strId = (String) id; value = thisObj.get(strId, thisObj); - if (value == Scriptable.NOT_FOUND) - continue; // a property has been removed - if (i > 0) - result.append(", "); + if (value == Scriptable.NOT_FOUND) continue; // a property has been removed + if (i > 0) result.append(", "); if (ScriptRuntime.isValidIdentifierName(strId, cx, cx.isStrictMode())) { result.append(strId); } else { result.append('\''); - result.append( - ScriptRuntime.escapeString(strId, '\'')); + result.append(ScriptRuntime.escapeString(strId, '\'')); result.append('\''); } } @@ -1046,54 +993,44 @@ static String defaultObjectToSource(Context cx, Scriptable scope, return result.toString(); } - public static Scriptable toObject(Scriptable scope, Object val) - { + public static Scriptable toObject(Scriptable scope, Object val) { if (val instanceof Scriptable) { - return (Scriptable)val; + return (Scriptable) val; } return toObject(Context.getContext(), scope, val); } /** - * Warning: This doesn't allow to resolve primitive - * prototype properly when many top scopes are involved + * Warning: This doesn't allow to resolve primitive prototype properly when + * many top scopes are involved * * @deprecated Use {@link #toObjectOrNull(Context, Object, Scriptable)} instead */ @Deprecated - public static Scriptable toObjectOrNull(Context cx, Object obj) - { + public static Scriptable toObjectOrNull(Context cx, Object obj) { if (obj instanceof Scriptable) { - return (Scriptable)obj; + return (Scriptable) obj; } else if (obj != null && obj != Undefined.instance) { return toObject(cx, getTopCallScope(cx), obj); } return null; } - /** - * @param scope the scope that should be used to resolve primitive prototype - */ - public static Scriptable toObjectOrNull(Context cx, Object obj, - Scriptable scope) - { + /** @param scope the scope that should be used to resolve primitive prototype */ + public static Scriptable toObjectOrNull(Context cx, Object obj, Scriptable scope) { if (obj instanceof Scriptable) { - return (Scriptable)obj; + return (Scriptable) obj; } else if (obj != null && obj != Undefined.instance) { return toObject(cx, scope, obj); } return null; } - /** - * @deprecated Use {@link #toObject(Scriptable, Object)} instead. - */ + /** @deprecated Use {@link #toObject(Scriptable, Object)} instead. */ @Deprecated - public static Scriptable toObject(Scriptable scope, Object val, - Class staticClass) - { + public static Scriptable toObject(Scriptable scope, Object val, Class staticClass) { if (val instanceof Scriptable) { - return (Scriptable)val; + return (Scriptable) val; } return toObject(Context.getContext(), scope, val); } @@ -1101,10 +1038,9 @@ public static Scriptable toObject(Scriptable scope, Object val, /** * Convert the value to an object. * - * See ECMA 9.9. + *

See ECMA 9.9. */ - public static Scriptable toObject(Context cx, Scriptable scope, Object val) - { + public static Scriptable toObject(Context cx, Scriptable scope, Object val) { if (val == null) { throw typeErrorById("msg.null.to.object"); } @@ -1113,7 +1049,7 @@ public static Scriptable toObject(Context cx, Scriptable scope, Object val) } if (isSymbol(val)) { - NativeSymbol result = new NativeSymbol((NativeSymbol)val); + NativeSymbol result = new NativeSymbol((NativeSymbol) val); setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Symbol); return result; } @@ -1122,49 +1058,42 @@ public static Scriptable toObject(Context cx, Scriptable scope, Object val) } if (val instanceof CharSequence) { // FIXME we want to avoid toString() here, especially for concat() - NativeString result = new NativeString((CharSequence)val); + NativeString result = new NativeString((CharSequence) val); setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.String); return result; } if (val instanceof Number) { - NativeNumber result = new NativeNumber(((Number)val).doubleValue()); + NativeNumber result = new NativeNumber(((Number) val).doubleValue()); setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Number); return result; } if (val instanceof Boolean) { - NativeBoolean result = new NativeBoolean(((Boolean)val).booleanValue()); + NativeBoolean result = new NativeBoolean(((Boolean) val).booleanValue()); setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Boolean); return result; } // Extension: Wrap as a LiveConnect object. Object wrapped = cx.getWrapFactory().wrap(cx, scope, val, null); - if (wrapped instanceof Scriptable) - return (Scriptable) wrapped; + if (wrapped instanceof Scriptable) return (Scriptable) wrapped; throw errorWithClassName("msg.invalid.type", val); } - /** - * @deprecated Use {@link #toObject(Context, Scriptable, Object)} instead. - */ + /** @deprecated Use {@link #toObject(Context, Scriptable, Object)} instead. */ @Deprecated - public static Scriptable toObject(Context cx, Scriptable scope, Object val, - Class staticClass) - { + public static Scriptable toObject( + Context cx, Scriptable scope, Object val, Class staticClass) { return toObject(cx, scope, val); } - /** - * @deprecated The method is only present for compatibility. - */ + /** @deprecated The method is only present for compatibility. */ @Deprecated - public static Object call(Context cx, Object fun, Object thisArg, - Object[] args, Scriptable scope) - { + public static Object call( + Context cx, Object fun, Object thisArg, Object[] args, Scriptable scope) { if (!(fun instanceof Function)) { throw notFunctionError(toString(fun)); } - Function function = (Function)fun; + Function function = (Function) fun; Scriptable thisObj = toObjectOrNull(cx, thisArg, scope); if (thisObj == null) { throw undefCallError(null, "function"); @@ -1172,38 +1101,37 @@ public static Object call(Context cx, Object fun, Object thisArg, return function.call(cx, scope, thisObj, args); } - public static Scriptable newObject(Context cx, Scriptable scope, - String constructorName, Object[] args) - { + public static Scriptable newObject( + Context cx, Scriptable scope, String constructorName, Object[] args) { scope = ScriptableObject.getTopLevelScope(scope); Function ctor = getExistingCtor(cx, scope, constructorName); - if (args == null) { args = ScriptRuntime.emptyArgs; } + if (args == null) { + args = ScriptRuntime.emptyArgs; + } return ctor.construct(cx, scope, args); } - public static Scriptable newBuiltinObject(Context cx, Scriptable scope, - TopLevel.Builtins type, - Object[] args) - { + public static Scriptable newBuiltinObject( + Context cx, Scriptable scope, TopLevel.Builtins type, Object[] args) { scope = ScriptableObject.getTopLevelScope(scope); Function ctor = TopLevel.getBuiltinCtor(cx, scope, type); - if (args == null) { args = ScriptRuntime.emptyArgs; } + if (args == null) { + args = ScriptRuntime.emptyArgs; + } return ctor.construct(cx, scope, args); } - static Scriptable newNativeError(Context cx, Scriptable scope, - TopLevel.NativeErrors type, Object[] args) - { + static Scriptable newNativeError( + Context cx, Scriptable scope, TopLevel.NativeErrors type, Object[] args) { scope = ScriptableObject.getTopLevelScope(scope); Function ctor = TopLevel.getNativeErrorCtor(cx, scope, type); - if (args == null) { args = ScriptRuntime.emptyArgs; } + if (args == null) { + args = ScriptRuntime.emptyArgs; + } return ctor.construct(cx, scope, args); } - /** - * - * See ECMA 9.4. - */ + /** See ECMA 9.4. */ public static double toInteger(Object val) { return toInteger(toNumber(val)); } @@ -1211,14 +1139,11 @@ public static double toInteger(Object val) { // convenience method public static double toInteger(double d) { // if it's NaN - if (Double.isNaN(d)) - return +0.0; + if (Double.isNaN(d)) return +0.0; - if ((d == 0.0) || Double.isInfinite(d)) - return d; + if ((d == 0.0) || Double.isInfinite(d)) return d; - if (d > 0.0) - return Math.floor(d); + if (d > 0.0) return Math.floor(d); return Math.ceil(d); } @@ -1235,15 +1160,10 @@ public static long toLength(Object[] args, int index) { return (long) Math.min(len, NativeNumber.MAX_SAFE_INTEGER); } - /** - * - * See ECMA 9.5. - */ - public static int toInt32(Object val) - { + /** See ECMA 9.5. */ + public static int toInt32(Object val) { // short circuit for common integer values - if (val instanceof Integer) - return ((Integer)val).intValue(); + if (val instanceof Integer) return ((Integer) val).intValue(); return toInt32(toNumber(val)); } @@ -1258,6 +1178,7 @@ public static int toInt32(double d) { /** * See ECMA 9.6. + * * @return long value representing 32 bits unsigned integer */ public static long toUint32(double d) { @@ -1268,21 +1189,17 @@ public static long toUint32(Object val) { return toUint32(toNumber(val)); } - /** - * - * See ECMA 9.7. - */ + /** See ECMA 9.7. */ public static char toUint16(Object val) { double d = toNumber(val); - return (char)DoubleConversion.doubleToInt32(d); + return (char) DoubleConversion.doubleToInt32(d); } // XXX: this is until setDefaultNamespace will learn how to store NS // properly and separates namespace form Scriptable.get etc. private static final String DEFAULT_NS_TAG = "__default_namespace__"; - public static Object setDefaultNamespace(Object namespace, Context cx) - { + public static Object setDefaultNamespace(Object namespace, Context cx) { Scriptable scope = cx.currentActivationCall; if (scope == null) { scope = getTopCallScope(cx); @@ -1294,9 +1211,11 @@ public static Object setDefaultNamespace(Object namespace, Context cx) // XXX : this should be in separated namesapce from Scriptable.get/put if (!scope.has(DEFAULT_NS_TAG, scope)) { // XXX: this is racy of cause - ScriptableObject.defineProperty(scope, DEFAULT_NS_TAG, ns, - ScriptableObject.PERMANENT - | ScriptableObject.DONTENUM); + ScriptableObject.defineProperty( + scope, + DEFAULT_NS_TAG, + ns, + ScriptableObject.PERMANENT | ScriptableObject.DONTENUM); } else { scope.put(DEFAULT_NS_TAG, scope, ns); } @@ -1304,14 +1223,13 @@ public static Object setDefaultNamespace(Object namespace, Context cx) return Undefined.instance; } - public static Object searchDefaultNamespace(Context cx) - { + public static Object searchDefaultNamespace(Context cx) { Scriptable scope = cx.currentActivationCall; if (scope == null) { scope = getTopCallScope(cx); } Object nsObject; - for (;;) { + for (; ; ) { Scriptable parent = scope.getParentScope(); if (parent == null) { nsObject = ScriptableObject.getProperty(scope, DEFAULT_NS_TAG); @@ -1334,27 +1252,23 @@ public static Object getTopLevelProp(Scriptable scope, String id) { return ScriptableObject.getProperty(scope, id); } - static Function getExistingCtor(Context cx, Scriptable scope, - String constructorName) - { + static Function getExistingCtor(Context cx, Scriptable scope, String constructorName) { Object ctorVal = ScriptableObject.getProperty(scope, constructorName); if (ctorVal instanceof Function) { - return (Function)ctorVal; + return (Function) ctorVal; } if (ctorVal == Scriptable.NOT_FOUND) { - throw Context.reportRuntimeErrorById( - "msg.ctor.not.found", constructorName); + throw Context.reportRuntimeErrorById("msg.ctor.not.found", constructorName); } throw Context.reportRuntimeErrorById("msg.not.ctor", constructorName); } /** - * Return -1L if str is not an index, or the index value as lower 32 - * bits of the result. Note that the result needs to be cast to an int - * in order to produce the actual index, which may be negative. + * Return -1L if str is not an index, or the index value as lower 32 bits of the result. Note + * that the result needs to be cast to an int in order to produce the actual index, which may be + * negative. */ - public static long indexFromString(String str) - { + public static long indexFromString(String str) { // The length of the decimal string representation of // Integer.MAX_VALUE, 2147483647 final int MAX_VALUE_LENGTH = 10; @@ -1373,9 +1287,7 @@ public static long indexFromString(String str) } } c -= '0'; - if (0 <= c && c <= 9 - && len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH)) - { + if (0 <= c && c <= 9 && len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH)) { // Use negative numbers to accumulate index to handle // Integer.MIN_VALUE that is greater by 1 in absolute value // then Integer.MAX_VALUE @@ -1384,8 +1296,7 @@ public static long indexFromString(String str) i++; if (index != 0) { // Note that 00, 01, 000 etc. are not indexes - while (i != len && 0 <= (c = str.charAt(i) - '0') && c <= 9) - { + while (i != len && 0 <= (c = str.charAt(i) - '0') && c <= 9) { oldIndex = index; index = 10 * index - c; i++; @@ -1393,12 +1304,13 @@ public static long indexFromString(String str) } // Make sure all characters were consumed and that it couldn't // have overflowed. - if (i == len && - (oldIndex > (Integer.MIN_VALUE / 10) || - (oldIndex == (Integer.MIN_VALUE / 10) && - c <= (negate ? -(Integer.MIN_VALUE % 10) - : (Integer.MAX_VALUE % 10))))) - { + if (i == len + && (oldIndex > (Integer.MIN_VALUE / 10) + || (oldIndex == (Integer.MIN_VALUE / 10) + && c + <= (negate + ? -(Integer.MIN_VALUE % 10) + : (Integer.MAX_VALUE % 10))))) { return 0xFFFFFFFFL & (negate ? index : -index); } } @@ -1406,12 +1318,8 @@ public static long indexFromString(String str) return -1L; } - /** - * If str is a decimal presentation of Uint32 value, return it as long. - * Othewise return -1L; - */ - public static long testUint32String(String str) - { + /** If str is a decimal presentation of Uint32 value, return it as long. Othewise return -1L; */ + public static long testUint32String(String str) { // The length of the decimal string representation of // UINT32_MAX_VALUE, 4294967296 final int MAX_VALUE_LENGTH = 10; @@ -1442,26 +1350,21 @@ public static long testUint32String(String str) return -1; } - /** - * If s represents index, then return index value wrapped as Integer - * and othewise return s. - */ - static Object getIndexObject(String s) - { + /** If s represents index, then return index value wrapped as Integer and othewise return s. */ + static Object getIndexObject(String s) { long indexTest = indexFromString(s); if (indexTest >= 0) { - return Integer.valueOf((int)indexTest); + return Integer.valueOf((int) indexTest); } return s; } /** - * If d is exact int value, return its value wrapped as Integer - * and othewise return d converted to String. + * If d is exact int value, return its value wrapped as Integer and othewise return d converted + * to String. */ - static Object getIndexObject(double d) - { - int i = (int)d; + static Object getIndexObject(double d) { + int i = (int) d; if (i == d) { return Integer.valueOf(i); } @@ -1469,8 +1372,7 @@ static Object getIndexObject(double d) } /** - * Helper to return a string or an integer. - * Always use a null check on s.stringId to determine + * Helper to return a string or an integer. Always use a null check on s.stringId to determine * if the result is string or integer. * * @see ScriptRuntime#toStringIdOrIndex(Context, Object) @@ -1491,15 +1393,14 @@ static final class StringIdOrIndex { } /** - * If toString(id) is a decimal presentation of int32 value, then id - * is index. In this case return null and make the index available - * as ScriptRuntime.lastIndexResult(cx). Otherwise return toString(id). + * If toString(id) is a decimal presentation of int32 value, then id is index. In this case + * return null and make the index available as ScriptRuntime.lastIndexResult(cx). Otherwise + * return toString(id). */ - static StringIdOrIndex toStringIdOrIndex(Context cx, Object id) - { + static StringIdOrIndex toStringIdOrIndex(Context cx, Object id) { if (id instanceof Number) { - double d = ((Number)id).doubleValue(); - int index = (int)d; + double d = ((Number) id).doubleValue(); + int index = (int) d; if (index == d) { return new StringIdOrIndex(index); } @@ -1507,13 +1408,13 @@ static StringIdOrIndex toStringIdOrIndex(Context cx, Object id) } String s; if (id instanceof String) { - s = (String)id; + s = (String) id; } else { s = toString(id); } long indexTest = indexFromString(s); if (indexTest >= 0) { - return new StringIdOrIndex((int)indexTest); + return new StringIdOrIndex((int) indexTest); } return new StringIdOrIndex(s); } @@ -1524,16 +1425,12 @@ static StringIdOrIndex toStringIdOrIndex(Context cx, Object id) * @deprecated Use {@link #getObjectElem(Object, Object, Context, Scriptable)} instead */ @Deprecated - public static Object getObjectElem(Object obj, Object elem, Context cx) - { + public static Object getObjectElem(Object obj, Object elem, Context cx) { return getObjectElem(obj, elem, cx, getTopCallScope(cx)); } - /** - * Call obj.[[Get]](id) - */ - public static Object getObjectElem(Object obj, Object elem, Context cx, Scriptable scope) - { + /** Call obj.[[Get]](id) */ + public static Object getObjectElem(Object obj, Object elem, Context cx, Scriptable scope) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { throw undefReadError(obj, elem); @@ -1541,16 +1438,14 @@ public static Object getObjectElem(Object obj, Object elem, Context cx, Scriptab return getObjectElem(sobj, elem, cx); } - public static Object getObjectElem(Scriptable obj, Object elem, - Context cx) - { + public static Object getObjectElem(Scriptable obj, Object elem, Context cx) { Object result; if (obj instanceof XMLObject) { - result = ((XMLObject)obj).get(cx, elem); + result = ((XMLObject) obj).get(cx, elem); } else if (isSymbol(elem)) { - result = ScriptableObject.getProperty(obj, (Symbol)elem); + result = ScriptableObject.getProperty(obj, (Symbol) elem); } else { StringIdOrIndex s = toStringIdOrIndex(cx, elem); if (s.stringId == null) { @@ -1574,9 +1469,7 @@ public static Object getObjectElem(Scriptable obj, Object elem, * @deprecated Use {@link #getObjectProp(Object, String, Context, Scriptable)} instead */ @Deprecated - public static Object getObjectProp(Object obj, String property, - Context cx) - { + public static Object getObjectProp(Object obj, String property, Context cx) { return getObjectProp(obj, property, cx, getTopCallScope(cx)); } @@ -1585,9 +1478,7 @@ public static Object getObjectProp(Object obj, String property, * * @param scope the scope that should be used to resolve primitive prototype */ - public static Object getObjectProp(Object obj, String property, - Context cx, Scriptable scope) - { + public static Object getObjectProp(Object obj, String property, Context cx, Scriptable scope) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { throw undefReadError(obj, property); @@ -1595,15 +1486,13 @@ public static Object getObjectProp(Object obj, String property, return getObjectProp(sobj, property, cx); } - public static Object getObjectProp(Scriptable obj, String property, - Context cx) - { + public static Object getObjectProp(Scriptable obj, String property, Context cx) { Object result = ScriptableObject.getProperty(obj, property); if (result == Scriptable.NOT_FOUND) { if (cx.hasFeature(Context.FEATURE_STRICT_MODE)) { - Context.reportWarning(ScriptRuntime.getMessageById( - "msg.ref.undefined.prop", property)); + Context.reportWarning( + ScriptRuntime.getMessageById("msg.ref.undefined.prop", property)); } result = Undefined.instance; } @@ -1611,56 +1500,43 @@ public static Object getObjectProp(Scriptable obj, String property, return result; } - /** - * @deprecated Use {@link #getObjectPropNoWarn(Object, String, Context, Scriptable)} instead - */ + /** @deprecated Use {@link #getObjectPropNoWarn(Object, String, Context, Scriptable)} instead */ @Deprecated - public static Object getObjectPropNoWarn(Object obj, String property, - Context cx) - { + public static Object getObjectPropNoWarn(Object obj, String property, Context cx) { return getObjectPropNoWarn(obj, property, cx, getTopCallScope(cx)); } - public static Object getObjectPropNoWarn(Object obj, String property, - Context cx, Scriptable scope) - { + public static Object getObjectPropNoWarn( + Object obj, String property, Context cx, Scriptable scope) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { throw undefReadError(obj, property); } Object result = ScriptableObject.getProperty(sobj, property); if (result == Scriptable.NOT_FOUND) { - return Undefined.instance; + return Undefined.instance; } return result; } /** - * A cheaper and less general version of the above for well-known argument - * types. + * A cheaper and less general version of the above for well-known argument types. * * @deprecated Use {@link #getObjectIndex(Object, double, Context, Scriptable)} instead */ @Deprecated - public static Object getObjectIndex(Object obj, double dblIndex, - Context cx) - { + public static Object getObjectIndex(Object obj, double dblIndex, Context cx) { return getObjectIndex(obj, dblIndex, cx, getTopCallScope(cx)); } - /** - * A cheaper and less general version of the above for well-known argument - * types. - */ - public static Object getObjectIndex(Object obj, double dblIndex, - Context cx, Scriptable scope) - { + /** A cheaper and less general version of the above for well-known argument types. */ + public static Object getObjectIndex(Object obj, double dblIndex, Context cx, Scriptable scope) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { throw undefReadError(obj, toString(dblIndex)); } - int index = (int)dblIndex; + int index = (int) dblIndex; if (index == dblIndex) { return getObjectIndex(sobj, index, cx); } @@ -1668,9 +1544,7 @@ public static Object getObjectIndex(Object obj, double dblIndex, return getObjectProp(sobj, s, cx); } - public static Object getObjectIndex(Scriptable obj, int index, - Context cx) - { + public static Object getObjectIndex(Scriptable obj, int index, Context cx) { Object result = ScriptableObject.getProperty(obj, index); if (result == Scriptable.NOT_FOUND) { result = Undefined.instance; @@ -1685,18 +1559,13 @@ public static Object getObjectIndex(Scriptable obj, int index, * @deprecated Use {@link #setObjectElem(Object, Object, Object, Context, Scriptable)} instead */ @Deprecated - public static Object setObjectElem(Object obj, Object elem, Object value, - Context cx) - { + public static Object setObjectElem(Object obj, Object elem, Object value, Context cx) { return setObjectElem(obj, elem, value, cx, getTopCallScope(cx)); } - /** - * Call obj.[[Put]](id, value) - */ - public static Object setObjectElem(Object obj, Object elem, Object value, - Context cx, Scriptable scope) - { + /** Call obj.[[Put]](id, value) */ + public static Object setObjectElem( + Object obj, Object elem, Object value, Context cx, Scriptable scope) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { throw undefWriteError(obj, elem, value); @@ -1704,13 +1573,11 @@ public static Object setObjectElem(Object obj, Object elem, Object value, return setObjectElem(sobj, elem, value, cx); } - public static Object setObjectElem(Scriptable obj, Object elem, - Object value, Context cx) - { + public static Object setObjectElem(Scriptable obj, Object elem, Object value, Context cx) { if (obj instanceof XMLObject) { - ((XMLObject)obj).put(cx, elem, value); + ((XMLObject) obj).put(cx, elem, value); } else if (isSymbol(elem)) { - ScriptableObject.putProperty(obj, (Symbol)elem, value); + ScriptableObject.putProperty(obj, (Symbol) elem, value); } else { StringIdOrIndex s = toStringIdOrIndex(cx, elem); if (s.stringId == null) { @@ -1729,19 +1596,13 @@ public static Object setObjectElem(Scriptable obj, Object elem, * @deprecated Use {@link #setObjectProp(Object, String, Object, Context, Scriptable)} instead */ @Deprecated - public static Object setObjectProp(Object obj, String property, - Object value, Context cx) - { + public static Object setObjectProp(Object obj, String property, Object value, Context cx) { return setObjectProp(obj, property, value, cx, getTopCallScope(cx)); } - /** - * Version of setObjectElem when elem is a valid JS identifier name. - */ - public static Object setObjectProp(Object obj, String property, - Object value, Context cx, - Scriptable scope) - { + /** Version of setObjectElem when elem is a valid JS identifier name. */ + public static Object setObjectProp( + Object obj, String property, Object value, Context cx, Scriptable scope) { if (!(obj instanceof Scriptable) && cx.isStrictMode() && cx.getLanguageVersion() >= Context.VERSION_1_8) { @@ -1756,40 +1617,30 @@ public static Object setObjectProp(Object obj, String property, return setObjectProp(sobj, property, value, cx); } - public static Object setObjectProp(Scriptable obj, String property, - Object value, Context cx) - { + public static Object setObjectProp(Scriptable obj, String property, Object value, Context cx) { ScriptableObject.putProperty(obj, property, value); return value; } /** - * A cheaper and less general version of the above for well-known argument - * types. + * A cheaper and less general version of the above for well-known argument types. * * @deprecated Use {@link #setObjectIndex(Object, double, Object, Context, Scriptable)} instead */ @Deprecated - public static Object setObjectIndex(Object obj, double dblIndex, - Object value, Context cx) - { + public static Object setObjectIndex(Object obj, double dblIndex, Object value, Context cx) { return setObjectIndex(obj, dblIndex, value, cx, getTopCallScope(cx)); } - /** - * A cheaper and less general version of the above for well-known argument - * types. - */ - public static Object setObjectIndex(Object obj, double dblIndex, - Object value, Context cx, - Scriptable scope) - { + /** A cheaper and less general version of the above for well-known argument types. */ + public static Object setObjectIndex( + Object obj, double dblIndex, Object value, Context cx, Scriptable scope) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { throw undefWriteError(obj, String.valueOf(dblIndex), value); } - int index = (int)dblIndex; + int index = (int) dblIndex; if (index == dblIndex) { return setObjectIndex(sobj, index, value, cx); } @@ -1797,19 +1648,15 @@ public static Object setObjectIndex(Object obj, double dblIndex, return setObjectProp(sobj, s, value, cx); } - public static Object setObjectIndex(Scriptable obj, int index, Object value, - Context cx) - { + public static Object setObjectIndex(Scriptable obj, int index, Object value, Context cx) { ScriptableObject.putProperty(obj, index, value); return value; } - public static boolean deleteObjectElem(Scriptable target, Object elem, - Context cx) - { + public static boolean deleteObjectElem(Scriptable target, Object elem, Context cx) { if (isSymbol(elem)) { SymbolScriptable so = ScriptableObject.ensureSymbolScriptable(target); - Symbol s = (Symbol)elem; + Symbol s = (Symbol) elem; so.delete(s); return !so.has(s, target); } @@ -1822,13 +1669,11 @@ public static boolean deleteObjectElem(Scriptable target, Object elem, return !target.has(s.stringId, target); } - public static boolean hasObjectElem(Scriptable target, Object elem, - Context cx) - { + public static boolean hasObjectElem(Scriptable target, Object elem, Context cx) { boolean result; if (isSymbol(elem)) { - result = ScriptableObject.hasProperty(target, (Symbol)elem); + result = ScriptableObject.hasProperty(target, (Symbol) elem); } else { StringIdOrIndex s = toStringIdOrIndex(cx, elem); if (s.stringId == null) { @@ -1841,94 +1686,71 @@ public static boolean hasObjectElem(Scriptable target, Object elem, return result; } - public static Object refGet(Ref ref, Context cx) - { + public static Object refGet(Ref ref, Context cx) { return ref.get(cx); } - /** - * @deprecated Use {@link #refSet(Ref, Object, Context, Scriptable)} instead - */ + /** @deprecated Use {@link #refSet(Ref, Object, Context, Scriptable)} instead */ @Deprecated - public static Object refSet(Ref ref, Object value, Context cx) - { + public static Object refSet(Ref ref, Object value, Context cx) { return refSet(ref, value, cx, getTopCallScope(cx)); } - public static Object refSet(Ref ref, Object value, Context cx, - Scriptable scope) - { + public static Object refSet(Ref ref, Object value, Context cx, Scriptable scope) { return ref.set(cx, scope, value); } - public static Object refDel(Ref ref, Context cx) - { + public static Object refDel(Ref ref, Context cx) { return wrapBoolean(ref.delete(cx)); } - static boolean isSpecialProperty(String s) - { + static boolean isSpecialProperty(String s) { return s.equals("__proto__") || s.equals("__parent__"); } - /** - * @deprecated Use {@link #specialRef(Object, String, Context, Scriptable)} instead - */ + /** @deprecated Use {@link #specialRef(Object, String, Context, Scriptable)} instead */ @Deprecated - public static Ref specialRef(Object obj, String specialProperty, - Context cx) - { + public static Ref specialRef(Object obj, String specialProperty, Context cx) { return specialRef(obj, specialProperty, cx, getTopCallScope(cx)); } - public static Ref specialRef(Object obj, String specialProperty, - Context cx, Scriptable scope) - { + public static Ref specialRef(Object obj, String specialProperty, Context cx, Scriptable scope) { return SpecialRef.createSpecial(cx, scope, obj, specialProperty); } - /** - * @deprecated Use {@link #delete(Object, Object, Context, Scriptable, boolean)} instead - */ + /** @deprecated Use {@link #delete(Object, Object, Context, Scriptable, boolean)} instead */ @Deprecated - public static Object delete(Object obj, Object id, Context cx) - { + public static Object delete(Object obj, Object id, Context cx) { return delete(obj, id, cx, false); } /** * The delete operator * - * See ECMA 11.4.1 + *

See ECMA 11.4.1 * - * In ECMA 0.19, the description of the delete operator (11.4.1) - * assumes that the [[Delete]] method returns a value. However, - * the definition of the [[Delete]] operator (8.6.2.5) does not - * define a return value. Here we assume that the [[Delete]] - * method doesn't return a value. + *

In ECMA 0.19, the description of the delete operator (11.4.1) assumes that the [[Delete]] + * method returns a value. However, the definition of the [[Delete]] operator (8.6.2.5) does not + * define a return value. Here we assume that the [[Delete]] method doesn't return a value. * * @deprecated Use {@link #delete(Object, Object, Context, Scriptable, boolean)} instead */ @Deprecated - public static Object delete(Object obj, Object id, Context cx, boolean isName) - { + public static Object delete(Object obj, Object id, Context cx, boolean isName) { return delete(obj, id, cx, getTopCallScope(cx), isName); } /** * The delete operator * - * See ECMA 11.4.1 + *

See ECMA 11.4.1 * - * In ECMA 0.19, the description of the delete operator (11.4.1) - * assumes that the [[Delete]] method returns a value. However, - * the definition of the [[Delete]] operator (8.6.2.5) does not - * define a return value. Here we assume that the [[Delete]] - * method doesn't return a value. + *

In ECMA 0.19, the description of the delete operator (11.4.1) assumes that the [[Delete]] + * method returns a value. However, the definition of the [[Delete]] operator (8.6.2.5) does not + * define a return value. Here we assume that the [[Delete]] method doesn't return a value. */ - public static Object delete(Object obj, Object id, Context cx, - Scriptable scope, boolean isName) - { + public static Object delete( + Object obj, Object id, Context cx, Scriptable scope, boolean isName) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { if (isName) { @@ -1940,11 +1762,8 @@ public static Object delete(Object obj, Object id, Context cx, return wrapBoolean(result); } - /** - * Looks up a name in the scope chain and returns its value. - */ - public static Object name(Context cx, Scriptable scope, String name) - { + /** Looks up a name in the scope chain and returns its value. */ + public static Object name(Context cx, Scriptable scope, String name) { Scriptable parent = scope.getParentScope(); if (parent == null) { Object result = topScopeName(cx, scope, name); @@ -1957,19 +1776,21 @@ public static Object name(Context cx, Scriptable scope, String name) return nameOrFunction(cx, scope, parent, name, false); } - private static Object nameOrFunction(Context cx, Scriptable scope, - Scriptable parentScope, String name, - boolean asFunctionCall) - { + private static Object nameOrFunction( + Context cx, + Scriptable scope, + Scriptable parentScope, + String name, + boolean asFunctionCall) { Object result; Scriptable thisObj = scope; // It is used only if asFunctionCall==true. XMLObject firstXMLObject = null; - for (;;) { + for (; ; ) { if (scope instanceof NativeWith) { Scriptable withObj = scope.getPrototype(); if (withObj instanceof XMLObject) { - XMLObject xmlObj = (XMLObject)withObj; + XMLObject xmlObj = (XMLObject) withObj; if (xmlObj.has(name, xmlObj)) { // function this should be the target object of with thisObj = xmlObj; @@ -1995,8 +1816,7 @@ private static Object nameOrFunction(Context cx, Scriptable scope, if (asFunctionCall) { // ECMA 262 requires that this for nested funtions // should be top scope - thisObj = ScriptableObject. - getTopLevelScope(parentScope); + thisObj = ScriptableObject.getTopLevelScope(parentScope); } break; } @@ -2039,39 +1859,34 @@ private static Object nameOrFunction(Context cx, Scriptable scope, return result; } - private static Object topScopeName(Context cx, Scriptable scope, - String name) - { + private static Object topScopeName(Context cx, Scriptable scope, String name) { if (cx.useDynamicScope) { scope = checkDynamicScope(cx.topCallScope, scope); } return ScriptableObject.getProperty(scope, name); } - /** * Returns the object in the scope chain that has a given property. * - * The order of evaluation of an assignment expression involves - * evaluating the lhs to a reference, evaluating the rhs, and then - * modifying the reference with the rhs value. This method is used - * to 'bind' the given name to an object containing that property - * so that the side effects of evaluating the rhs do not affect - * which property is modified. - * Typically used in conjunction with setName. + *

The order of evaluation of an assignment expression involves evaluating the lhs to a + * reference, evaluating the rhs, and then modifying the reference with the rhs value. This + * method is used to 'bind' the given name to an object containing that property so that the + * side effects of evaluating the rhs do not affect which property is modified. Typically used + * in conjunction with setName. * - * See ECMA 10.1.4 + *

See ECMA 10.1.4 */ - public static Scriptable bind(Context cx, Scriptable scope, String id) - { + public static Scriptable bind(Context cx, Scriptable scope, String id) { Scriptable firstXMLObject = null; Scriptable parent = scope.getParentScope(); - childScopesChecks: if (parent != null) { + childScopesChecks: + if (parent != null) { // Check for possibly nested "with" scopes first while (scope instanceof NativeWith) { Scriptable withObj = scope.getPrototype(); if (withObj instanceof XMLObject) { - XMLObject xmlObject = (XMLObject)withObj; + XMLObject xmlObject = (XMLObject) withObj; if (xmlObject.has(cx, id)) { return xmlObject; } @@ -2089,7 +1904,7 @@ public static Scriptable bind(Context cx, Scriptable scope, String id) break childScopesChecks; } } - for (;;) { + for (; ; ) { if (ScriptableObject.hasProperty(scope, id)) { return scope; } @@ -2112,9 +1927,8 @@ public static Scriptable bind(Context cx, Scriptable scope, String id) return firstXMLObject; } - public static Object setName(Scriptable bound, Object value, - Context cx, Scriptable scope, String id) - { + public static Object setName( + Scriptable bound, Object value, Context cx, Scriptable scope, String id) { if (bound != null) { // TODO: we used to special-case XMLObject here, but putProperty // seems to work for E4X and it's better to optimize the common case @@ -2123,11 +1937,9 @@ public static Object setName(Scriptable bound, Object value, // "newname = 7;", where 'newname' has not yet // been defined, creates a new property in the // top scope unless strict mode is specified. - if (cx.hasFeature(Context.FEATURE_STRICT_MODE) || - cx.hasFeature(Context.FEATURE_STRICT_VARS)) - { - Context.reportWarning( - ScriptRuntime.getMessageById("msg.assn.create.strict", id)); + if (cx.hasFeature(Context.FEATURE_STRICT_MODE) + || cx.hasFeature(Context.FEATURE_STRICT_VARS)) { + Context.reportWarning(ScriptRuntime.getMessageById("msg.assn.create.strict", id)); } // Find the top scope by walking up the scope chain. bound = ScriptableObject.getTopLevelScope(scope); @@ -2139,8 +1951,8 @@ public static Object setName(Scriptable bound, Object value, return value; } - public static Object strictSetName(Scriptable bound, Object value, - Context cx, Scriptable scope, String id) { + public static Object strictSetName( + Scriptable bound, Object value, Context cx, Scriptable scope, String id) { if (bound != null) { // TODO: The LeftHandSide also may not be a reference to a // data property with the attribute value {[[Writable]]:false}, @@ -2158,9 +1970,7 @@ public static Object strictSetName(Scriptable bound, Object value, throw constructError("ReferenceError", msg); } - public static Object setConst(Scriptable bound, Object value, - Context cx, String id) - { + public static Object setConst(Scriptable bound, Object value, Context cx, String id) { if (bound instanceof XMLObject) { bound.put(id, bound, value); } else { @@ -2172,17 +1982,14 @@ public static Object setConst(Scriptable bound, Object value, /** * This is the enumeration needed by the for..in statement. * - * See ECMA 12.6.3. - * - * IdEnumeration maintains a ObjToIntMap to make sure a given - * id is enumerated only once across multiple objects in a - * prototype chain. + *

See ECMA 12.6.3. * - * XXX - ECMA delete doesn't hide properties in the prototype, - * but js/ref does. This means that the js/ref for..in can - * avoid maintaining a hash table and instead perform lookups - * to see if a given property has already been enumerated. + *

IdEnumeration maintains a ObjToIntMap to make sure a given id is enumerated only once + * across multiple objects in a prototype chain. * + *

XXX - ECMA delete doesn't hide properties in the prototype, but js/ref does. This means + * that the js/ref for..in can avoid maintaining a hash table and instead perform lookups to see + * if a given property has already been enumerated. */ private static class IdEnumeration implements Serializable { private static final long serialVersionUID = 1L; @@ -2195,25 +2002,20 @@ private static class IdEnumeration implements Serializable { ENUM_INIT_ARRAY, ENUMERATE_VALUES_IN_ORDER */ // if true, integer ids will be returned as numbers rather than strings - boolean enumNumbers; + boolean enumNumbers; Scriptable iterator; } - public static Scriptable toIterator(Context cx, Scriptable scope, - Scriptable obj, boolean keyOnly) - { - if (ScriptableObject.hasProperty(obj, - NativeIterator.ITERATOR_PROPERTY_NAME)) - { - Object v = ScriptableObject.getProperty(obj, - NativeIterator.ITERATOR_PROPERTY_NAME); + public static Scriptable toIterator( + Context cx, Scriptable scope, Scriptable obj, boolean keyOnly) { + if (ScriptableObject.hasProperty(obj, NativeIterator.ITERATOR_PROPERTY_NAME)) { + Object v = ScriptableObject.getProperty(obj, NativeIterator.ITERATOR_PROPERTY_NAME); if (!(v instanceof Callable)) { - throw typeErrorById("msg.invalid.iterator"); + throw typeErrorById("msg.invalid.iterator"); } Callable f = (Callable) v; - Object[] args = new Object[] { keyOnly ? Boolean.TRUE - : Boolean.FALSE }; + Object[] args = new Object[] {keyOnly ? Boolean.TRUE : Boolean.FALSE}; v = f.call(cx, scope, obj, args); if (!(v instanceof Scriptable)) { throw typeErrorById("msg.iterator.primitive"); @@ -2229,10 +2031,8 @@ public static Scriptable toIterator(Context cx, Scriptable scope, * @deprecated Use {@link #enumInit(Object, Context, Scriptable, int)} instead */ @Deprecated - public static Object enumInit(Object value, Context cx, boolean enumValues) - { - return enumInit(value, cx, enumValues ? ENUMERATE_VALUES - : ENUMERATE_KEYS); + public static Object enumInit(Object value, Context cx, boolean enumValues) { + return enumInit(value, cx, enumValues ? ENUMERATE_VALUES : ENUMERATE_KEYS); } public static final int ENUMERATE_KEYS = 0; @@ -2243,18 +2043,13 @@ public static Object enumInit(Object value, Context cx, boolean enumValues) public static final int ENUMERATE_ARRAY_NO_ITERATOR = 5; public static final int ENUMERATE_VALUES_IN_ORDER = 6; - /** - * @deprecated Use {@link #enumInit(Object, Context, Scriptable, int)} instead - */ + /** @deprecated Use {@link #enumInit(Object, Context, Scriptable, int)} instead */ @Deprecated - public static Object enumInit(Object value, Context cx, int enumType) - { + public static Object enumInit(Object value, Context cx, int enumType) { return enumInit(value, cx, getTopCallScope(cx), enumType); } - public static Object enumInit(Object value, Context cx, Scriptable scope, - int enumType) - { + public static Object enumInit(Object value, Context cx, Scriptable scope, int enumType) { IdEnumeration x = new IdEnumeration(); x.obj = toObjectOrNull(cx, value, scope); // "for of" loop @@ -2270,12 +2065,15 @@ public static Object enumInit(Object value, Context cx, Scriptable scope, } x.enumType = enumType; x.iterator = null; - if (enumType != ENUMERATE_KEYS_NO_ITERATOR && - enumType != ENUMERATE_VALUES_NO_ITERATOR && - enumType != ENUMERATE_ARRAY_NO_ITERATOR) - { - x.iterator = toIterator(cx, x.obj.getParentScope(), x.obj, - enumType == ScriptRuntime.ENUMERATE_KEYS); + if (enumType != ENUMERATE_KEYS_NO_ITERATOR + && enumType != ENUMERATE_VALUES_NO_ITERATOR + && enumType != ENUMERATE_ARRAY_NO_ITERATOR) { + x.iterator = + toIterator( + cx, + x.obj.getParentScope(), + x.obj, + enumType == ScriptRuntime.ENUMERATE_KEYS); } if (x.iterator == null) { // enumInit should read all initial ids before returning @@ -2287,7 +2085,8 @@ public static Object enumInit(Object value, Context cx, Scriptable scope, } private static Object enumInitInOrder(Context cx, IdEnumeration x) { - if (!(x.obj instanceof SymbolScriptable) || !ScriptableObject.hasProperty(x.obj, SymbolKey.ITERATOR)) { + if (!(x.obj instanceof SymbolScriptable) + || !ScriptableObject.hasProperty(x.obj, SymbolKey.ITERATOR)) { throw typeErrorById("msg.not.iterable", toString(x.obj)); } @@ -2307,24 +2106,21 @@ private static Object enumInitInOrder(Context cx, IdEnumeration x) { } public static void setEnumNumbers(Object enumObj, boolean enumNumbers) { - ((IdEnumeration)enumObj).enumNumbers = enumNumbers; + ((IdEnumeration) enumObj).enumNumbers = enumNumbers; } - public static Boolean enumNext(Object enumObj) - { - IdEnumeration x = (IdEnumeration)enumObj; + public static Boolean enumNext(Object enumObj) { + IdEnumeration x = (IdEnumeration) enumObj; if (x.iterator != null) { if (x.enumType == ENUMERATE_VALUES_IN_ORDER) { return enumNextInOrder(x); } Object v = ScriptableObject.getProperty(x.iterator, "next"); - if (!(v instanceof Callable)) - return Boolean.FALSE; + if (!(v instanceof Callable)) return Boolean.FALSE; Callable f = (Callable) v; Context cx = Context.getContext(); try { - x.currentId = f.call(cx, x.iterator.getParentScope(), - x.iterator, emptyArgs); + x.currentId = f.call(cx, x.iterator.getParentScope(), x.iterator, emptyArgs); return Boolean.TRUE; } catch (JavaScriptException e) { if (e.getValue() instanceof NativeIterator.StopIteration) { @@ -2333,7 +2129,7 @@ public static Boolean enumNext(Object enumObj) throw e; } } - for (;;) { + for (; ; ) { if (x.obj == null) { return Boolean.FALSE; } @@ -2350,22 +2146,19 @@ public static Boolean enumNext(Object enumObj) continue; } else if (id instanceof String) { String strId = (String) id; - if (!x.obj.has(strId, x.obj)) - continue; // must have been deleted + if (!x.obj.has(strId, x.obj)) continue; // must have been deleted x.currentId = strId; } else { - int intId = ((Number)id).intValue(); - if (!x.obj.has(intId, x.obj)) - continue; // must have been deleted - x.currentId = x.enumNumbers ? (Object) (Integer.valueOf(intId)) - : String.valueOf(intId); + int intId = ((Number) id).intValue(); + if (!x.obj.has(intId, x.obj)) continue; // must have been deleted + x.currentId = + x.enumNumbers ? (Object) (Integer.valueOf(intId)) : String.valueOf(intId); } return Boolean.TRUE; } } - private static Boolean enumNextInOrder(IdEnumeration enumObj) - { + private static Boolean enumNextInOrder(IdEnumeration enumObj) { Object v = ScriptableObject.getProperty(enumObj.iterator, ES6Iterator.NEXT_METHOD); if (!(v instanceof Callable)) { throw notFunctionError(enumObj.iterator, ES6Iterator.NEXT_METHOD); @@ -2379,40 +2172,40 @@ private static Boolean enumNextInOrder(IdEnumeration enumObj) if (done != Scriptable.NOT_FOUND && toBoolean(done)) { return Boolean.FALSE; } - enumObj.currentId = ScriptableObject.getProperty(iteratorResult, ES6Iterator.VALUE_PROPERTY); + enumObj.currentId = + ScriptableObject.getProperty(iteratorResult, ES6Iterator.VALUE_PROPERTY); return Boolean.TRUE; } - public static Object enumId(Object enumObj, Context cx) - { - IdEnumeration x = (IdEnumeration)enumObj; + public static Object enumId(Object enumObj, Context cx) { + IdEnumeration x = (IdEnumeration) enumObj; if (x.iterator != null) { return x.currentId; } switch (x.enumType) { - case ENUMERATE_KEYS: - case ENUMERATE_KEYS_NO_ITERATOR: - return x.currentId; - case ENUMERATE_VALUES: - case ENUMERATE_VALUES_NO_ITERATOR: - return enumValue(enumObj, cx); - case ENUMERATE_ARRAY: - case ENUMERATE_ARRAY_NO_ITERATOR: - Object[] elements = { x.currentId, enumValue(enumObj, cx) }; - return cx.newArray(ScriptableObject.getTopLevelScope(x.obj), elements); - default: - throw Kit.codeBug(); + case ENUMERATE_KEYS: + case ENUMERATE_KEYS_NO_ITERATOR: + return x.currentId; + case ENUMERATE_VALUES: + case ENUMERATE_VALUES_NO_ITERATOR: + return enumValue(enumObj, cx); + case ENUMERATE_ARRAY: + case ENUMERATE_ARRAY_NO_ITERATOR: + Object[] elements = {x.currentId, enumValue(enumObj, cx)}; + return cx.newArray(ScriptableObject.getTopLevelScope(x.obj), elements); + default: + throw Kit.codeBug(); } } public static Object enumValue(Object enumObj, Context cx) { - IdEnumeration x = (IdEnumeration)enumObj; + IdEnumeration x = (IdEnumeration) enumObj; Object result; if (isSymbol(x.currentId)) { SymbolScriptable so = ScriptableObject.ensureSymbolScriptable(x.obj); - result = so.get((Symbol)x.currentId, x.obj); + result = so.get((Symbol) x.currentId, x.obj); } else { StringIdOrIndex s = toStringIdOrIndex(cx, x.currentId); if (s.stringId == null) { @@ -2425,8 +2218,7 @@ public static Object enumValue(Object enumObj, Context cx) { return result; } - private static void enumChangeObject(IdEnumeration x) - { + private static void enumChangeObject(IdEnumeration x) { Object[] ids = null; while (x.obj != null) { ids = x.obj.getIds(); @@ -2450,16 +2242,11 @@ private static void enumChangeObject(IdEnumeration x) } /** - * Prepare for calling name(...): return function corresponding to - * name and make current top scope available - * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. - * The caller must call ScriptRuntime.lastStoredScriptable() immediately - * after calling this method. + * Prepare for calling name(...): return function corresponding to name and make current top + * scope available as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. The + * caller must call ScriptRuntime.lastStoredScriptable() immediately after calling this method. */ - public static Callable getNameFunctionAndThis(String name, - Context cx, - Scriptable scope) - { + public static Callable getNameFunctionAndThis(String name, Context cx, Scriptable scope) { Scriptable parent = scope.getParentScope(); if (parent == null) { Object result = topScopeName(cx, scope, name); @@ -2472,40 +2259,34 @@ public static Callable getNameFunctionAndThis(String name, // Top scope is not NativeWith or NativeCall => thisObj == scope Scriptable thisObj = scope; storeScriptable(cx, thisObj); - return (Callable)result; + return (Callable) result; } // name will call storeScriptable(cx, thisObj); - return (Callable)nameOrFunction(cx, scope, parent, name, true); + return (Callable) nameOrFunction(cx, scope, parent, name, true); } /** - * Prepare for calling obj[id](...): return function corresponding to - * obj[id] and make obj properly converted to Scriptable available - * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. - * The caller must call ScriptRuntime.lastStoredScriptable() immediately + * Prepare for calling obj[id](...): return function corresponding to obj[id] and make obj + * properly converted to Scriptable available as ScriptRuntime.lastStoredScriptable() for + * consumption as thisObj. The caller must call ScriptRuntime.lastStoredScriptable() immediately * after calling this method. * * @deprecated Use {@link #getElemFunctionAndThis(Object, Object, Context, Scriptable)} instead */ @Deprecated - public static Callable getElemFunctionAndThis(Object obj, - Object elem, - Context cx) - { + public static Callable getElemFunctionAndThis(Object obj, Object elem, Context cx) { return getElemFunctionAndThis(obj, elem, cx, getTopCallScope(cx)); } /** - * Prepare for calling obj[id](...): return function corresponding to - * obj[id] and make obj properly converted to Scriptable available - * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. - * The caller must call ScriptRuntime.lastStoredScriptable() immediately + * Prepare for calling obj[id](...): return function corresponding to obj[id] and make obj + * properly converted to Scriptable available as ScriptRuntime.lastStoredScriptable() for + * consumption as thisObj. The caller must call ScriptRuntime.lastStoredScriptable() immediately * after calling this method. */ - public static Callable getElemFunctionAndThis(Object obj, Object elem, - Context cx, Scriptable scope) - { + public static Callable getElemFunctionAndThis( + Object obj, Object elem, Context cx, Scriptable scope) { Scriptable thisObj; Object value; @@ -2514,7 +2295,7 @@ public static Callable getElemFunctionAndThis(Object obj, Object elem, if (thisObj == null) { throw undefCallError(obj, String.valueOf(elem)); } - value = ScriptableObject.getProperty(thisObj, (Symbol)elem); + value = ScriptableObject.getProperty(thisObj, (Symbol) elem); } else { StringIdOrIndex s = toStringIdOrIndex(cx, elem); @@ -2535,46 +2316,37 @@ public static Callable getElemFunctionAndThis(Object obj, Object elem, } storeScriptable(cx, thisObj); - return (Callable)value; + return (Callable) value; } /** - * Prepare for calling obj.property(...): return function corresponding to - * obj.property and make obj properly converted to Scriptable available - * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. - * The caller must call ScriptRuntime.lastStoredScriptable() immediately - * after calling this method. - * Warning: this doesn't allow to resolve primitive prototype properly when - * many top scopes are involved. + * Prepare for calling obj.property(...): return function corresponding to obj.property and make + * obj properly converted to Scriptable available as ScriptRuntime.lastStoredScriptable() for + * consumption as thisObj. The caller must call ScriptRuntime.lastStoredScriptable() immediately + * after calling this method. Warning: this doesn't allow to resolve primitive prototype + * properly when many top scopes are involved. * * @deprecated Use {@link #getPropFunctionAndThis(Object, String, Context, Scriptable)} instead */ @Deprecated - public static Callable getPropFunctionAndThis(Object obj, - String property, - Context cx) - { + public static Callable getPropFunctionAndThis(Object obj, String property, Context cx) { return getPropFunctionAndThis(obj, property, cx, getTopCallScope(cx)); } /** - * Prepare for calling obj.property(...): return function corresponding to - * obj.property and make obj properly converted to Scriptable available - * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. - * The caller must call ScriptRuntime.lastStoredScriptable() immediately + * Prepare for calling obj.property(...): return function corresponding to obj.property and make + * obj properly converted to Scriptable available as ScriptRuntime.lastStoredScriptable() for + * consumption as thisObj. The caller must call ScriptRuntime.lastStoredScriptable() immediately * after calling this method. */ - public static Callable getPropFunctionAndThis(Object obj, - String property, - Context cx, Scriptable scope) - { + public static Callable getPropFunctionAndThis( + Object obj, String property, Context cx, Scriptable scope) { Scriptable thisObj = toObjectOrNull(cx, obj, scope); return getPropFunctionAndThisHelper(obj, property, cx, thisObj); } - private static Callable getPropFunctionAndThisHelper(Object obj, - String property, Context cx, Scriptable thisObj) - { + private static Callable getPropFunctionAndThisHelper( + Object obj, String property, Context cx, Scriptable thisObj) { if (thisObj == null) { throw undefCallError(obj, property); } @@ -2583,7 +2355,7 @@ private static Callable getPropFunctionAndThisHelper(Object obj, if (!(value instanceof Callable)) { Object noSuchMethod = ScriptableObject.getProperty(thisObj, "__noSuchMethod__"); if (noSuchMethod instanceof Callable) - value = new NoSuchMethodShim((Callable)noSuchMethod, property); + value = new NoSuchMethodShim((Callable) noSuchMethod, property); } if (!(value instanceof Callable)) { @@ -2591,26 +2363,24 @@ private static Callable getPropFunctionAndThisHelper(Object obj, } storeScriptable(cx, thisObj); - return (Callable)value; + return (Callable) value; } /** * Prepare for calling <expression>(...): return function corresponding to - * <expression> and make parent scope of the function available - * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. - * The caller must call ScriptRuntime.lastStoredScriptable() immediately - * after calling this method. + * <expression> and make parent scope of the function available as + * ScriptRuntime.lastStoredScriptable() for consumption as thisObj. The caller must call + * ScriptRuntime.lastStoredScriptable() immediately after calling this method. */ - public static Callable getValueFunctionAndThis(Object value, Context cx) - { + public static Callable getValueFunctionAndThis(Object value, Context cx) { if (!(value instanceof Callable)) { throw notFunctionError(value); } - Callable f = (Callable)value; + Callable f = (Callable) value; Scriptable thisObj = null; if (f instanceof Scriptable) { - thisObj = ((Scriptable)f).getParentScope(); + thisObj = ((Scriptable) f).getParentScope(); } if (thisObj == null) { if (cx.topCallScope == null) throw new IllegalStateException(); @@ -2630,12 +2400,11 @@ public static Callable getValueFunctionAndThis(Object value, Context cx) } /** - * Given an object, get the "Symbol.iterator" element, throw a TypeError if it - * is not present, then call the result, (throwing a TypeError if the result is - * not a function), and return that result, whatever it is. + * Given an object, get the "Symbol.iterator" element, throw a TypeError if it is not present, + * then call the result, (throwing a TypeError if the result is not a function), and return that + * result, whatever it is. */ - public static Object callIterator(Object obj, Context cx, Scriptable scope) - { + public static Object callIterator(Object obj, Context cx, Scriptable scope) { final Callable getIterator = ScriptRuntime.getElemFunctionAndThis(obj, SymbolKey.ITERATOR, cx, scope); final Scriptable iterable = ScriptRuntime.lastStoredScriptable(cx); @@ -2643,74 +2412,68 @@ public static Object callIterator(Object obj, Context cx, Scriptable scope) } /** - * Given an iterator result, return true if and only if there is a "done" - * property that's true. + * Given an iterator result, return true if and only if there is a "done" property that's true. */ - public static boolean isIteratorDone(Context cx, Object result) - { + public static boolean isIteratorDone(Context cx, Object result) { if (!(result instanceof Scriptable)) { return false; } - final Object prop = getObjectProp((Scriptable)result, ES6Iterator.DONE_PROPERTY, cx); + final Object prop = getObjectProp((Scriptable) result, ES6Iterator.DONE_PROPERTY, cx); return toBoolean(prop); } /** - * Perform function call in reference context. Should always - * return value that can be passed to - * {@link #refGet(Ref, Context)} or {@link #refSet(Ref, Object, Context)} - * arbitrary number of times. - * The args array reference should not be stored in any object that is - * can be GC-reachable after this method returns. If this is necessary, - * store args.clone(), not args array itself. + * Perform function call in reference context. Should always return value that can be passed to + * {@link #refGet(Ref, Context)} or {@link #refSet(Ref, Object, Context)} arbitrary number of + * times. The args array reference should not be stored in any object that is can be + * GC-reachable after this method returns. If this is necessary, store args.clone(), not args + * array itself. */ - public static Ref callRef(Callable function, Scriptable thisObj, - Object[] args, Context cx) - { + public static Ref callRef(Callable function, Scriptable thisObj, Object[] args, Context cx) { if (function instanceof RefCallable) { - RefCallable rfunction = (RefCallable)function; + RefCallable rfunction = (RefCallable) function; Ref ref = rfunction.refCall(cx, thisObj, args); if (ref == null) { - throw new IllegalStateException(rfunction.getClass().getName()+".refCall() returned null"); + throw new IllegalStateException( + rfunction.getClass().getName() + ".refCall() returned null"); } return ref; } // No runtime support for now - String msg = getMessageById("msg.no.ref.from.function", - toString(function)); + String msg = getMessageById("msg.no.ref.from.function", toString(function)); throw constructError("ReferenceError", msg); } /** * Operator new. * - * See ECMA 11.2.2 + *

See ECMA 11.2.2 */ - public static Scriptable newObject(Object fun, Context cx, - Scriptable scope, Object[] args) - { + public static Scriptable newObject(Object fun, Context cx, Scriptable scope, Object[] args) { if (!(fun instanceof Function)) { throw notFunctionError(fun); } - Function function = (Function)fun; + Function function = (Function) fun; return function.construct(cx, scope, args); } - public static Object callSpecial(Context cx, Callable fun, - Scriptable thisObj, - Object[] args, Scriptable scope, - Scriptable callerThis, int callType, - String filename, int lineNumber) - { + public static Object callSpecial( + Context cx, + Callable fun, + Scriptable thisObj, + Object[] args, + Scriptable scope, + Scriptable callerThis, + int callType, + String filename, + int lineNumber) { if (callType == Node.SPECIALCALL_EVAL) { if (thisObj.getParentScope() == null && NativeGlobal.isEvalFunction(fun)) { - return evalSpecial(cx, scope, callerThis, args, - filename, lineNumber); + return evalSpecial(cx, scope, callerThis, args, filename, lineNumber); } } else if (callType == Node.SPECIALCALL_WITH) { if (NativeWith.isWithFunction(fun)) { - throw Context.reportRuntimeErrorById("msg.only.from.new", - "With"); + throw Context.reportRuntimeErrorById("msg.only.from.new", "With"); } } else { throw Kit.codeBug(); @@ -2719,10 +2482,8 @@ public static Object callSpecial(Context cx, Callable fun, return fun.call(cx, scope, thisObj, args); } - public static Object newSpecial(Context cx, Object fun, - Object[] args, Scriptable scope, - int callType) - { + public static Object newSpecial( + Context cx, Object fun, Object[] args, Scriptable scope, int callType) { if (callType == Node.SPECIALCALL_EVAL) { if (NativeGlobal.isEvalFunction(fun)) { throw typeErrorById("msg.not.ctor", "eval"); @@ -2741,32 +2502,34 @@ public static Object newSpecial(Context cx, Object fun, /** * Function.prototype.apply and Function.prototype.call * - * See Ecma 15.3.4.[34] + *

See Ecma 15.3.4.[34] */ - public static Object applyOrCall(boolean isApply, - Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) - { + public static Object applyOrCall( + boolean isApply, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { int L = args.length; Callable function = getCallable(thisObj); Scriptable callThis = null; if (L != 0) { - if (cx.hasFeature(Context.FEATURE_OLD_UNDEF_NULL_THIS)) { + if (cx.hasFeature(Context.FEATURE_OLD_UNDEF_NULL_THIS)) { callThis = toObjectOrNull(cx, args[0], scope); } else { - callThis = args[0] == Undefined.instance ? Undefined.SCRIPTABLE_UNDEFINED : toObjectOrNull(cx, args[0], scope); + callThis = + args[0] == Undefined.instance + ? Undefined.SCRIPTABLE_UNDEFINED + : toObjectOrNull(cx, args[0], scope); } } if (callThis == null && cx.hasFeature(Context.FEATURE_OLD_UNDEF_NULL_THIS)) { - callThis = getTopCallScope(cx); // This covers the case of args[0] == (null|undefined) as well. + callThis = + getTopCallScope( + cx); // This covers the case of args[0] == (null|undefined) as well. } Object[] callArgs; if (isApply) { // Follow Ecma 15.3.4.3 - callArgs = L <= 1 ? ScriptRuntime.emptyArgs : - getApplyArguments(cx, args[1]); + callArgs = L <= 1 ? ScriptRuntime.emptyArgs : getApplyArguments(cx, args[1]); } else { // Follow Ecma 15.3.4.4 if (L <= 1) { @@ -2780,42 +2543,36 @@ public static Object applyOrCall(boolean isApply, return function.call(cx, scope, callThis, callArgs); } - /** - * @return true if the passed in Scriptable looks like an array - */ - private static boolean isArrayLike(Scriptable obj) - { - return obj != null && ( - obj instanceof NativeArray || - obj instanceof Arguments || - ScriptableObject.hasProperty(obj, "length") - ); - } - - static Object[] getApplyArguments(Context cx, Object arg1) - { + /** @return true if the passed in Scriptable looks like an array */ + private static boolean isArrayLike(Scriptable obj) { + return obj != null + && (obj instanceof NativeArray + || obj instanceof Arguments + || ScriptableObject.hasProperty(obj, "length")); + } + + static Object[] getApplyArguments(Context cx, Object arg1) { if (arg1 == null || arg1 == Undefined.instance) { return ScriptRuntime.emptyArgs; - } else if ( arg1 instanceof Scriptable && isArrayLike((Scriptable) arg1) ) { + } else if (arg1 instanceof Scriptable && isArrayLike((Scriptable) arg1)) { return cx.getElements((Scriptable) arg1); - } else if( arg1 instanceof ScriptableObject ) { + } else if (arg1 instanceof ScriptableObject) { return ScriptRuntime.emptyArgs; } else { throw ScriptRuntime.typeErrorById("msg.arg.isnt.array"); } } - static Callable getCallable(Scriptable thisObj) - { + static Callable getCallable(Scriptable thisObj) { Callable function; if (thisObj instanceof Callable) { - function = (Callable)thisObj; + function = (Callable) thisObj; } else { Object value = thisObj.getDefaultValue(ScriptRuntime.FunctionClass); if (!(value instanceof Callable)) { throw ScriptRuntime.notFunctionError(value, thisObj); } - function = (Callable)value; + function = (Callable) value; } return function; } @@ -2823,19 +2580,20 @@ static Callable getCallable(Scriptable thisObj) /** * The eval function property of the global object. * - * See ECMA 15.1.2.1 + *

See ECMA 15.1.2.1 */ - public static Object evalSpecial(Context cx, Scriptable scope, - Object thisArg, Object[] args, - String filename, int lineNumber) - { - if (args.length < 1) - return Undefined.instance; + public static Object evalSpecial( + Context cx, + Scriptable scope, + Object thisArg, + Object[] args, + String filename, + int lineNumber) { + if (args.length < 1) return Undefined.instance; Object x = args[0]; if (!(x instanceof CharSequence)) { - if (cx.hasFeature(Context.FEATURE_STRICT_MODE) || - cx.hasFeature(Context.FEATURE_STRICT_EVAL)) - { + if (cx.hasFeature(Context.FEATURE_STRICT_MODE) + || cx.hasFeature(Context.FEATURE_STRICT_EVAL)) { throw Context.reportRuntimeErrorById("msg.eval.nonstring.strict"); } String message = ScriptRuntime.getMessageById("msg.eval.nonstring"); @@ -2851,68 +2609,49 @@ public static Object evalSpecial(Context cx, Scriptable scope, filename = ""; } } - String sourceName = ScriptRuntime. - makeUrlForGeneratedScript(true, filename, lineNumber); + String sourceName = ScriptRuntime.makeUrlForGeneratedScript(true, filename, lineNumber); ErrorReporter reporter; reporter = DefaultErrorReporter.forEval(cx.getErrorReporter()); Evaluator evaluator = Context.createInterpreter(); if (evaluator == null) { - throw new JavaScriptException("Interpreter not present", - filename, lineNumber); + throw new JavaScriptException("Interpreter not present", filename, lineNumber); } // Compile with explicit interpreter instance to force interpreter // mode. - Script script = cx.compileString(x.toString(), evaluator, - reporter, sourceName, 1, null); + Script script = cx.compileString(x.toString(), evaluator, reporter, sourceName, 1, null); evaluator.setEvalScriptFlag(script); - Callable c = (Callable)script; - return c.call(cx, scope, (Scriptable)thisArg, ScriptRuntime.emptyArgs); - } - - /** - * The typeof operator - */ - public static String typeof(Object value) - { - if (value == null) - return "object"; - if (value == Undefined.instance) - return "undefined"; - if (value instanceof ScriptableObject) - return ((ScriptableObject) value).getTypeOf(); - if (value instanceof Scriptable) - return (value instanceof Callable) ? "function" : "object"; - if (value instanceof CharSequence) - return "string"; - if (value instanceof Number) - return "number"; - if (value instanceof Boolean) - return "boolean"; + Callable c = (Callable) script; + return c.call(cx, scope, (Scriptable) thisArg, ScriptRuntime.emptyArgs); + } + + /** The typeof operator */ + public static String typeof(Object value) { + if (value == null) return "object"; + if (value == Undefined.instance) return "undefined"; + if (value instanceof ScriptableObject) return ((ScriptableObject) value).getTypeOf(); + if (value instanceof Scriptable) return (value instanceof Callable) ? "function" : "object"; + if (value instanceof CharSequence) return "string"; + if (value instanceof Number) return "number"; + if (value instanceof Boolean) return "boolean"; throw errorWithClassName("msg.invalid.type", value); } - /** - * The typeof operator that correctly handles the undefined case - */ - public static String typeofName(Scriptable scope, String id) - { + /** The typeof operator that correctly handles the undefined case */ + public static String typeofName(Scriptable scope, String id) { Context cx = Context.getContext(); Scriptable val = bind(cx, scope, id); - if (val == null) - return "undefined"; + if (val == null) return "undefined"; return typeof(getObjectProp(val, id, cx)); } - /** - * returns the raw type. Taken from google guice. - */ + /** returns the raw type. Taken from google guice. */ public static Class getRawType(Type type) { if (type == null) { return null; - + } else if (type instanceof Class) { // Type is a normal class. return (Class) type; @@ -2930,27 +2669,27 @@ public static Class getRawType(Type type) { return (Class) rawType; } else if (type instanceof GenericArrayType) { - Type componentType = ((GenericArrayType) type) - .getGenericComponentType(); + Type componentType = ((GenericArrayType) type).getGenericComponentType(); return Array.newInstance(getRawType(componentType), 0).getClass(); - } else if (type instanceof TypeVariable - || type instanceof WildcardType) { + } else if (type instanceof TypeVariable || type instanceof WildcardType) { // We could use the variable's bounds, but that won't work if there - // are multiple. Having a raw type that's more general than + // are multiple. Having a raw type that's more general than // necessary is okay. return Object.class; } else { String className = type.getClass().getName(); - throw new IllegalArgumentException("Expected a Class, " - + "ParameterizedType, or GenericArrayType, but <" - + type + "> is of type " + className); + throw new IllegalArgumentException( + "Expected a Class, " + + "ParameterizedType, or GenericArrayType, but <" + + type + + "> is of type " + + className); } } - public static boolean isObject(Object value) - { + public static boolean isObject(Object value) { if (value == null) { return false; } @@ -2958,7 +2697,7 @@ public static boolean isObject(Object value) return false; } if (value instanceof ScriptableObject) { - String type = ((ScriptableObject)value).getTypeOf(); + String type = ((ScriptableObject) value).getTypeOf(); return "object".equals(type) || "function".equals(type); } if (value instanceof Scriptable) { @@ -2979,20 +2718,18 @@ public static boolean isObject(Object value) // implement the '~' operator inline in the caller // as "~toInt32(val)" - public static Object add(Object val1, Object val2, Context cx) - { - if(val1 instanceof Number && val2 instanceof Number) { - return wrapNumber(((Number)val1).doubleValue() + - ((Number)val2).doubleValue()); + public static Object add(Object val1, Object val2, Context cx) { + if (val1 instanceof Number && val2 instanceof Number) { + return wrapNumber(((Number) val1).doubleValue() + ((Number) val2).doubleValue()); } if (val1 instanceof XMLObject) { - Object test = ((XMLObject)val1).addValues(cx, true, val2); + Object test = ((XMLObject) val1).addValues(cx, true, val2); if (test != Scriptable.NOT_FOUND) { return test; } } if (val2 instanceof XMLObject) { - Object test = ((XMLObject)val2).addValues(cx, false, val1); + Object test = ((XMLObject) val2).addValues(cx, false, val1); if (test != Scriptable.NOT_FOUND) { return test; } @@ -3000,13 +2737,11 @@ public static Object add(Object val1, Object val2, Context cx) if ((val1 instanceof Symbol) || (val2 instanceof Symbol)) { throw typeErrorById("msg.not.a.number"); } - if (val1 instanceof Scriptable) - val1 = ((Scriptable) val1).getDefaultValue(null); - if (val2 instanceof Scriptable) - val2 = ((Scriptable) val2).getDefaultValue(null); + if (val1 instanceof Scriptable) val1 = ((Scriptable) val1).getDefaultValue(null); + if (val2 instanceof Scriptable) val2 = ((Scriptable) val2).getDefaultValue(null); if (!(val1 instanceof CharSequence) && !(val2 instanceof CharSequence)) { if ((val1 instanceof Number) && (val2 instanceof Number)) { - return wrapNumber(((Number)val1).doubleValue() + ((Number)val2).doubleValue()); + return wrapNumber(((Number) val1).doubleValue() + ((Number) val2).doubleValue()); } return wrapNumber(toNumber(val1) + toNumber(val2)); } @@ -3027,26 +2762,24 @@ public static CharSequence add(Object val1, CharSequence val2) { * @deprecated Use {@link #nameIncrDecr(Scriptable, String, Context, int)} instead */ @Deprecated - public static Object nameIncrDecr(Scriptable scopeChain, String id, - int incrDecrMask) - { + public static Object nameIncrDecr(Scriptable scopeChain, String id, int incrDecrMask) { return nameIncrDecr(scopeChain, id, Context.getContext(), incrDecrMask); } - public static Object nameIncrDecr(Scriptable scopeChain, String id, - Context cx, int incrDecrMask) - { + public static Object nameIncrDecr( + Scriptable scopeChain, String id, Context cx, int incrDecrMask) { Scriptable target; Object value; - search: { + search: + { do { if (cx.useDynamicScope && scopeChain.getParentScope() == null) { scopeChain = checkDynamicScope(cx.topCallScope, scopeChain); } target = scopeChain; do { - if (target instanceof NativeWith && - target.getPrototype() instanceof XMLObject) { + if (target instanceof NativeWith + && target.getPrototype() instanceof XMLObject) { break; } value = target.get(id, scopeChain); @@ -3059,24 +2792,17 @@ public static Object nameIncrDecr(Scriptable scopeChain, String id, } while (scopeChain != null); throw notFoundError(null, id); } - return doScriptableIncrDecr(target, id, scopeChain, value, - incrDecrMask); + return doScriptableIncrDecr(target, id, scopeChain, value, incrDecrMask); } - /** - * @deprecated Use {@link #propIncrDecr(Object, String, Context, Scriptable, int)} instead - */ + /** @deprecated Use {@link #propIncrDecr(Object, String, Context, Scriptable, int)} instead */ @Deprecated - public static Object propIncrDecr(Object obj, String id, - Context cx, int incrDecrMask) - { + public static Object propIncrDecr(Object obj, String id, Context cx, int incrDecrMask) { return propIncrDecr(obj, id, cx, getTopCallScope(cx), incrDecrMask); } - public static Object propIncrDecr(Object obj, String id, - Context cx, Scriptable scope, - int incrDecrMask) - { + public static Object propIncrDecr( + Object obj, String id, Context cx, Scriptable scope, int incrDecrMask) { Scriptable start = toObjectOrNull(cx, obj, scope); if (start == null) { throw undefReadError(obj, id); @@ -3084,7 +2810,8 @@ public static Object propIncrDecr(Object obj, String id, Scriptable target = start; Object value; - search: { + search: + { do { value = target.get(id, start); if (value != Scriptable.NOT_FOUND) { @@ -3095,20 +2822,19 @@ public static Object propIncrDecr(Object obj, String id, start.put(id, start, NaNobj); return NaNobj; } - return doScriptableIncrDecr(target, id, start, value, - incrDecrMask); + return doScriptableIncrDecr(target, id, start, value, incrDecrMask); } - private static Object doScriptableIncrDecr(Scriptable target, - String id, - Scriptable protoChainStart, - Object value, - int incrDecrMask) - { + private static Object doScriptableIncrDecr( + Scriptable target, + String id, + Scriptable protoChainStart, + Object value, + int incrDecrMask) { final boolean post = (incrDecrMask & Node.POST_FLAG) != 0; double number; if (value instanceof Number) { - number = ((Number)value).doubleValue(); + number = ((Number) value).doubleValue(); } else { number = toNumber(value); if (post) { @@ -3129,25 +2855,19 @@ private static Object doScriptableIncrDecr(Scriptable target, return result; } - /** - * @deprecated Use {@link #elemIncrDecr(Object, Object, Context, Scriptable, int)} instead - */ + /** @deprecated Use {@link #elemIncrDecr(Object, Object, Context, Scriptable, int)} instead */ @Deprecated - public static Object elemIncrDecr(Object obj, Object index, - Context cx, int incrDecrMask) - { + public static Object elemIncrDecr(Object obj, Object index, Context cx, int incrDecrMask) { return elemIncrDecr(obj, index, cx, getTopCallScope(cx), incrDecrMask); } - public static Object elemIncrDecr(Object obj, Object index, - Context cx, Scriptable scope, - int incrDecrMask) - { + public static Object elemIncrDecr( + Object obj, Object index, Context cx, Scriptable scope, int incrDecrMask) { Object value = getObjectElem(obj, index, cx, scope); final boolean post = (incrDecrMask & Node.POST_FLAG) != 0; double number; if (value instanceof Number) { - number = ((Number)value).doubleValue(); + number = ((Number) value).doubleValue(); } else { number = toNumber(value); if (post) { @@ -3168,23 +2888,18 @@ public static Object elemIncrDecr(Object obj, Object index, return result; } - /** - * @deprecated Use {@link #refIncrDecr(Ref, Context, Scriptable, int)} instead - */ + /** @deprecated Use {@link #refIncrDecr(Ref, Context, Scriptable, int)} instead */ @Deprecated - public static Object refIncrDecr(Ref ref, Context cx, int incrDecrMask) - { + public static Object refIncrDecr(Ref ref, Context cx, int incrDecrMask) { return refIncrDecr(ref, cx, getTopCallScope(cx), incrDecrMask); } - public static Object refIncrDecr(Ref ref, Context cx, Scriptable scope, - int incrDecrMask) - { + public static Object refIncrDecr(Ref ref, Context cx, Scriptable scope, int incrDecrMask) { Object value = ref.get(cx); boolean post = ((incrDecrMask & Node.POST_FLAG) != 0); double number; if (value instanceof Number) { - number = ((Number)value).doubleValue(); + number = ((Number) value).doubleValue(); } else { number = toNumber(value); if (post) { @@ -3209,12 +2924,11 @@ public static Object toPrimitive(Object val) { return toPrimitive(val, null); } - public static Object toPrimitive(Object val, Class typeHint) - { + public static Object toPrimitive(Object val, Class typeHint) { if (!(val instanceof Scriptable)) { return val; } - Scriptable s = (Scriptable)val; + Scriptable s = (Scriptable) val; Object result = s.getDefaultValue(typeHint); if ((result instanceof Scriptable) && !isSymbol(result)) throw typeErrorById("msg.bad.default.value"); @@ -3224,77 +2938,76 @@ public static Object toPrimitive(Object val, Class typeHint) /** * Equality * - * See ECMA 11.9 + *

See ECMA 11.9 */ - public static boolean eq(Object x, Object y) - { + public static boolean eq(Object x, Object y) { if (x == null || x == Undefined.instance) { if (y == null || y == Undefined.instance) { return true; } if (y instanceof ScriptableObject) { - Object test = ((ScriptableObject)y).equivalentValues(x); + Object test = ((ScriptableObject) y).equivalentValues(x); if (test != Scriptable.NOT_FOUND) { - return ((Boolean)test).booleanValue(); + return ((Boolean) test).booleanValue(); } } return false; } else if (x instanceof Number) { - return eqNumber(((Number)x).doubleValue(), y); + return eqNumber(((Number) x).doubleValue(), y); } else if (x == y) { return true; } else if (x instanceof CharSequence) { - return eqString((CharSequence)x, y); + return eqString((CharSequence) x, y); } else if (x instanceof Boolean) { - boolean b = ((Boolean)x).booleanValue(); + boolean b = ((Boolean) x).booleanValue(); if (y instanceof Boolean) { - return b == ((Boolean)y).booleanValue(); + return b == ((Boolean) y).booleanValue(); } if (y instanceof ScriptableObject) { - Object test = ((ScriptableObject)y).equivalentValues(x); + Object test = ((ScriptableObject) y).equivalentValues(x); if (test != Scriptable.NOT_FOUND) { - return ((Boolean)test).booleanValue(); + return ((Boolean) test).booleanValue(); } } return eqNumber(b ? 1.0 : 0.0, y); } else if (x instanceof Scriptable) { if (y instanceof Scriptable) { if (x instanceof ScriptableObject) { - Object test = ((ScriptableObject)x).equivalentValues(y); + Object test = ((ScriptableObject) x).equivalentValues(y); if (test != Scriptable.NOT_FOUND) { - return ((Boolean)test).booleanValue(); + return ((Boolean) test).booleanValue(); } } if (y instanceof ScriptableObject) { - Object test = ((ScriptableObject)y).equivalentValues(x); + Object test = ((ScriptableObject) y).equivalentValues(x); if (test != Scriptable.NOT_FOUND) { - return ((Boolean)test).booleanValue(); + return ((Boolean) test).booleanValue(); } } if (x instanceof Wrapper && y instanceof Wrapper) { // See bug 413838. Effectively an extension to ECMA for // the LiveConnect case. - Object unwrappedX = ((Wrapper)x).unwrap(); - Object unwrappedY = ((Wrapper)y).unwrap(); - return unwrappedX == unwrappedY || - (isPrimitive(unwrappedX) && - isPrimitive(unwrappedY) && - eq(unwrappedX, unwrappedY)); + Object unwrappedX = ((Wrapper) x).unwrap(); + Object unwrappedY = ((Wrapper) y).unwrap(); + return unwrappedX == unwrappedY + || (isPrimitive(unwrappedX) + && isPrimitive(unwrappedY) + && eq(unwrappedX, unwrappedY)); } return false; } else if (y instanceof Boolean) { if (x instanceof ScriptableObject) { - Object test = ((ScriptableObject)x).equivalentValues(y); + Object test = ((ScriptableObject) x).equivalentValues(y); if (test != Scriptable.NOT_FOUND) { - return ((Boolean)test).booleanValue(); + return ((Boolean) test).booleanValue(); } } - double d = ((Boolean)y).booleanValue() ? 1.0 : 0.0; + double d = ((Boolean) y).booleanValue() ? 1.0 : 0.0; return eqNumber(d, x); } else if (y instanceof Number) { - return eqNumber(((Number)y).doubleValue(), x); + return eqNumber(((Number) y).doubleValue(), x); } else if (y instanceof CharSequence) { - return eqString((CharSequence)y, x); + return eqString((CharSequence) y, x); } // covers the case when y == Undefined.instance as well return false; @@ -3321,9 +3034,7 @@ public static boolean same(Object x, Object y) { return eq(x, y); } - /** - * Implement "SameValueZero" from ECMA 7.2.9 - */ + /** Implement "SameValueZero" from ECMA 7.2.9 */ public static boolean sameZero(Object x, Object y) { if (!typeof(x).equals(typeof(y))) { return false; @@ -3332,12 +3043,11 @@ public static boolean sameZero(Object x, Object y) { if (isNaN(x) && isNaN(y)) { return true; } - final double dx = ((Number)x).doubleValue(); + final double dx = ((Number) x).doubleValue(); if (y instanceof Number) { - final double dy = ((Number)y).doubleValue(); - if (((dx == negativeZero) && (dy == 0.0)) || - ((dx == 0.0) && dy == negativeZero)) { - return true; + final double dy = ((Number) y).doubleValue(); + if (((dx == negativeZero) && (dy == 0.0)) || ((dx == 0.0) && dy == negativeZero)) { + return true; } } return eqNumber(dx, y); @@ -3347,27 +3057,28 @@ public static boolean sameZero(Object x, Object y) { public static boolean isNaN(Object n) { if (n instanceof Double) { - return ((Double)n).isNaN(); + return ((Double) n).isNaN(); } if (n instanceof Float) { - return ((Float)n).isNaN(); + return ((Float) n).isNaN(); } return false; } public static boolean isPrimitive(Object obj) { - return obj == null || obj == Undefined.instance || - (obj instanceof Number) || (obj instanceof String) || - (obj instanceof Boolean); + return obj == null + || obj == Undefined.instance + || (obj instanceof Number) + || (obj instanceof String) + || (obj instanceof Boolean); } - static boolean eqNumber(double x, Object y) - { - for (;;) { + static boolean eqNumber(double x, Object y) { + for (; ; ) { if (y == null || y == Undefined.instance) { return false; } else if (y instanceof Number) { - return x == ((Number)y).doubleValue(); + return x == ((Number) y).doubleValue(); } else if (y instanceof CharSequence) { return x == toNumber(y); } else if (y instanceof Boolean) { @@ -3377,9 +3088,9 @@ static boolean eqNumber(double x, Object y) } else if (y instanceof Scriptable) { if (y instanceof ScriptableObject) { Object xval = wrapNumber(x); - Object test = ((ScriptableObject)y).equivalentValues(xval); + Object test = ((ScriptableObject) y).equivalentValues(xval); if (test != Scriptable.NOT_FOUND) { - return ((Boolean)test).booleanValue(); + return ((Boolean) test).booleanValue(); } } y = toPrimitive(y); @@ -3390,25 +3101,24 @@ static boolean eqNumber(double x, Object y) } } - private static boolean eqString(CharSequence x, Object y) - { - for (;;) { + private static boolean eqString(CharSequence x, Object y) { + for (; ; ) { if (y == null || y == Undefined.instance) { return false; } else if (y instanceof CharSequence) { - CharSequence c = (CharSequence)y; + CharSequence c = (CharSequence) y; return x.length() == c.length() && x.toString().equals(c.toString()); } else if (y instanceof Number) { - return toNumber(x.toString()) == ((Number)y).doubleValue(); + return toNumber(x.toString()) == ((Number) y).doubleValue(); } else if (y instanceof Boolean) { return toNumber(x.toString()) == (((Boolean) y).booleanValue() ? 1.0 : 0.0); } else if (isSymbol(y)) { return false; } else if (y instanceof Scriptable) { if (y instanceof ScriptableObject) { - Object test = ((ScriptableObject)y).equivalentValues(x.toString()); + Object test = ((ScriptableObject) y).equivalentValues(x.toString()); if (test != Scriptable.NOT_FOUND) { - return ((Boolean)test).booleanValue(); + return ((Boolean) test).booleanValue(); } } y = toPrimitive(y); @@ -3419,23 +3129,24 @@ private static boolean eqString(CharSequence x, Object y) } } } - public static boolean shallowEq(Object x, Object y) - { + + public static boolean shallowEq(Object x, Object y) { if (x == y) { if (!(x instanceof Number)) { return true; } // NaN check - double d = ((Number)x).doubleValue(); + double d = ((Number) x).doubleValue(); return !Double.isNaN(d); } if (x == null || x == Undefined.instance || x == Undefined.SCRIPTABLE_UNDEFINED) { if ((x == Undefined.instance && y == Undefined.SCRIPTABLE_UNDEFINED) - || (x == Undefined.SCRIPTABLE_UNDEFINED && y == Undefined.instance)) return true; + || (x == Undefined.SCRIPTABLE_UNDEFINED && y == Undefined.instance)) + return true; return false; } else if (x instanceof Number) { if (y instanceof Number) { - return ((Number)x).doubleValue() == ((Number)y).doubleValue(); + return ((Number) x).doubleValue() == ((Number) y).doubleValue(); } } else if (x instanceof CharSequence) { if (y instanceof CharSequence) { @@ -3447,7 +3158,7 @@ public static boolean shallowEq(Object x, Object y) } } else if (x instanceof Scriptable) { if (x instanceof Wrapper && y instanceof Wrapper) { - return ((Wrapper)x).unwrap() == ((Wrapper)y).unwrap(); + return ((Wrapper) x).unwrap() == ((Wrapper) y).unwrap(); } } else { warnAboutNonJSObject(x); @@ -3461,18 +3172,16 @@ public static boolean shallowEq(Object x, Object y) * * @return a instanceof b */ - public static boolean instanceOf(Object a, Object b, Context cx) - { + public static boolean instanceOf(Object a, Object b, Context cx) { // Check RHS is an object - if (! (b instanceof Scriptable)) { + if (!(b instanceof Scriptable)) { throw typeErrorById("msg.instanceof.not.object"); } // for primitive values on LHS, return false - if (! (a instanceof Scriptable)) - return false; + if (!(a instanceof Scriptable)) return false; - return ((Scriptable)b).hasInstance((Scriptable)a); + return ((Scriptable) b).hasInstance((Scriptable) a); } /** @@ -3494,40 +3203,35 @@ public static boolean jsDelegatesTo(Scriptable lhs, Scriptable rhs) { /** * The in operator. * - * This is a new JS 1.3 language feature. The in operator mirrors - * the operation of the for .. in construct, and tests whether the - * rhs has the property given by the lhs. It is different from the - * for .. in construct in that: - *
- it doesn't perform ToObject on the right hand side - *
- it returns true for DontEnum properties. + *

This is a new JS 1.3 language feature. The in operator mirrors the operation of the for .. + * in construct, and tests whether the rhs has the property given by the lhs. It is different + * from the for .. in construct in that:
+ * - it doesn't perform ToObject on the right hand side
+ * - it returns true for DontEnum properties. + * * @param a the left hand operand * @param b the right hand operand - * * @return true if property name or element number a is a property of b */ - public static boolean in(Object a, Object b, Context cx) - { + public static boolean in(Object a, Object b, Context cx) { if (!(b instanceof Scriptable)) { throw typeErrorById("msg.in.not.object"); } - return hasObjectElem((Scriptable)b, a, cx); + return hasObjectElem((Scriptable) b, a, cx); } - public static boolean cmp_LT(Object val1, Object val2) - { + public static boolean cmp_LT(Object val1, Object val2) { double d1, d2; if (val1 instanceof Number && val2 instanceof Number) { - d1 = ((Number)val1).doubleValue(); - d2 = ((Number)val2).doubleValue(); + d1 = ((Number) val1).doubleValue(); + d2 = ((Number) val2).doubleValue(); } else { if ((val1 instanceof Symbol) || (val2 instanceof Symbol)) { throw typeErrorById("msg.compare.symbol"); } - if (val1 instanceof Scriptable) - val1 = ((Scriptable) val1).getDefaultValue(NumberClass); - if (val2 instanceof Scriptable) - val2 = ((Scriptable) val2).getDefaultValue(NumberClass); + if (val1 instanceof Scriptable) val1 = ((Scriptable) val1).getDefaultValue(NumberClass); + if (val2 instanceof Scriptable) val2 = ((Scriptable) val2).getDefaultValue(NumberClass); if (val1 instanceof CharSequence && val2 instanceof CharSequence) { return val1.toString().compareTo(val2.toString()) < 0; } @@ -3537,20 +3241,17 @@ public static boolean cmp_LT(Object val1, Object val2) return d1 < d2; } - public static boolean cmp_LE(Object val1, Object val2) - { + public static boolean cmp_LE(Object val1, Object val2) { double d1, d2; if (val1 instanceof Number && val2 instanceof Number) { - d1 = ((Number)val1).doubleValue(); - d2 = ((Number)val2).doubleValue(); + d1 = ((Number) val1).doubleValue(); + d2 = ((Number) val2).doubleValue(); } else { if ((val1 instanceof Symbol) || (val2 instanceof Symbol)) { throw typeErrorById("msg.compare.symbol"); } - if (val1 instanceof Scriptable) - val1 = ((Scriptable) val1).getDefaultValue(NumberClass); - if (val2 instanceof Scriptable) - val2 = ((Scriptable) val2).getDefaultValue(NumberClass); + if (val1 instanceof Scriptable) val1 = ((Scriptable) val1).getDefaultValue(NumberClass); + if (val2 instanceof Scriptable) val2 = ((Scriptable) val2).getDefaultValue(NumberClass); if (val1 instanceof CharSequence && val2 instanceof CharSequence) { return val1.toString().compareTo(val2.toString()) <= 0; } @@ -3569,28 +3270,24 @@ public static ScriptableObject getGlobal(Context cx) { Class globalClass = Kit.classOrNull(GLOBAL_CLASS); if (globalClass != null) { try { - Class[] parm = { ScriptRuntime.ContextClass }; + Class[] parm = {ScriptRuntime.ContextClass}; Constructor globalClassCtor = globalClass.getConstructor(parm); - Object[] arg = { cx }; + Object[] arg = {cx}; return (ScriptableObject) globalClassCtor.newInstance(arg); - } - catch (RuntimeException e) { + } catch (RuntimeException e) { throw e; - } - catch (Exception e) { + } catch (Exception e) { // fall through... } } return new ImporterTopLevel(cx); } - public static boolean hasTopCall(Context cx) - { + public static boolean hasTopCall(Context cx) { return (cx.topCallScope != null); } - public static Scriptable getTopCallScope(Context cx) - { + public static Scriptable getTopCallScope(Context cx) { Scriptable scope = cx.topCallScope; if (scope == null) { throw new IllegalStateException(); @@ -3599,22 +3296,23 @@ public static Scriptable getTopCallScope(Context cx) } /** - * @deprecated Use {@link #doTopCall(Callable, Context, Scriptable, Scriptable, Object[], boolean)} instead + * @deprecated Use {@link #doTopCall(Callable, Context, Scriptable, Scriptable, Object[], + * boolean)} instead */ @Deprecated - public static Object doTopCall(Callable callable, - Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) - { + public static Object doTopCall( + Callable callable, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { return doTopCall(callable, cx, scope, thisObj, args, cx.isTopLevelStrict); } - public static Object doTopCall(Callable callable, - Context cx, Scriptable scope, - Scriptable thisObj, Object[] args, boolean isTopLevelStrict) - { - if (scope == null) - throw new IllegalArgumentException(); + public static Object doTopCall( + Callable callable, + Context cx, + Scriptable scope, + Scriptable thisObj, + Object[] args, + boolean isTopLevelStrict) { + if (scope == null) throw new IllegalArgumentException(); if (cx.topCallScope != null) throw new IllegalStateException(); Object result; @@ -3641,20 +3339,18 @@ public static Object doTopCall(Callable callable, } /** - * Return possibleDynamicScope if staticTopScope - * is present on its prototype chain and return staticTopScope - * otherwise. - * Should only be called when staticTopScope is top scope. + * Return possibleDynamicScope if staticTopScope is present on its + * prototype chain and return staticTopScope otherwise. Should only be called when + * staticTopScope is top scope. */ - static Scriptable checkDynamicScope(Scriptable possibleDynamicScope, - Scriptable staticTopScope) - { + static Scriptable checkDynamicScope( + Scriptable possibleDynamicScope, Scriptable staticTopScope) { // Return cx.topCallScope if scope if (possibleDynamicScope == staticTopScope) { return possibleDynamicScope; } Scriptable proto = possibleDynamicScope; - for (;;) { + for (; ; ) { proto = proto.getPrototype(); if (proto == staticTopScope) { return possibleDynamicScope; @@ -3665,22 +3361,21 @@ static Scriptable checkDynamicScope(Scriptable possibleDynamicScope, } } - public static void addInstructionCount(Context cx, int instructionsToAdd) - { + public static void addInstructionCount(Context cx, int instructionsToAdd) { cx.instructionCount += instructionsToAdd; - if (cx.instructionCount > cx.instructionThreshold) - { + if (cx.instructionCount > cx.instructionThreshold) { cx.observeInstructionCount(cx.instructionCount); cx.instructionCount = 0; } } - public static void initScript(NativeFunction funObj, Scriptable thisObj, - Context cx, Scriptable scope, - boolean evalScript) - { - if (cx.topCallScope == null) - throw new IllegalStateException(); + public static void initScript( + NativeFunction funObj, + Scriptable thisObj, + Context cx, + Scriptable scope, + boolean evalScript) { + if (cx.topCallScope == null) throw new IllegalStateException(); int varCount = funObj.getParamAndVarCount(); if (varCount != 0) { @@ -3692,7 +3387,7 @@ public static void initScript(NativeFunction funObj, Scriptable thisObj, varScope = varScope.getParentScope(); } - for (int i = varCount; i-- != 0;) { + for (int i = varCount; i-- != 0; ) { String name = funObj.getParamOrVarName(i); boolean isConst = funObj.getParamOrVarConst(i); // Don't overwrite existing def if already defined in object @@ -3702,9 +3397,10 @@ public static void initScript(NativeFunction funObj, Scriptable thisObj, ScriptableObject.defineConstProperty(varScope, name); } else if (!evalScript) { if (!(funObj instanceof InterpretedFunction) - || ((InterpretedFunction) funObj).hasFunctionNamed(name)) { + || ((InterpretedFunction) funObj).hasFunctionNamed(name)) { // Global var definitions are supposed to be DONTDELETE - ScriptableObject.defineProperty(varScope, name, Undefined.instance, ScriptableObject.PERMANENT); + ScriptableObject.defineProperty( + varScope, name, Undefined.instance, ScriptableObject.PERMANENT); } } else { varScope.put(name, varScope, Undefined.instance); @@ -3717,73 +3413,61 @@ public static void initScript(NativeFunction funObj, Scriptable thisObj, } /** - * @deprecated Use {@link #createFunctionActivation(NativeFunction, Scriptable, Object[], boolean)} instead + * @deprecated Use {@link #createFunctionActivation(NativeFunction, Scriptable, Object[], + * boolean)} instead */ @Deprecated - public static Scriptable createFunctionActivation(NativeFunction funObj, - Scriptable scope, - Object[] args) - { + public static Scriptable createFunctionActivation( + NativeFunction funObj, Scriptable scope, Object[] args) { return createFunctionActivation(funObj, scope, args, false); } - public static Scriptable createFunctionActivation(NativeFunction funObj, - Scriptable scope, - Object[] args, - boolean isStrict) - { + public static Scriptable createFunctionActivation( + NativeFunction funObj, Scriptable scope, Object[] args, boolean isStrict) { return new NativeCall(funObj, scope, args, false, isStrict); } - public static Scriptable createArrowFunctionActivation(NativeFunction funObj, - Scriptable scope, - Object[] args, - boolean isStrict) - { + public static Scriptable createArrowFunctionActivation( + NativeFunction funObj, Scriptable scope, Object[] args, boolean isStrict) { return new NativeCall(funObj, scope, args, true, isStrict); } - public static void enterActivationFunction(Context cx, - Scriptable scope) - { - if (cx.topCallScope == null) - throw new IllegalStateException(); - NativeCall call = (NativeCall)scope; + public static void enterActivationFunction(Context cx, Scriptable scope) { + if (cx.topCallScope == null) throw new IllegalStateException(); + NativeCall call = (NativeCall) scope; call.parentActivationCall = cx.currentActivationCall; cx.currentActivationCall = call; call.defineAttributesForArguments(); } - public static void exitActivationFunction(Context cx) - { + public static void exitActivationFunction(Context cx) { NativeCall call = cx.currentActivationCall; cx.currentActivationCall = call.parentActivationCall; call.parentActivationCall = null; } - static NativeCall findFunctionActivation(Context cx, Function f) - { + static NativeCall findFunctionActivation(Context cx, Function f) { NativeCall call = cx.currentActivationCall; while (call != null) { - if (call.function == f) - return call; + if (call.function == f) return call; call = call.parentActivationCall; } return null; } - public static Scriptable newCatchScope(Throwable t, - Scriptable lastCatchScope, - String exceptionName, - Context cx, Scriptable scope) - { + public static Scriptable newCatchScope( + Throwable t, + Scriptable lastCatchScope, + String exceptionName, + Context cx, + Scriptable scope) { Object obj; boolean cacheObj; - getObj: + getObj: if (t instanceof JavaScriptException) { cacheObj = false; - obj = ((JavaScriptException)t).getValue(); + obj = ((JavaScriptException) t).getValue(); } else { cacheObj = true; @@ -3791,7 +3475,7 @@ public static Scriptable newCatchScope(Throwable t, // the previous scope object if (lastCatchScope != null) { - NativeObject last = (NativeObject)lastCatchScope; + NativeObject last = (NativeObject) lastCatchScope; obj = last.getAssociatedValue(t); if (obj == null) Kit.codeBug(); break getObj; @@ -3803,20 +3487,19 @@ public static Scriptable newCatchScope(Throwable t, Throwable javaException = null; if (t instanceof EcmaError) { - EcmaError ee = (EcmaError)t; + EcmaError ee = (EcmaError) t; re = ee; type = TopLevel.NativeErrors.valueOf(ee.getName()); errorMsg = ee.getErrorMessage(); } else if (t instanceof WrappedException) { - WrappedException we = (WrappedException)t; + WrappedException we = (WrappedException) t; re = we; javaException = we.getWrappedException(); type = TopLevel.NativeErrors.JavaException; - errorMsg = javaException.getClass().getName() - +": "+javaException.getMessage(); + errorMsg = javaException.getClass().getName() + ": " + javaException.getMessage(); } else if (t instanceof EvaluatorException) { // Pure evaluator exception, nor WrappedException instance - EvaluatorException ee = (EvaluatorException)t; + EvaluatorException ee = (EvaluatorException) t; re = ee; type = TopLevel.NativeErrors.InternalError; errorMsg = ee.getMessage(); @@ -3839,9 +3522,9 @@ public static Scriptable newCatchScope(Throwable t, int line = re.lineNumber(); Object args[]; if (line > 0) { - args = new Object[] { errorMsg, sourceUri, Integer.valueOf(line) }; + args = new Object[] {errorMsg, sourceUri, Integer.valueOf(line)}; } else { - args = new Object[] { errorMsg, sourceUri }; + args = new Object[] {errorMsg, sourceUri}; } Scriptable errorObject = newNativeError(cx, scope, type, args); @@ -3851,33 +3534,40 @@ public static Scriptable newCatchScope(Throwable t, } if (javaException != null && isVisible(cx, javaException)) { - Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException, - null); + Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException, null); ScriptableObject.defineProperty( - errorObject, "javaException", wrap, - ScriptableObject.PERMANENT | ScriptableObject.READONLY | ScriptableObject.DONTENUM); + errorObject, + "javaException", + wrap, + ScriptableObject.PERMANENT + | ScriptableObject.READONLY + | ScriptableObject.DONTENUM); } if (isVisible(cx, re)) { Object wrap = cx.getWrapFactory().wrap(cx, scope, re, null); ScriptableObject.defineProperty( - errorObject, "rhinoException", wrap, - ScriptableObject.PERMANENT | ScriptableObject.READONLY | ScriptableObject.DONTENUM); + errorObject, + "rhinoException", + wrap, + ScriptableObject.PERMANENT + | ScriptableObject.READONLY + | ScriptableObject.DONTENUM); } obj = errorObject; } NativeObject catchScopeObject = new NativeObject(); // See ECMA 12.4 - catchScopeObject.defineProperty( - exceptionName, obj, ScriptableObject.PERMANENT); + catchScopeObject.defineProperty(exceptionName, obj, ScriptableObject.PERMANENT); if (isVisible(cx, t)) { // Add special Rhino object __exception__ defined in the catch // scope that can be used to retrieve the Java exception associated // with the JavaScript exception (to get stack trace info, etc.) catchScopeObject.defineProperty( - "__exception__", Context.javaToJS(t, scope), - ScriptableObject.PERMANENT|ScriptableObject.DONTENUM); + "__exception__", + Context.javaToJS(t, scope), + ScriptableObject.PERMANENT | ScriptableObject.DONTENUM); } if (cacheObj) { @@ -3886,29 +3576,26 @@ public static Scriptable newCatchScope(Throwable t, return catchScopeObject; } - public static Scriptable wrapException(Throwable t, - Scriptable scope, - Context cx) { + public static Scriptable wrapException(Throwable t, Scriptable scope, Context cx) { RhinoException re; String errorName; String errorMsg; Throwable javaException = null; if (t instanceof EcmaError) { - EcmaError ee = (EcmaError)t; + EcmaError ee = (EcmaError) t; re = ee; errorName = ee.getName(); errorMsg = ee.getErrorMessage(); } else if (t instanceof WrappedException) { - WrappedException we = (WrappedException)t; + WrappedException we = (WrappedException) t; re = we; javaException = we.getWrappedException(); errorName = "JavaException"; - errorMsg = javaException.getClass().getName() - +": "+javaException.getMessage(); + errorMsg = javaException.getClass().getName() + ": " + javaException.getMessage(); } else if (t instanceof EvaluatorException) { // Pure evaluator exception, nor WrappedException instance - EvaluatorException ee = (EvaluatorException)t; + EvaluatorException ee = (EvaluatorException) t; re = ee; errorName = "InternalError"; errorMsg = ee.getMessage(); @@ -3931,9 +3618,9 @@ public static Scriptable wrapException(Throwable t, int line = re.lineNumber(); Object args[]; if (line > 0) { - args = new Object[] { errorMsg, sourceUri, Integer.valueOf(line) }; + args = new Object[] {errorMsg, sourceUri, Integer.valueOf(line)}; } else { - args = new Object[] { errorMsg, sourceUri }; + args = new Object[] {errorMsg, sourceUri}; } Scriptable errorObject = cx.newObject(scope, errorName, args); @@ -3944,79 +3631,75 @@ public static Scriptable wrapException(Throwable t, } if (javaException != null && isVisible(cx, javaException)) { - Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException, - null); + Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException, null); ScriptableObject.defineProperty( - errorObject, "javaException", wrap, - ScriptableObject.PERMANENT | ScriptableObject.READONLY | ScriptableObject.DONTENUM); + errorObject, + "javaException", + wrap, + ScriptableObject.PERMANENT + | ScriptableObject.READONLY + | ScriptableObject.DONTENUM); } if (isVisible(cx, re)) { Object wrap = cx.getWrapFactory().wrap(cx, scope, re, null); ScriptableObject.defineProperty( - errorObject, "rhinoException", wrap, - ScriptableObject.PERMANENT | ScriptableObject.READONLY | ScriptableObject.DONTENUM); + errorObject, + "rhinoException", + wrap, + ScriptableObject.PERMANENT + | ScriptableObject.READONLY + | ScriptableObject.DONTENUM); } return errorObject; } private static boolean isVisible(Context cx, Object obj) { ClassShutter shutter = cx.getClassShutter(); - return shutter == null || - shutter.visibleToScripts(obj.getClass().getName()); + return shutter == null || shutter.visibleToScripts(obj.getClass().getName()); } - public static Scriptable enterWith(Object obj, Context cx, - Scriptable scope) - { + public static Scriptable enterWith(Object obj, Context cx, Scriptable scope) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { throw typeErrorById("msg.undef.with", toString(obj)); } if (sobj instanceof XMLObject) { - XMLObject xmlObject = (XMLObject)sobj; + XMLObject xmlObject = (XMLObject) sobj; return xmlObject.enterWith(scope); } return new NativeWith(scope, sobj); } - public static Scriptable leaveWith(Scriptable scope) - { - NativeWith nw = (NativeWith)scope; + public static Scriptable leaveWith(Scriptable scope) { + NativeWith nw = (NativeWith) scope; return nw.getParentScope(); } - public static Scriptable enterDotQuery(Object value, Scriptable scope) - { + public static Scriptable enterDotQuery(Object value, Scriptable scope) { if (!(value instanceof XMLObject)) { throw notXmlError(value); } - XMLObject object = (XMLObject)value; + XMLObject object = (XMLObject) value; return object.enterDotQuery(scope); } - public static Object updateDotQuery(boolean value, Scriptable scope) - { + public static Object updateDotQuery(boolean value, Scriptable scope) { // Return null to continue looping - NativeWith nw = (NativeWith)scope; + NativeWith nw = (NativeWith) scope; return nw.updateDotQuery(value); } - public static Scriptable leaveDotQuery(Scriptable scope) - { - NativeWith nw = (NativeWith)scope; + public static Scriptable leaveDotQuery(Scriptable scope) { + NativeWith nw = (NativeWith) scope; return nw.getParentScope(); } - public static void setFunctionProtoAndParent(BaseFunction fn, - Scriptable scope) - { + public static void setFunctionProtoAndParent(BaseFunction fn, Scriptable scope) { setFunctionProtoAndParent(fn, scope, false); } - public static void setFunctionProtoAndParent(BaseFunction fn, - Scriptable scope, - boolean es6GeneratorFunction) - { + public static void setFunctionProtoAndParent( + BaseFunction fn, Scriptable scope, boolean es6GeneratorFunction) { fn.setParentScope(scope); if (es6GeneratorFunction) { fn.setPrototype(ScriptableObject.getGeneratorFunctionPrototype(scope)); @@ -4025,39 +3708,31 @@ public static void setFunctionProtoAndParent(BaseFunction fn, } } - public static void setObjectProtoAndParent(ScriptableObject object, - Scriptable scope) - { + public static void setObjectProtoAndParent(ScriptableObject object, Scriptable scope) { // Compared with function it always sets the scope to top scope scope = ScriptableObject.getTopLevelScope(scope); object.setParentScope(scope); - Scriptable proto - = ScriptableObject.getClassPrototype(scope, object.getClassName()); + Scriptable proto = ScriptableObject.getClassPrototype(scope, object.getClassName()); object.setPrototype(proto); } - public static void setBuiltinProtoAndParent(ScriptableObject object, - Scriptable scope, - TopLevel.Builtins type) - { + public static void setBuiltinProtoAndParent( + ScriptableObject object, Scriptable scope, TopLevel.Builtins type) { scope = ScriptableObject.getTopLevelScope(scope); object.setParentScope(scope); object.setPrototype(TopLevel.getBuiltinPrototype(scope, type)); } - - public static void initFunction(Context cx, Scriptable scope, - NativeFunction function, int type, - boolean fromEvalCode) - { + public static void initFunction( + Context cx, Scriptable scope, NativeFunction function, int type, boolean fromEvalCode) { if (type == FunctionNode.FUNCTION_STATEMENT) { String name = function.getFunctionName(); if (name != null && name.length() != 0) { if (!fromEvalCode) { // ECMA specifies that functions defined in global and // function scope outside eval should have DONTDELETE set. - ScriptableObject.defineProperty - (scope, name, function, ScriptableObject.PERMANENT); + ScriptableObject.defineProperty( + scope, name, function, ScriptableObject.PERMANENT); } else { scope.put(name, scope, function); } @@ -4078,10 +3753,8 @@ public static void initFunction(Context cx, Scriptable scope, } } - public static Scriptable newArrayLiteral(Object[] objects, - int[] skipIndices, - Context cx, Scriptable scope) - { + public static Scriptable newArrayLiteral( + Object[] objects, int[] skipIndices, Context cx, Scriptable scope) { final int SKIP_DENSITY = 2; int count = objects.length; int skipCount = 0; @@ -4125,26 +3798,26 @@ public static Scriptable newArrayLiteral(Object[] objects, } /** - * This method is here for backward compat with existing compiled code. It - * is called when an object literal is compiled. The next instance will be - * the version called from new code. + * This method is here for backward compat with existing compiled code. It is called when an + * object literal is compiled. The next instance will be the version called from new code. * This method only present for compatibility. - * @deprecated Use {@link #newObjectLiteral(Object[], Object[], int[], Context, Scriptable)} instead + * + * @deprecated Use {@link #newObjectLiteral(Object[], Object[], int[], Context, Scriptable)} + * instead */ @Deprecated - public static Scriptable newObjectLiteral(Object[] propertyIds, - Object[] propertyValues, - Context cx, Scriptable scope) - { + public static Scriptable newObjectLiteral( + Object[] propertyIds, Object[] propertyValues, Context cx, Scriptable scope) { // Passing null for getterSetters means no getters or setters return newObjectLiteral(propertyIds, propertyValues, null, cx, scope); } - public static Scriptable newObjectLiteral(Object[] propertyIds, - Object[] propertyValues, - int [] getterSetters, - Context cx, Scriptable scope) - { + public static Scriptable newObjectLiteral( + Object[] propertyIds, + Object[] propertyValues, + int[] getterSetters, + Context cx, + Scriptable scope) { Scriptable object = cx.newObject(scope); for (int i = 0, end = propertyIds.length; i != end; ++i) { Object id = propertyIds[i]; @@ -4152,33 +3825,31 @@ public static Scriptable newObjectLiteral(Object[] propertyIds, Object value = propertyValues[i]; if (id instanceof String) { if (getterSetter == 0) { - if (isSpecialProperty((String)id)) { - Ref ref = specialRef(object, (String)id, cx, scope); + if (isSpecialProperty((String) id)) { + Ref ref = specialRef(object, (String) id, cx, scope); ref.set(cx, scope, value); } else { - object.put((String)id, object, value); + object.put((String) id, object, value); } } else { - ScriptableObject so = (ScriptableObject)object; - Callable getterOrSetter = (Callable)value; + ScriptableObject so = (ScriptableObject) object; + Callable getterOrSetter = (Callable) value; boolean isSetter = getterSetter == 1; - so.setGetterOrSetter((String)id, 0, getterOrSetter, isSetter); + so.setGetterOrSetter((String) id, 0, getterOrSetter, isSetter); } } else { - int index = ((Integer)id).intValue(); + int index = ((Integer) id).intValue(); object.put(index, object, value); } } return object; } - public static boolean isArrayObject(Object obj) - { + public static boolean isArrayObject(Object obj) { return obj instanceof NativeArray || obj instanceof Arguments; } - public static Object[] getArrayElements(Scriptable object) - { + public static Object[] getArrayElements(Scriptable object) { Context cx = Context.getContext(); long longLen = NativeArray.getLengthProperty(cx, object, false); if (longLen > Integer.MAX_VALUE) { @@ -4190,10 +3861,9 @@ public static Object[] getArrayElements(Scriptable object) return ScriptRuntime.emptyArgs; } Object[] result = new Object[len]; - for (int i=0; i < len; i++) { + for (int i = 0; i < len; i++) { Object elem = ScriptableObject.getProperty(object, i); - result[i] = (elem == Scriptable.NOT_FOUND) ? Undefined.instance - : elem; + result[i] = (elem == Scriptable.NOT_FOUND) ? Undefined.instance : elem; } return result; } @@ -4202,76 +3872,57 @@ static void checkDeprecated(Context cx, String name) { int version = cx.getLanguageVersion(); if (version >= Context.VERSION_1_4 || version == Context.VERSION_DEFAULT) { String msg = getMessageById("msg.deprec.ctor", name); - if (version == Context.VERSION_DEFAULT) - Context.reportWarning(msg); - else - throw Context.reportRuntimeError(msg); + if (version == Context.VERSION_DEFAULT) Context.reportWarning(msg); + else throw Context.reportRuntimeError(msg); } } - /** - * @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */ @Deprecated - public static String getMessage0(String messageId) - { + public static String getMessage0(String messageId) { return getMessage(messageId, null); } - /** - * @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */ @Deprecated - public static String getMessage1(String messageId, Object arg1) - { + public static String getMessage1(String messageId, Object arg1) { Object[] arguments = {arg1}; return getMessage(messageId, arguments); } - /** - * @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */ @Deprecated - public static String getMessage2( - String messageId, Object arg1, Object arg2) - { + public static String getMessage2(String messageId, Object arg1, Object arg2) { Object[] arguments = {arg1, arg2}; return getMessage(messageId, arguments); } - /** - * @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */ @Deprecated - public static String getMessage3( - String messageId, Object arg1, Object arg2, Object arg3) - { + public static String getMessage3(String messageId, Object arg1, Object arg2, Object arg3) { Object[] arguments = {arg1, arg2, arg3}; return getMessage(messageId, arguments); } - /** - * @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */ @Deprecated public static String getMessage4( - String messageId, Object arg1, Object arg2, Object arg3, Object arg4) - { + String messageId, Object arg1, Object arg2, Object arg3, Object arg4) { Object[] arguments = {arg1, arg2, arg3, arg4}; return getMessage(messageId, arguments); } /** - * This is an interface defining a message provider. Create your - * own implementation to override the default error message provider. + * This is an interface defining a message provider. Create your own implementation to override + * the default error message provider. * * @author Mike Harm */ public interface MessageProvider { /** - * Returns a textual message identified by the given messageId, - * parameterized by the given arguments. + * Returns a textual message identified by the given messageId, parameterized by the given + * arguments. * * @param messageId the identifier of the message * @param arguments the arguments to fill into the message @@ -4281,17 +3932,13 @@ public interface MessageProvider { public static final MessageProvider messageProvider = new DefaultMessageProvider(); - /** - * @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */ @Deprecated - public static String getMessage(String messageId, Object[] arguments) - { + public static String getMessage(String messageId, Object[] arguments) { return messageProvider.getMessage(messageId, arguments); } - public static String getMessageById(String messageId, Object... args) - { + public static String getMessageById(String messageId, Object... args) { return messageProvider.getMessage(messageId, args); } @@ -4302,8 +3949,7 @@ public static String getMessageById(String messageId, Object... args) private static class DefaultMessageProvider implements MessageProvider { @Override public String getMessage(String messageId, Object[] arguments) { - final String defaultResource - = "org.mozilla.javascript.resources.Messages"; + final String defaultResource = "org.mozilla.javascript.resources.Messages"; Context cx = Context.getCurrentContext(); Locale locale = cx != null ? cx.getLocale() : Locale.getDefault(); @@ -4315,8 +3961,8 @@ public String getMessage(String messageId, Object[] arguments) { try { formatString = rb.getString(messageId); } catch (java.util.MissingResourceException mre) { - throw new RuntimeException - ("no message resource found for message property "+ messageId); + throw new RuntimeException( + "no message resource found for message property " + messageId); } /* @@ -4329,17 +3975,13 @@ public String getMessage(String messageId, Object[] arguments) { } } - public static EcmaError constructError(String error, String message) - { + public static EcmaError constructError(String error, String message) { int[] linep = new int[1]; String filename = Context.getSourcePositionFromStack(linep); return constructError(error, message, filename, linep[0], null, 0); } - public static EcmaError constructError(String error, - String message, - int lineNumberDelta) - { + public static EcmaError constructError(String error, String message, int lineNumberDelta) { int[] linep = new int[1]; String filename = Context.getSourcePositionFromStack(linep); if (linep[0] != 0) { @@ -4348,126 +3990,94 @@ public static EcmaError constructError(String error, return constructError(error, message, filename, linep[0], null, 0); } - public static EcmaError constructError(String error, - String message, - String sourceName, - int lineNumber, - String lineSource, - int columnNumber) - { - return new EcmaError(error, message, sourceName, - lineNumber, lineSource, columnNumber); + public static EcmaError constructError( + String error, + String message, + String sourceName, + int lineNumber, + String lineSource, + int columnNumber) { + return new EcmaError(error, message, sourceName, lineNumber, lineSource, columnNumber); } - public static EcmaError rangeError(String message) - { + public static EcmaError rangeError(String message) { return constructError("RangeError", message); } - public static EcmaError typeError(String message) - { + public static EcmaError typeError(String message) { return constructError("TypeError", message); } - public static EcmaError typeErrorById(String messageId, Object... args) - { + public static EcmaError typeErrorById(String messageId, Object... args) { String msg = getMessageById(messageId, args); return typeError(msg); } - /** - * @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead */ @Deprecated - public static EcmaError typeError0(String messageId) - { + public static EcmaError typeError0(String messageId) { String msg = getMessage0(messageId); return typeError(msg); } - /** - * @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead */ @Deprecated - public static EcmaError typeError1(String messageId, Object arg1) - { + public static EcmaError typeError1(String messageId, Object arg1) { String msg = getMessage1(messageId, arg1); return typeError(msg); } - /** - * @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead */ @Deprecated - public static EcmaError typeError2(String messageId, Object arg1, - Object arg2) - { + public static EcmaError typeError2(String messageId, Object arg1, Object arg2) { String msg = getMessage2(messageId, arg1, arg2); return typeError(msg); } - /** - * @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead - */ + /** @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead */ @Deprecated - public static EcmaError typeError3(String messageId, String arg1, - String arg2, String arg3) - { + public static EcmaError typeError3(String messageId, String arg1, String arg2, String arg3) { String msg = getMessage3(messageId, arg1, arg2, arg3); return typeError(msg); } - public static RuntimeException undefReadError(Object object, Object id) - { + public static RuntimeException undefReadError(Object object, Object id) { return typeErrorById("msg.undef.prop.read", toString(object), toString(id)); } - public static RuntimeException undefCallError(Object object, Object id) - { + public static RuntimeException undefCallError(Object object, Object id) { return typeErrorById("msg.undef.method.call", toString(object), toString(id)); } - public static RuntimeException undefWriteError(Object object, - Object id, - Object value) - { - return typeErrorById("msg.undef.prop.write", toString(object), toString(id), - toString(value)); + public static RuntimeException undefWriteError(Object object, Object id, Object value) { + return typeErrorById( + "msg.undef.prop.write", toString(object), toString(id), toString(value)); } - private static RuntimeException undefDeleteError(Object object, Object id) - { + private static RuntimeException undefDeleteError(Object object, Object id) { throw typeErrorById("msg.undef.prop.delete", toString(object), toString(id)); } - public static RuntimeException notFoundError(Scriptable object, - String property) - { + public static RuntimeException notFoundError(Scriptable object, String property) { // XXX: use object to improve the error message String msg = getMessageById("msg.is.not.defined", property); throw constructError("ReferenceError", msg); } - public static RuntimeException notFunctionError(Object value) - { + public static RuntimeException notFunctionError(Object value) { return notFunctionError(value, value); } - public static RuntimeException notFunctionError(Object value, - Object messageHelper) - { + public static RuntimeException notFunctionError(Object value, Object messageHelper) { // Use value for better error reporting - String msg = (messageHelper == null) - ? "null" : messageHelper.toString(); + String msg = (messageHelper == null) ? "null" : messageHelper.toString(); if (value == Scriptable.NOT_FOUND) { return typeErrorById("msg.function.not.found", msg); } return typeErrorById("msg.isnt.function", msg, typeof(value)); } - public static RuntimeException notFunctionError(Object obj, Object value, - String propertyName) - { + public static RuntimeException notFunctionError(Object obj, Object value, String propertyName) { // Use obj and value for better error reporting String objString = toString(obj); if (obj instanceof NativeFunction) { @@ -4479,42 +4089,39 @@ public static RuntimeException notFunctionError(Object obj, Object value, } } if (value == Scriptable.NOT_FOUND) { - return typeErrorById("msg.function.not.found.in", propertyName, - objString); + return typeErrorById("msg.function.not.found.in", propertyName, objString); } - return typeErrorById("msg.isnt.function.in", propertyName, objString, - typeof(value)); + return typeErrorById("msg.isnt.function.in", propertyName, objString, typeof(value)); } - private static RuntimeException notXmlError(Object value) - { + private static RuntimeException notXmlError(Object value) { throw typeErrorById("msg.isnt.xml.object", toString(value)); } - private static void warnAboutNonJSObject(Object nonJSObject) - { + private static void warnAboutNonJSObject(Object nonJSObject) { final String omitParam = ScriptRuntime.getMessageById("params.omit.non.js.object.warning"); if (!"true".equals(omitParam)) { - String message = ScriptRuntime.getMessageById("msg.non.js.object.warning",nonJSObject,nonJSObject.getClass().getName()); + String message = + ScriptRuntime.getMessageById( + "msg.non.js.object.warning", + nonJSObject, + nonJSObject.getClass().getName()); Context.reportWarning(message); // Just to be sure that it would be noticed System.err.println(message); } } - public static RegExpProxy getRegExpProxy(Context cx) - { + public static RegExpProxy getRegExpProxy(Context cx) { return cx.getRegExpProxy(); } - public static void setRegExpProxy(Context cx, RegExpProxy proxy) - { + public static void setRegExpProxy(Context cx, RegExpProxy proxy) { if (proxy == null) throw new IllegalArgumentException(); cx.regExpProxy = proxy; } - public static RegExpProxy checkRegExpProxy(Context cx) - { + public static RegExpProxy checkRegExpProxy(Context cx) { RegExpProxy result = getRegExpProxy(cx); if (result == null) { throw Context.reportRuntimeErrorById("msg.no.regexp"); @@ -4522,22 +4129,18 @@ public static RegExpProxy checkRegExpProxy(Context cx) return result; } - public static Scriptable wrapRegExp(Context cx, Scriptable scope, - Object compiled) { + public static Scriptable wrapRegExp(Context cx, Scriptable scope, Object compiled) { return cx.getRegExpProxy().wrapRegExp(cx, scope, compiled); } - private static XMLLib currentXMLLib(Context cx) - { + private static XMLLib currentXMLLib(Context cx) { // Scripts should be running to access this - if (cx.topCallScope == null) - throw new IllegalStateException(); + if (cx.topCallScope == null) throw new IllegalStateException(); XMLLib xmlLib = cx.cachedXMLLib; if (xmlLib == null) { xmlLib = XMLLib.extractFromScope(cx.topCallScope); - if (xmlLib == null) - throw new IllegalStateException(); + if (xmlLib == null) throw new IllegalStateException(); cx.cachedXMLLib = xmlLib; } @@ -4550,8 +4153,7 @@ private static XMLLib currentXMLLib(Context cx) * @param value Unescaped text * @return The escaped text */ - public static String escapeAttributeValue(Object value, Context cx) - { + public static String escapeAttributeValue(Object value, Context cx) { XMLLib xmlLib = currentXMLLib(cx); return xmlLib.escapeAttributeValue(value); } @@ -4562,141 +4164,130 @@ public static String escapeAttributeValue(Object value, Context cx) * @param value Unescaped text * @return The escaped text */ - public static String escapeTextValue(Object value, Context cx) - { + public static String escapeTextValue(Object value, Context cx) { XMLLib xmlLib = currentXMLLib(cx); return xmlLib.escapeTextValue(value); } - public static Ref memberRef(Object obj, Object elem, - Context cx, int memberTypeFlags) - { + public static Ref memberRef(Object obj, Object elem, Context cx, int memberTypeFlags) { if (!(obj instanceof XMLObject)) { throw notXmlError(obj); } - XMLObject xmlObject = (XMLObject)obj; + XMLObject xmlObject = (XMLObject) obj; return xmlObject.memberRef(cx, elem, memberTypeFlags); } - public static Ref memberRef(Object obj, Object namespace, Object elem, - Context cx, int memberTypeFlags) - { + public static Ref memberRef( + Object obj, Object namespace, Object elem, Context cx, int memberTypeFlags) { if (!(obj instanceof XMLObject)) { throw notXmlError(obj); } - XMLObject xmlObject = (XMLObject)obj; + XMLObject xmlObject = (XMLObject) obj; return xmlObject.memberRef(cx, namespace, elem, memberTypeFlags); } - public static Ref nameRef(Object name, Context cx, - Scriptable scope, int memberTypeFlags) - { + public static Ref nameRef(Object name, Context cx, Scriptable scope, int memberTypeFlags) { XMLLib xmlLib = currentXMLLib(cx); return xmlLib.nameRef(cx, name, scope, memberTypeFlags); } - public static Ref nameRef(Object namespace, Object name, Context cx, - Scriptable scope, int memberTypeFlags) - { + public static Ref nameRef( + Object namespace, Object name, Context cx, Scriptable scope, int memberTypeFlags) { XMLLib xmlLib = currentXMLLib(cx); return xmlLib.nameRef(cx, namespace, name, scope, memberTypeFlags); } - public static void storeUint32Result(Context cx, long value) - { - if ((value >>> 32) != 0) - throw new IllegalArgumentException(); + public static void storeUint32Result(Context cx, long value) { + if ((value >>> 32) != 0) throw new IllegalArgumentException(); cx.scratchUint32 = value; } - public static long lastUint32Result(Context cx) - { + public static long lastUint32Result(Context cx) { long value = cx.scratchUint32; - if ((value >>> 32) != 0) - throw new IllegalStateException(); + if ((value >>> 32) != 0) throw new IllegalStateException(); return value; } - private static void storeScriptable(Context cx, Scriptable value) - { + private static void storeScriptable(Context cx, Scriptable value) { // The previously stored scratchScriptable should be consumed - if (cx.scratchScriptable != null) - throw new IllegalStateException(); + if (cx.scratchScriptable != null) throw new IllegalStateException(); cx.scratchScriptable = value; } - public static Scriptable lastStoredScriptable(Context cx) - { + public static Scriptable lastStoredScriptable(Context cx) { Scriptable result = cx.scratchScriptable; cx.scratchScriptable = null; return result; } - static String makeUrlForGeneratedScript - (boolean isEval, String masterScriptUrl, int masterScriptLine) - { + static String makeUrlForGeneratedScript( + boolean isEval, String masterScriptUrl, int masterScriptLine) { if (isEval) { - return masterScriptUrl+'#'+masterScriptLine+"(eval)"; + return masterScriptUrl + '#' + masterScriptLine + "(eval)"; } - return masterScriptUrl+'#'+masterScriptLine+"(Function)"; + return masterScriptUrl + '#' + masterScriptLine + "(Function)"; } static boolean isGeneratedScript(String sourceUrl) { // ALERT: this may clash with a valid URL containing (eval) or // (Function) - return sourceUrl.indexOf("(eval)") >= 0 - || sourceUrl.indexOf("(Function)") >= 0; + return sourceUrl.indexOf("(eval)") >= 0 || sourceUrl.indexOf("(Function)") >= 0; } /** - * Not all "NativeSymbol" instances are actually symbols. So account for that here rather than just - * by using an "instanceof" check. + * Not all "NativeSymbol" instances are actually symbols. So account for that here rather than + * just by using an "instanceof" check. */ static boolean isSymbol(Object obj) { - return (( (obj instanceof NativeSymbol) && ((NativeSymbol)obj).isSymbol())) - || (obj instanceof SymbolKey); + return (((obj instanceof NativeSymbol) && ((NativeSymbol) obj).isSymbol())) + || (obj instanceof SymbolKey); } - private static RuntimeException errorWithClassName(String msg, Object val) - { + private static RuntimeException errorWithClassName(String msg, Object val) { return Context.reportRuntimeErrorById(msg, val.getClass().getName()); } /** * Equivalent to executing "new Error(message, sourceFileName, sourceLineNo)" from JavaScript. + * * @param cx the current context * @param scope the current scope * @param message the message * @return a JavaScriptException you should throw */ - public static JavaScriptException throwError(Context cx, Scriptable scope, - String message) { - int[] linep = { 0 }; - String filename = Context.getSourcePositionFromStack(linep); - final Scriptable error = newBuiltinObject(cx, scope, - TopLevel.Builtins.Error, new Object[] { message, filename, Integer.valueOf(linep[0]) }); + public static JavaScriptException throwError(Context cx, Scriptable scope, String message) { + int[] linep = {0}; + String filename = Context.getSourcePositionFromStack(linep); + final Scriptable error = + newBuiltinObject( + cx, + scope, + TopLevel.Builtins.Error, + new Object[] {message, filename, Integer.valueOf(linep[0])}); return new JavaScriptException(error, filename, linep[0]); } - /** - * Equivalent to executing "new $constructorName(message, sourceFileName, sourceLineNo)" from JavaScript. + * Equivalent to executing "new $constructorName(message, sourceFileName, sourceLineNo)" from + * JavaScript. + * * @param cx the current context * @param scope the current scope * @param message the message * @return a JavaScriptException you should throw */ - public static JavaScriptException throwCustomError(Context cx, Scriptable scope, String constructorName, - String message) { - int[] linep = { 0 }; - String filename = Context.getSourcePositionFromStack(linep); - final Scriptable error = cx.newObject(scope, constructorName, - new Object[] { message, filename, Integer.valueOf(linep[0]) }); - return new JavaScriptException(error, filename, linep[0]); + public static JavaScriptException throwCustomError( + Context cx, Scriptable scope, String constructorName, String message) { + int[] linep = {0}; + String filename = Context.getSourcePositionFromStack(linep); + final Scriptable error = + cx.newObject( + scope, + constructorName, + new Object[] {message, filename, Integer.valueOf(linep[0])}); + return new JavaScriptException(error, filename, linep[0]); } public static final Object[] emptyArgs = new Object[0]; public static final String[] emptyStrings = new String[0]; - - } diff --git a/src/org/mozilla/javascript/WrapFactory.java b/src/org/mozilla/javascript/WrapFactory.java index bb1f23f733..a30fe4dacf 100644 --- a/src/org/mozilla/javascript/WrapFactory.java +++ b/src/org/mozilla/javascript/WrapFactory.java @@ -13,65 +13,57 @@ import java.util.Map; /** - * Embeddings that wish to provide their own custom wrappings for Java - * objects may extend this class and call - * {@link Context#setWrapFactory(WrapFactory)} - * Once an instance of this class or an extension of this class is enabled - * for a given context (by calling setWrapFactory on that context), Rhino - * will call the methods of this class whenever it needs to wrap a value - * resulting from a call to a Java method or an access to a Java field. + * Embeddings that wish to provide their own custom wrappings for Java objects may extend this class + * and call {@link Context#setWrapFactory(WrapFactory)} Once an instance of this class or an + * extension of this class is enabled for a given context (by calling setWrapFactory on that + * context), Rhino will call the methods of this class whenever it needs to wrap a value resulting + * from a call to a Java method or an access to a Java field. * * @see org.mozilla.javascript.Context#setWrapFactory(WrapFactory) * @since 1.5 Release 4 */ -public class WrapFactory -{ +public class WrapFactory { /** * Wrap the object. - *

- * The value returned must be one of + * + *

The value returned must be one of + * *

    - *
  • java.lang.Boolean
  • - *
  • java.lang.String
  • - *
  • java.lang.Number
  • - *
  • org.mozilla.javascript.Scriptable objects
  • - *
  • The value returned by Context.getUndefinedValue()
  • - *
  • null
  • + *
  • java.lang.Boolean + *
  • java.lang.String + *
  • java.lang.Number + *
  • org.mozilla.javascript.Scriptable objects + *
  • The value returned by Context.getUndefinedValue() + *
  • null *
+ * * @param cx the current Context for this thread * @param scope the scope of the executing script * @param obj the object to be wrapped. Note it can be null. - * @param staticType type hint. If security restrictions prevent to wrap - object based on its class, staticType will be used instead. + * @param staticType type hint. If security restrictions prevent to wrap object based on its + * class, staticType will be used instead. * @return the wrapped value. */ - public Object wrap(Context cx, Scriptable scope, - Object obj, Type staticType) - { - if (obj == null || obj == Undefined.instance - || obj instanceof Scriptable) - { + public Object wrap(Context cx, Scriptable scope, Object obj, Type staticType) { + if (obj == null || obj == Undefined.instance || obj instanceof Scriptable) { return obj; } - if (staticType instanceof Class && ((Class)staticType).isPrimitive()) { - if (staticType == Void.TYPE) - return Undefined.instance; - if (staticType == Character.TYPE) - return Integer.valueOf(((Character) obj).charValue()); + if (staticType instanceof Class && ((Class) staticType).isPrimitive()) { + if (staticType == Void.TYPE) return Undefined.instance; + if (staticType == Character.TYPE) return Integer.valueOf(((Character) obj).charValue()); return obj; } if (!isJavaPrimitiveWrap()) { - if (obj instanceof String || - obj instanceof Boolean || - obj instanceof Integer || - obj instanceof Short || - obj instanceof Long || - obj instanceof Float || - obj instanceof Double) - { + if (obj instanceof String + || obj instanceof Boolean + || obj instanceof Integer + || obj instanceof Short + || obj instanceof Long + || obj instanceof Float + || obj instanceof Double) { return obj; } else if (obj instanceof Character) { - return String.valueOf(((Character)obj).charValue()); + return String.valueOf(((Character) obj).charValue()); } } Class cls = obj.getClass(); @@ -83,15 +75,15 @@ public Object wrap(Context cx, Scriptable scope, /** * Wrap an object newly created by a constructor call. + * * @param cx the current Context for this thread * @param scope the scope of the executing script * @param obj the object to be wrapped * @return the wrapped value. */ - public Scriptable wrapNewObject(Context cx, Scriptable scope, Object obj) - { + public Scriptable wrapNewObject(Context cx, Scriptable scope, Object obj) { if (obj instanceof Scriptable) { - return (Scriptable)obj; + return (Scriptable) obj; } Class cls = obj.getClass(); if (cls.isArray()) { @@ -101,26 +93,24 @@ public Scriptable wrapNewObject(Context cx, Scriptable scope, Object obj) } /** - * Wrap Java object as Scriptable instance to allow full access to its - * methods and fields from JavaScript. - *

- * {@link #wrap(Context, Scriptable, Object, Type)} and - * {@link #wrapNewObject(Context, Scriptable, Object)} call this method - * when they can not convert javaObject to JavaScript primitive - * value or JavaScript array. - *

- * Subclasses can override the method to provide custom wrappers - * for Java objects. + * Wrap Java object as Scriptable instance to allow full access to its methods and fields from + * JavaScript. + * + *

{@link #wrap(Context, Scriptable, Object, Type)} and {@link #wrapNewObject(Context, + * Scriptable, Object)} call this method when they can not convert javaObject to + * JavaScript primitive value or JavaScript array. + * + *

Subclasses can override the method to provide custom wrappers for Java objects. + * * @param cx the current Context for this thread * @param scope the scope of the executing script * @param javaObject the object to be wrapped - * @param staticType type hint. If security restrictions prevent to wrap - object based on its class, staticType will be used instead. + * @param staticType type hint. If security restrictions prevent to wrap object based on its + * class, staticType will be used instead. * @return the wrapped value which shall not be null */ - public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, - Object javaObject, Type staticType) - { + public Scriptable wrapAsJavaObject( + Context cx, Scriptable scope, Object javaObject, Type staticType) { if (javaObject instanceof List) { return new NativeJavaList(scope, javaObject, staticType); } else if (javaObject instanceof Map) { @@ -131,11 +121,10 @@ public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, } /** - * Wrap a Java class as Scriptable instance to allow access to its static - * members and fields and use as constructor from JavaScript. - *

- * Subclasses can override this method to provide custom wrappers for - * Java classes. + * Wrap a Java class as Scriptable instance to allow access to its static members and fields and + * use as constructor from JavaScript. + * + *

Subclasses can override this method to provide custom wrappers for Java classes. * * @param cx the current Context for this thread * @param scope the scope of the executing script @@ -143,33 +132,24 @@ public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, * @return the wrapped value which shall not be null * @since 1.7R3 */ - public Scriptable wrapJavaClass(Context cx, Scriptable scope, - Class javaClass) - { + public Scriptable wrapJavaClass(Context cx, Scriptable scope, Class javaClass) { return new NativeJavaClass(scope, javaClass); } /** - * Return false if result of Java method, which is instance of - * String, Number, Boolean and - * Character, should be used directly as JavaScript primitive - * type. - * By default the method returns true to indicate that instances of - * String, Number, Boolean and - * Character should be wrapped as any other Java object and - * scripts can access any Java method available in these objects. - * Use {@link #setJavaPrimitiveWrap(boolean)} to change this. + * Return false if result of Java method, which is instance of String, + * Number, Boolean and Character, should be used directly + * as JavaScript primitive type. By default the method returns true to indicate that instances + * of String, Number, Boolean and Character + * should be wrapped as any other Java object and scripts can access any Java method available + * in these objects. Use {@link #setJavaPrimitiveWrap(boolean)} to change this. */ - public final boolean isJavaPrimitiveWrap() - { + public final boolean isJavaPrimitiveWrap() { return javaPrimitiveWrap; } - /** - * @see #isJavaPrimitiveWrap() - */ - public final void setJavaPrimitiveWrap(boolean value) - { + /** @see #isJavaPrimitiveWrap() */ + public final void setJavaPrimitiveWrap(boolean value) { Context cx = Context.getCurrentContext(); if (cx != null && cx.isSealed()) { Context.onSealedMutation(); @@ -178,5 +158,4 @@ public final void setJavaPrimitiveWrap(boolean value) } private boolean javaPrimitiveWrap = true; - } diff --git a/testsrc/org/mozilla/javascript/tests/JavaIterableIteratorTest.java b/testsrc/org/mozilla/javascript/tests/JavaIterableIteratorTest.java index 28404a8142..9832e8fa24 100644 --- a/testsrc/org/mozilla/javascript/tests/JavaIterableIteratorTest.java +++ b/testsrc/org/mozilla/javascript/tests/JavaIterableIteratorTest.java @@ -8,21 +8,16 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Comparator; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; import java.util.Set; - +import junit.framework.TestCase; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.mozilla.javascript.ScriptableObject; -import org.mozilla.javascript.Wrapper; - -import junit.framework.TestCase; /* * This testcase tests the basic access to Java classess implementing Iterable @@ -32,20 +27,19 @@ public class JavaIterableIteratorTest extends TestCase { private static final String FOO_BAR_BAZ = "foo,bar,42.5,"; - + @Parameters public static Collection> data() { - return Arrays.asList(new Iterable[] { - arrayList(), linkedHashSet(), iterable(), collection() - }); + return Arrays.asList( + new Iterable[] {arrayList(), linkedHashSet(), iterable(), collection()}); } private Iterable iterable; - + public JavaIterableIteratorTest(Iterable iterable) { - this.iterable = iterable; + this.iterable = iterable; } - + private static List arrayList() { List list = new ArrayList<>(); list.add("foo"); @@ -53,51 +47,50 @@ private static List arrayList() { list.add(42.5); return list; } + private static Set linkedHashSet() { return new LinkedHashSet<>(arrayList()); } private static Iterable iterable() { return new Iterable() { - + @Override public Iterator iterator() { return arrayList().iterator(); } }; } - + private static Collection collection() { - return new AbstractCollection() { - - @Override - public Iterator iterator() { - return arrayList().iterator(); - } + return new AbstractCollection() { - @Override - public int size() { - return arrayList().size(); - } - }; + @Override + public Iterator iterator() { + return arrayList().iterator(); + } + + @Override + public int size() { + return arrayList().size(); + } + }; } - - + @Test public void testArrayIterator() { - String js = "var ret = '';\n" - + "var iter = list.iterator();\n" - + "while(iter.hasNext()) ret += iter.next()+',';\n" - + "ret"; + String js = + "var ret = '';\n" + + "var iter = list.iterator();\n" + + "while(iter.hasNext()) ret += iter.next()+',';\n" + + "ret"; testJavaObjectIterate(js, FOO_BAR_BAZ); // there is no .iterator() function on the JS side } @Test public void testArrayForEach() { - String js = "var ret = '';\n" - + "for each(elem in list) ret += elem + ',';\n" - + "ret"; + String js = "var ret = '';\n" + "for each(elem in list) ret += elem + ',';\n" + "ret"; testJsArrayIterate(js, FOO_BAR_BAZ); testJavaObjectIterate(js, FOO_BAR_BAZ); testJavaArrayIterate(js, FOO_BAR_BAZ); @@ -105,9 +98,7 @@ public void testArrayForEach() { @Test public void testArrayForKeys() { - String js = "var ret = '';\n" - + "for(elem in list) ret += elem + ',';\n" - + "ret"; + String js = "var ret = '';\n" + "for(elem in list) ret += elem + ',';\n" + "ret"; testJsArrayIterate(js, "0,1,2,"); if (iterable instanceof Collection) { testJavaObjectIterate(js, "0,1,2,"); @@ -117,9 +108,10 @@ public void testArrayForKeys() { @Test public void testArrayForIndex() { - String js = "var ret = '';\n" - + "for(var idx = 0; idx < list.length; idx++) ret += idx + ',';\n" - + "ret"; + String js = + "var ret = '';\n" + + "for(var idx = 0; idx < list.length; idx++) ret += idx + ',';\n" + + "ret"; testJsArrayIterate(js, "0,1,2,"); testJavaArrayIterate(js, "0,1,2,"); if (iterable instanceof Collection) { @@ -129,46 +121,43 @@ public void testArrayForIndex() { // use NativeJavaArray private void testJavaArrayIterate(String script, String expected) { - Utils.runWithAllOptimizationLevels(cx -> { - final ScriptableObject scope = cx.initStandardObjects(); - List list = new ArrayList<>(); - iterable.forEach(list::add); - scope.put("list", scope, list.toArray()); - Object o = cx.evaluateString(scope, script, - "testJavaArrayIterate.js", 1, null); - assertEquals(expected, o); - - return null; - }); + Utils.runWithAllOptimizationLevels( + cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + List list = new ArrayList<>(); + iterable.forEach(list::add); + scope.put("list", scope, list.toArray()); + Object o = cx.evaluateString(scope, script, "testJavaArrayIterate.js", 1, null); + assertEquals(expected, o); + + return null; + }); } - + // use the java object directly private void testJavaObjectIterate(String script, String expected) { - Utils.runWithAllOptimizationLevels(cx -> { - final ScriptableObject scope = cx.initStandardObjects(); - scope.put("list", scope, iterable); - Object o = cx.evaluateString(scope, script, - "testJavaListIterate.js", 1, null); - assertEquals(expected, o); - - return null; - }); - + Utils.runWithAllOptimizationLevels( + cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + scope.put("list", scope, iterable); + Object o = cx.evaluateString(scope, script, "testJavaListIterate.js", 1, null); + assertEquals(expected, o); + + return null; + }); } // use nativeArray private void testJsArrayIterate(String script, String expected) { - Utils.runWithAllOptimizationLevels(cx -> { - final ScriptableObject scope = cx.initStandardObjects(); - List list = new ArrayList<>(); - iterable.forEach(list::add); - scope.put("list", scope, - cx.newArray(scope, list.toArray())); - Object o = cx.evaluateString(scope, script, - "testJsArrayIterate.js", 1, null); - assertEquals(expected, o); - return null; - }); + Utils.runWithAllOptimizationLevels( + cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + List list = new ArrayList<>(); + iterable.forEach(list::add); + scope.put("list", scope, cx.newArray(scope, list.toArray())); + Object o = cx.evaluateString(scope, script, "testJsArrayIterate.js", 1, null); + assertEquals(expected, o); + return null; + }); } - } diff --git a/testsrc/org/mozilla/javascript/tests/JavaListAccessTest.java b/testsrc/org/mozilla/javascript/tests/JavaListAccessTest.java index a9c0b2f062..a3da30a278 100644 --- a/testsrc/org/mozilla/javascript/tests/JavaListAccessTest.java +++ b/testsrc/org/mozilla/javascript/tests/JavaListAccessTest.java @@ -5,14 +5,10 @@ package org.mozilla.javascript.tests; import java.util.ArrayList; -import java.util.Comparator; import java.util.List; - +import junit.framework.TestCase; import org.junit.Test; import org.mozilla.javascript.ScriptableObject; -import org.mozilla.javascript.Wrapper; - -import junit.framework.TestCase; /* * This testcase tests the basic access to java List with [] @@ -21,91 +17,89 @@ public class JavaListAccessTest extends TestCase { @Test public void testBeanAccess() { - String js = "bean.integers[0] = 3;\n" - + "bean.doubles[0] = 3;" - + "bean.doubles[0].getClass().getSimpleName() + ' ' " - + "+ bean.integers[0].getClass().getSimpleName()\n"; + String js = + "bean.integers[0] = 3;\n" + + "bean.doubles[0] = 3;" + + "bean.doubles[0].getClass().getSimpleName() + ' ' " + + "+ bean.integers[0].getClass().getSimpleName()\n"; testIt(js, "Double Integer"); } - + @Test public void testListAccess() { - String js = "intList[0] = 3;\n" - + "dblList[0] = 3;" - + "dblList[0].getClass().getSimpleName() + ' ' " - + "+ intList[0].getClass().getSimpleName()\n"; + String js = + "intList[0] = 3;\n" + + "dblList[0] = 3;" + + "dblList[0].getClass().getSimpleName() + ' ' " + + "+ intList[0].getClass().getSimpleName()\n"; testIt(js, "Double Integer"); } @Test public void testIntListIncrement() { - String js = "intList[0] = 3.5;\n" - + "intList[0]++;\n" - + "intList[0].getClass().getSimpleName() + ' ' + intList[0]\n"; + String js = + "intList[0] = 3.5;\n" + + "intList[0]++;\n" + + "intList[0].getClass().getSimpleName() + ' ' + intList[0]\n"; testIt(js, "Integer 4"); } - + @Test public void testDblListIncrement() { - String js = "dblList[0] = 3.5;\n" - + "dblList[0]++;\n" - + "dblList[0].getClass().getSimpleName() + ' ' + dblList[0]\n"; + String js = + "dblList[0] = 3.5;\n" + + "dblList[0]++;\n" + + "dblList[0].getClass().getSimpleName() + ' ' + dblList[0]\n"; testIt(js, "Double 4.5"); } - public static class Bean { public List integers = new ArrayList<>(); private List doubles = new ArrayList<>(); - + public List getDoubles() { return doubles; } - + public List numbers = new ArrayList<>(); } - private List createIntegerList() { - List list = new ArrayList() { - - }; + List list = new ArrayList() {}; + list.add(42); list.add(7); return list; } private List createDoubleList() { - List list = new ArrayList() { - - }; + List list = new ArrayList() {}; + list.add(42.5); list.add(7.5); return list; } - + private List createNumberList() { - List list = new ArrayList() { - - }; + List list = new ArrayList() {}; + list.add(42); list.add(7.5); return list; } private void testIt(String script, String expected) { - Utils.runWithAllOptimizationLevels(cx -> { - final ScriptableObject scope = cx.initStandardObjects(); - scope.put("intList", scope, createIntegerList()); - scope.put("dblList", scope, createDoubleList()); - scope.put("numList", scope, createNumberList()); - scope.put("bean", scope, new Bean()); - Object o = cx.evaluateString(scope, script, - "testJavaArrayIterate.js", 1, null); - assertEquals(expected, o); - - return null; - }); - + Utils.runWithAllOptimizationLevels( + cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + scope.put("intList", scope, createIntegerList()); + scope.put("dblList", scope, createDoubleList()); + scope.put("numList", scope, createNumberList()); + scope.put("bean", scope, new Bean()); + Object o = cx.evaluateString(scope, script, "testJavaArrayIterate.js", 1, null); + assertEquals(expected, o); + + return null; + }); } } diff --git a/testsrc/org/mozilla/javascript/tests/JavaListIteratorTest.java b/testsrc/org/mozilla/javascript/tests/JavaListIteratorTest.java index 9a0352efb0..66f46df474 100644 --- a/testsrc/org/mozilla/javascript/tests/JavaListIteratorTest.java +++ b/testsrc/org/mozilla/javascript/tests/JavaListIteratorTest.java @@ -5,14 +5,10 @@ package org.mozilla.javascript.tests; import java.util.ArrayList; -import java.util.Comparator; import java.util.List; - +import junit.framework.TestCase; import org.junit.Test; import org.mozilla.javascript.ScriptableObject; -import org.mozilla.javascript.Wrapper; - -import junit.framework.TestCase; /* * This testcase tests the basic access to Java classess implementing Iterable @@ -21,7 +17,7 @@ public class JavaListIteratorTest extends TestCase { private static final String FOO_BAR_BAZ = "foo,bar,42.5,"; - + private List createJavaList() { List list = new ArrayList<>(); list.add("foo"); @@ -32,19 +28,18 @@ private List createJavaList() { @Test public void testArrayIterator() { - String js = "var ret = '';\n" - + "var iter = list.iterator();\n" - + "while(iter.hasNext()) ret += iter.next()+',';\n" - + "ret"; + String js = + "var ret = '';\n" + + "var iter = list.iterator();\n" + + "while(iter.hasNext()) ret += iter.next()+',';\n" + + "ret"; testJavaListIterate(js, FOO_BAR_BAZ); // there is no .iterator() function on the JS side } @Test public void testArrayForEach() { - String js = "var ret = '';\n" - + "for each(elem in list) ret += elem + ',';\n" - + "ret"; + String js = "var ret = '';\n" + "for each(elem in list) ret += elem + ',';\n" + "ret"; testJsArrayIterate(js, FOO_BAR_BAZ); testJavaListIterate(js, FOO_BAR_BAZ); testJavaArrayIterate(js, FOO_BAR_BAZ); @@ -52,9 +47,7 @@ public void testArrayForEach() { @Test public void testArrayForKeys() { - String js = "var ret = '';\n" - + "for(elem in list) ret += elem + ',';\n" - + "ret"; + String js = "var ret = '';\n" + "for(elem in list) ret += elem + ',';\n" + "ret"; testJsArrayIterate(js, "0,1,2,"); testJavaListIterate(js, "0,1,2,"); testJavaArrayIterate(js, "0,1,2,"); @@ -62,50 +55,48 @@ public void testArrayForKeys() { @Test public void testArrayForIndex() { - String js = "var ret = '';\n" - + "for(var idx = 0; idx < list.length; idx++) ret += idx + ',';\n" - + "ret"; + String js = + "var ret = '';\n" + + "for(var idx = 0; idx < list.length; idx++) ret += idx + ',';\n" + + "ret"; testJsArrayIterate(js, "0,1,2,"); testJavaArrayIterate(js, "0,1,2,"); testJavaListIterate(js, "0,1,2,"); } private void testJavaArrayIterate(String script, String expected) { - Utils.runWithAllOptimizationLevels(cx -> { - final ScriptableObject scope = cx.initStandardObjects(); - scope.put("list", scope, createJavaList().toArray()); - Object o = cx.evaluateString(scope, script, - "testJavaArrayIterate.js", 1, null); - assertEquals(expected, o); + Utils.runWithAllOptimizationLevels( + cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + scope.put("list", scope, createJavaList().toArray()); + Object o = cx.evaluateString(scope, script, "testJavaArrayIterate.js", 1, null); + assertEquals(expected, o); - return null; - }); + return null; + }); } - + private void testJavaListIterate(String script, String expected) { - Utils.runWithAllOptimizationLevels(cx -> { - final ScriptableObject scope = cx.initStandardObjects(); - scope.put("list", scope, createJavaList()); - Object o = cx.evaluateString(scope, script, - "testJavaListIterate.js", 1, null); - assertEquals(expected, o); - - return null; - }); - + Utils.runWithAllOptimizationLevels( + cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + scope.put("list", scope, createJavaList()); + Object o = cx.evaluateString(scope, script, "testJavaListIterate.js", 1, null); + assertEquals(expected, o); + + return null; + }); } private void testJsArrayIterate(String script, String expected) { - Utils.runWithAllOptimizationLevels(cx -> { - final ScriptableObject scope = cx.initStandardObjects(); + Utils.runWithAllOptimizationLevels( + cx -> { + final ScriptableObject scope = cx.initStandardObjects(); - scope.put("list", scope, - cx.newArray(scope, createJavaList().toArray())); - Object o = cx.evaluateString(scope, script, - "testJsArrayIterate.js", 1, null); - assertEquals(expected, o); - return null; - }); + scope.put("list", scope, cx.newArray(scope, createJavaList().toArray())); + Object o = cx.evaluateString(scope, script, "testJsArrayIterate.js", 1, null); + assertEquals(expected, o); + return null; + }); } - } diff --git a/testsrc/org/mozilla/javascript/tests/JavaMapIteratorTest.java b/testsrc/org/mozilla/javascript/tests/JavaMapIteratorTest.java index 95f730b991..4c6f9556a0 100644 --- a/testsrc/org/mozilla/javascript/tests/JavaMapIteratorTest.java +++ b/testsrc/org/mozilla/javascript/tests/JavaMapIteratorTest.java @@ -11,7 +11,6 @@ import java.util.EnumMap; import java.util.LinkedHashMap; import java.util.Map; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -20,7 +19,6 @@ import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; - /* * This testcase tests the basic access to Java classess implementing Iterable * (eg. ArrayList) @@ -30,14 +28,12 @@ public class JavaMapIteratorTest { private static final String EXPECTED_VALUES = "7,2,5,"; private static final String EXPECTED_KEYS = "foo,bar,baz,"; - + @Parameters - public static Collection> data() { - return Arrays.asList(new Map[] { - mapWithEnumKey(), - mapWithStringKey() - }); + public static Collection> data() { + return Arrays.asList(new Map[] {mapWithEnumKey(), mapWithStringKey()}); } + private static Map mapWithStringKey() { Map map = new LinkedHashMap<>(); map.put("foo", 7); @@ -45,29 +41,31 @@ private static Map mapWithStringKey() { map.put("baz", 5); return map; } - public enum MyEnum { - foo, bar, baz + + public enum MyEnum { + foo, + bar, + baz } + private static Map mapWithEnumKey() { - Map map = new EnumMap<>(MyEnum.class); - map.put(MyEnum.foo, 7); - map.put(MyEnum.bar, 2); - map.put(MyEnum.baz, 5); - return map; + Map map = new EnumMap<>(MyEnum.class); + map.put(MyEnum.foo, 7); + map.put(MyEnum.bar, 2); + map.put(MyEnum.baz, 5); + return map; } private Map map; - public JavaMapIteratorTest(Map map) { + public JavaMapIteratorTest(Map map) { this.map = map; } // iterate over all values with 'for each' @Test public void testForEachValue() { - String js = "var ret = '';\n" - + "for each(value in map) ret += value + ',';\n" - + "ret"; + String js = "var ret = '';\n" + "for each(value in map) ret += value + ',';\n" + "ret"; testJsMap(js, EXPECTED_VALUES); testJavaMap(js, EXPECTED_VALUES); } @@ -75,9 +73,7 @@ public void testForEachValue() { // iterate over all keys and concatenate them @Test public void testForKey() { - String js = "var ret = '';\n" - + "for(key in map) ret += key + ',';\n" - + "ret"; + String js = "var ret = '';\n" + "for(key in map) ret += key + ',';\n" + "ret"; testJsMap(js, EXPECTED_KEYS); testJavaMap(js, EXPECTED_KEYS); } @@ -85,9 +81,7 @@ public void testForKey() { // iterate over all keys and try to read the map value @Test public void testForKeyWithGet() { - String js = "var ret = '';\n" - + "for(key in map) ret += map[key] + ',';\n" - + "ret"; + String js = "var ret = '';\n" + "for(key in map) ret += map[key] + ',';\n" + "ret"; testJsMap(js, EXPECTED_VALUES); testJavaMap(js, EXPECTED_VALUES); } @@ -98,60 +92,60 @@ public void testForKeyWithGet() { // Java: forEach(key, value) @Test public void testMapForEach1() { - String js = "var ret = '';\n" - + "map.forEach(function(key) { ret += key + ',' });\n" - + "ret"; - testJavaMap(js, EXPECTED_KEYS); + String js = + "var ret = '';\n" + "map.forEach(function(key) { ret += key + ',' });\n" + "ret"; + testJavaMap(js, EXPECTED_KEYS); } - + @Test public void testMapForEach2() { - String js = "var ret = '';\n" - + "map.forEach(function(key, value) { ret += value + ',' });\n" - + "ret"; + String js = + "var ret = '';\n" + + "map.forEach(function(key, value) { ret += value + ',' });\n" + + "ret"; testJavaMap(js, EXPECTED_VALUES); // forEach(key, value) } - + @Test public void testMapForEach3() { - String js = "var ret = '';\n" - + "map.forEach(function(key) { ret += map[key] + ',' });\n" - + "ret"; - testJavaMap(js, EXPECTED_VALUES); + String js = + "var ret = '';\n" + + "map.forEach(function(key) { ret += map[key] + ',' });\n" + + "ret"; + testJavaMap(js, EXPECTED_VALUES); } - + @Test public void testObjectKeys() { String js = "Object.keys(map).join(',')+',';\n"; - testJavaMap(js, EXPECTED_KEYS); - testJsMap(js, EXPECTED_KEYS); + testJavaMap(js, EXPECTED_KEYS); + testJsMap(js, EXPECTED_KEYS); } - + private void testJavaMap(String script, Object expected) { - Utils.runWithAllOptimizationLevels(cx -> { - cx.setLanguageVersion(Context.VERSION_ES6); - final ScriptableObject scope = cx.initStandardObjects(); - scope.put("map", scope, map); - Object o = cx.evaluateString(scope, script, - "testJavaMap.js", 1, null); - assertEquals(expected, o); - - return null; - }); + Utils.runWithAllOptimizationLevels( + cx -> { + cx.setLanguageVersion(Context.VERSION_ES6); + final ScriptableObject scope = cx.initStandardObjects(); + scope.put("map", scope, map); + Object o = cx.evaluateString(scope, script, "testJavaMap.js", 1, null); + assertEquals(expected, o); + + return null; + }); } private void testJsMap(String script, Object expected) { - Utils.runWithAllOptimizationLevels(cx -> { - final ScriptableObject scope = cx.initStandardObjects(); - Scriptable obj = cx.newObject(scope); - map.forEach((key,value)->obj.put(String.valueOf(key), obj, value)); - scope.put("map", scope, obj); - Object o = cx.evaluateString(scope, script, - "testJsMap.js", 1, null); - assertEquals(expected, o); - - return null; - }); + Utils.runWithAllOptimizationLevels( + cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + Scriptable obj = cx.newObject(scope); + map.forEach((key, value) -> obj.put(String.valueOf(key), obj, value)); + scope.put("map", scope, obj); + Object o = cx.evaluateString(scope, script, "testJsMap.js", 1, null); + assertEquals(expected, o); + + return null; + }); } - } diff --git a/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java b/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java index c4196176b1..b9f25a4ff2 100644 --- a/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java +++ b/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java @@ -6,6 +6,9 @@ */ package org.mozilla.javascript.tests; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; import junit.framework.TestCase; import org.mozilla.javascript.Context; import org.mozilla.javascript.ContextFactory; @@ -13,25 +16,23 @@ import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.tools.shell.Global; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - -/** - * From @makusuko (Markus Sunela), imported from PR https://github.com/mozilla/rhino/pull/561 - */ +/** From @makusuko (Markus Sunela), imported from PR https://github.com/mozilla/rhino/pull/561 */ public class NativeJavaMapTest extends TestCase { protected final Global global = new Global(); public NativeJavaMapTest() { global.init(ContextFactory.getGlobal()); } - + public static enum MyEnum { - A, B, C, X, Y, Z + A, + B, + C, + X, + Y, + Z } - public void testAccessingJavaMapIntegerValues() { Map map = new HashMap<>(); map.put(0, 1); @@ -41,60 +42,67 @@ public void testAccessingJavaMapIntegerValues() { assertEquals(2, runScriptAsInt("value[1]", map)); assertEquals(3, runScriptAsInt("value[2]", map)); } + public void testAccessingJavaMapLongValues() { - Map map = new HashMap<>(); - map.put(0L, 1); - map.put(1L, 2); - map.put(2L, 3); - - assertEquals(2, runScriptAsInt("value[1]", map)); - assertEquals(3, runScriptAsInt("value[2]", map)); - runScriptAsString("value[4] = 4.01", map); - assertEquals(Double.valueOf(4.01), map.get(4)); - assertEquals(null, map.get(4L)); - } - + Map map = new HashMap<>(); + map.put(0L, 1); + map.put(1L, 2); + map.put(2L, 3); + + assertEquals(2, runScriptAsInt("value[1]", map)); + assertEquals(3, runScriptAsInt("value[2]", map)); + runScriptAsString("value[4] = 4.01", map); + assertEquals(Double.valueOf(4.01), map.get(4)); + assertEquals(null, map.get(4L)); + } + public void testAccessingJavaMapEnumValuesWithGeneric() { - // genrate inner class, that contains type information. - Map map = new HashMap() { - private static final long serialVersionUID = 1L; - }; - - map.put(MyEnum.A, 1); - map.put(MyEnum.B, 2); - map.put(MyEnum.C, 3); - - assertEquals(2, runScriptAsInt("value['B']", map)); - assertEquals(3, runScriptAsInt("value['C']", map)); - runScriptAsString("value['X'] = 4.01", map); - // we know the type info and can convert the key to Long and the value is rounded to Integer - assertEquals(Integer.valueOf(4),map.get(MyEnum.X)); - - try { - runScriptAsString("value['D'] = 4.0", map); - fail();; - } catch (IllegalArgumentException ex) { - assertEquals("No enum constant org.mozilla.javascript.tests.NativeJavaMapTest.MyEnum.D", ex.getMessage()); - } + // genrate inner class, that contains type information. + Map map = + new HashMap() { + private static final long serialVersionUID = 1L; + }; + + map.put(MyEnum.A, 1); + map.put(MyEnum.B, 2); + map.put(MyEnum.C, 3); + + assertEquals(2, runScriptAsInt("value['B']", map)); + assertEquals(3, runScriptAsInt("value['C']", map)); + runScriptAsString("value['X'] = 4.01", map); + // we know the type info and can convert the key to Long and the value is rounded to Integer + assertEquals(Integer.valueOf(4), map.get(MyEnum.X)); + + try { + runScriptAsString("value['D'] = 4.0", map); + fail(); + ; + } catch (IllegalArgumentException ex) { + assertEquals( + "No enum constant org.mozilla.javascript.tests.NativeJavaMapTest.MyEnum.D", + ex.getMessage()); + } } public void testAccessingJavaMapLongValuesWithGeneric() { - // genrate inner class, that contains type information. - Map map = new HashMap() { - private static final long serialVersionUID = 1L; - }; - - map.put(0L, 1); - map.put(1L, 2); - map.put(2L, 3); - - assertEquals(2, runScriptAsInt("value[1]", map)); - assertEquals(3, runScriptAsInt("value[2]", map)); - runScriptAsInt("value[4] = 4.0", map); - // we know the type info and can convert the key to Long and the value to Integer - assertEquals(Integer.valueOf(4),map.get(4L)); - assertEquals(null, map.get(4)); + // genrate inner class, that contains type information. + Map map = + new HashMap() { + private static final long serialVersionUID = 1L; + }; + + map.put(0L, 1); + map.put(1L, 2); + map.put(2L, 3); + + assertEquals(2, runScriptAsInt("value[1]", map)); + assertEquals(3, runScriptAsInt("value[2]", map)); + runScriptAsInt("value[4] = 4.0", map); + // we know the type info and can convert the key to Long and the value to Integer + assertEquals(Integer.valueOf(4), map.get(4L)); + assertEquals(null, map.get(4)); } + public void testJavaMethodCalls() { Map map = new HashMap<>(); map.put("a", 1); @@ -107,9 +115,9 @@ public void testJavaMethodCalls() { public void testUpdatingJavaMapIntegerValues() { Map map = new HashMap<>(); - map.put(0,1); - map.put(1,2); - map.put(2,3); + map.put(0, 1); + map.put(1, 2); + map.put(2, 3); assertEquals(2, runScriptAsInt("value[1]", map)); assertEquals(5, runScriptAsInt("value[1]=5;value[1]", map)); @@ -162,7 +170,8 @@ public void testUpdatingMapInMap() { public void testKeys() { Map map = new HashMap<>(); - NativeArray resEmpty = (NativeArray) runScript("Object.keys(value)", map, Function.identity()); + NativeArray resEmpty = + (NativeArray) runScript("Object.keys(value)", map, Function.identity()); assertEquals(0, resEmpty.size()); map.put("a", "a"); @@ -177,7 +186,8 @@ public void testKeys() { Map mapInt = new HashMap<>(); mapInt.put(42, "test"); - NativeArray resInt = (NativeArray) runScript("Object.keys(value)", mapInt, Function.identity()); + NativeArray resInt = + (NativeArray) runScript("Object.keys(value)", mapInt, Function.identity()); assertTrue(resInt.contains("42")); // Object.keys always return Strings as key } @@ -190,10 +200,13 @@ private String runScriptAsString(String scriptSourceText, Object value) { } private T runScript(String scriptSourceText, Object value, Function convert) { - return ContextFactory.getGlobal().call(context -> { - Scriptable scope = context.initStandardObjects(global); - scope.put("value", scope, Context.javaToJS(value, scope)); - return convert.apply(context.evaluateString(scope, scriptSourceText, "", 1, null)); - }); - } -} \ No newline at end of file + return ContextFactory.getGlobal() + .call( + context -> { + Scriptable scope = context.initStandardObjects(global); + scope.put("value", scope, Context.javaToJS(value, scope)); + return convert.apply( + context.evaluateString(scope, scriptSourceText, "", 1, null)); + }); + } +} From e0faf00d1f79a4d7167c947f69e6b4f76b32db36 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 8 Sep 2021 15:54:47 +0200 Subject: [PATCH 03/14] spotless some files --- examples/PrimitiveWrapFactory.java | 46 +- src/org/mozilla/javascript/ClassCache.java | 151 ++- src/org/mozilla/javascript/JavaMembers.java | 421 ++++----- .../mozilla/javascript/NativeJavaMethod.java | 228 ++--- .../mozilla/javascript/NativeJavaObject.java | 894 ++++++++---------- 5 files changed, 751 insertions(+), 989 deletions(-) diff --git a/examples/PrimitiveWrapFactory.java b/examples/PrimitiveWrapFactory.java index f527c4575e..fcb786c9e5 100644 --- a/examples/PrimitiveWrapFactory.java +++ b/examples/PrimitiveWrapFactory.java @@ -9,34 +9,28 @@ import org.mozilla.javascript.WrapFactory; /** - * An example WrapFactory that can be used to avoid wrapping of Java types - * that can be converted to ECMA primitive values. - * So java.lang.String is mapped to ECMA string, all java.lang.Numbers are - * mapped to ECMA numbers, and java.lang.Booleans are mapped to ECMA booleans - * instead of being wrapped as objects. Additionally java.lang.Character is - * converted to ECMA string with length 1. + * An example WrapFactory that can be used to avoid wrapping of Java types that can be converted to + * ECMA primitive values. So java.lang.String is mapped to ECMA string, all java.lang.Numbers are + * mapped to ECMA numbers, and java.lang.Booleans are mapped to ECMA booleans instead of being + * wrapped as objects. Additionally java.lang.Character is converted to ECMA string with length 1. * Other types have the default behavior. - *

- * Note that calling "new java.lang.String('foo')" in JavaScript with this - * wrap factory enabled will still produce a wrapped Java object since the - * WrapFactory.wrapNewObject method is not overridden. - *

- * The PrimitiveWrapFactory is enabled on a Context by calling setWrapFactory - * on that context. + * + *

Note that calling "new java.lang.String('foo')" in JavaScript with this wrap factory enabled + * will still produce a wrapped Java object since the WrapFactory.wrapNewObject method is not + * overridden. + * + *

The PrimitiveWrapFactory is enabled on a Context by calling setWrapFactory on that context. */ public class PrimitiveWrapFactory extends WrapFactory { - @Override - public Object wrap(Context cx, Scriptable scope, Object obj, - Class staticType) - { - if (obj instanceof String || obj instanceof Number || - obj instanceof Boolean) - { - return obj; - } else if (obj instanceof Character) { - char[] a = { ((Character)obj).charValue() }; - return new String(a); + + @Override + public Object wrap(Context cx, Scriptable scope, Object obj, Class staticType) { + if (obj instanceof String || obj instanceof Number || obj instanceof Boolean) { + return obj; + } else if (obj instanceof Character) { + char[] a = {((Character) obj).charValue()}; + return new String(a); + } + return super.wrap(cx, scope, obj, staticType); } - return super.wrap(cx, scope, obj, staticType); - } } diff --git a/src/org/mozilla/javascript/ClassCache.java b/src/org/mozilla/javascript/ClassCache.java index 97b3568e6e..11abba8a6c 100644 --- a/src/org/mozilla/javascript/ClassCache.java +++ b/src/org/mozilla/javascript/ClassCache.java @@ -11,61 +11,50 @@ import java.util.concurrent.ConcurrentHashMap; /** - * Cache of generated classes and data structures to access Java runtime - * from JavaScript. + * Cache of generated classes and data structures to access Java runtime from JavaScript. * * @author Igor Bukanov - * * @since Rhino 1.5 Release 5 */ -public class ClassCache implements Serializable -{ +public class ClassCache implements Serializable { + private static final long serialVersionUID = -8866246036237312215L; private static final Object AKEY = "ClassCache"; private volatile boolean cachingIsEnabled = true; - private transient Map,JavaMembers> classTable; - private transient Map> classAdapterCache; - private transient Map,Object> interfaceAdapterCache; + private transient Map, JavaMembers> classTable; + private transient Map> classAdapterCache; + private transient Map, Object> interfaceAdapterCache; private int generatedClassSerial; private Scriptable associatedScope; /** - * Search for ClassCache object in the given scope. - * The method first calls - * {@link ScriptableObject#getTopLevelScope(Scriptable scope)} - * to get the top most scope and then tries to locate associated - * ClassCache object in the prototype chain of the top scope. + * Search for ClassCache object in the given scope. The method first calls {@link + * ScriptableObject#getTopLevelScope(Scriptable scope)} to get the top most scope and then tries + * to locate associated ClassCache object in the prototype chain of the top scope. * * @param scope scope to search for ClassCache object. - * @return previously associated ClassCache object or a new instance of - * ClassCache if no ClassCache object was found. - * + * @return previously associated ClassCache object or a new instance of ClassCache if no + * ClassCache object was found. * @see #associate(ScriptableObject topScope) */ - public static ClassCache get(Scriptable scope) - { - ClassCache cache = (ClassCache) - ScriptableObject.getTopScopeValue(scope, AKEY); + public static ClassCache get(Scriptable scope) { + ClassCache cache = (ClassCache) ScriptableObject.getTopScopeValue(scope, AKEY); if (cache == null) { - throw new RuntimeException("Can't find top level scope for " + - "ClassCache.get"); + throw new RuntimeException("Can't find top level scope for " + "ClassCache.get"); } return cache; } /** - * Associate ClassCache object with the given top-level scope. - * The ClassCache object can only be associated with the given scope once. + * Associate ClassCache object with the given top-level scope. The ClassCache object can only be + * associated with the given scope once. * * @param topScope scope to associate this ClassCache object with. - * @return true if no previous ClassCache objects were embedded into - * the scope and this ClassCache were successfully associated - * or false otherwise. - * + * @return true if no previous ClassCache objects were embedded into the scope and this + * ClassCache were successfully associated or false otherwise. * @see #get(Scriptable scope) */ - public boolean associate(ScriptableObject topScope) - { + public boolean associate(ScriptableObject topScope) { if (topScope.getParentScope() != null) { // Can only associate cache with top level scope throw new IllegalArgumentException(); @@ -77,118 +66,92 @@ public boolean associate(ScriptableObject topScope) return false; } - /** - * Empty caches of generated Java classes and Java reflection information. - */ - public synchronized void clearCaches() - { + /** Empty caches of generated Java classes and Java reflection information. */ + public synchronized void clearCaches() { classTable = null; classAdapterCache = null; interfaceAdapterCache = null; } - /** - * Check if generated Java classes and Java reflection information - * is cached. - */ - public final boolean isCachingEnabled() - { + /** Check if generated Java classes and Java reflection information is cached. */ + public final boolean isCachingEnabled() { return cachingIsEnabled; } - /** + /** * Set whether to cache some values. - *

- * By default, the engine will cache the results of - * Class.getMethods() and similar calls. - * This can speed execution dramatically, but increases the memory - * footprint. Also, with caching enabled, references may be held to - * objects past the lifetime of any real usage. - *

- * If caching is enabled and this method is called with a - * false argument, the caches will be emptied. - *

- * Caching is enabled by default. * - * @param enabled if true, caching is enabled + *

By default, the engine will cache the results of Class.getMethods() and + * similar calls. This can speed execution dramatically, but increases the memory footprint. + * Also, with caching enabled, references may be held to objects past the lifetime of any real + * usage. + * + *

If caching is enabled and this method is called with a false argument, the + * caches will be emptied. + * + *

Caching is enabled by default. * + * @param enabled if true, caching is enabled * @see #clearCaches() */ - public synchronized void setCachingEnabled(boolean enabled) - { - if (enabled == cachingIsEnabled) - return; - if (!enabled) - clearCaches(); + public synchronized void setCachingEnabled(boolean enabled) { + if (enabled == cachingIsEnabled) return; + if (!enabled) clearCaches(); cachingIsEnabled = enabled; } - /** - * @return a map from classes to associated JavaMembers objects - */ - Map,JavaMembers> getClassCacheMap() { + /** @return a map from classes to associated JavaMembers objects */ + Map, JavaMembers> getClassCacheMap() { if (classTable == null) { // Use 1 as concurrency level here and for other concurrent hash maps // as we don't expect high levels of sustained concurrent writes. - classTable = new ConcurrentHashMap,JavaMembers>(16, 0.75f, 1); + classTable = new ConcurrentHashMap, JavaMembers>(16, 0.75f, 1); } return classTable; } - Map> getInterfaceAdapterCacheMap() - { + Map> getInterfaceAdapterCacheMap() { if (classAdapterCache == null) { - classAdapterCache = new ConcurrentHashMap>(16, 0.75f, 1); + classAdapterCache = + new ConcurrentHashMap>(16, 0.75f, 1); } return classAdapterCache; } /** - * @deprecated - * The method always returns false. + * @deprecated The method always returns false. * @see #setInvokerOptimizationEnabled(boolean enabled) */ @Deprecated - public boolean isInvokerOptimizationEnabled() - { + public boolean isInvokerOptimizationEnabled() { return false; } /** - * @deprecated - * The method does nothing. - * Invoker optimization is no longer used by Rhino. - * On modern JDK like 1.4 or 1.5 the disadvantages of the optimization - * like increased memory usage or longer initialization time overweight - * small speed increase that can be gained using generated proxy class - * to replace reflection. + * @deprecated The method does nothing. Invoker optimization is no longer used by Rhino. On + * modern JDK like 1.4 or 1.5 the disadvantages of the optimization like increased memory + * usage or longer initialization time overweight small speed increase that can be gained + * using generated proxy class to replace reflection. */ @Deprecated - public synchronized void setInvokerOptimizationEnabled(boolean enabled) - { - } + public synchronized void setInvokerOptimizationEnabled(boolean enabled) {} /** - * Internal engine method to return serial number for generated classes - * to ensure name uniqueness. + * Internal engine method to return serial number for generated classes to ensure name + * uniqueness. */ - public final synchronized int newClassSerialNumber() - { + public final synchronized int newClassSerialNumber() { return ++generatedClassSerial; } - Object getInterfaceAdapter(Class cl) - { - return interfaceAdapterCache == null - ? null - : interfaceAdapterCache.get(cl); + Object getInterfaceAdapter(Class cl) { + return interfaceAdapterCache == null ? null : interfaceAdapterCache.get(cl); } - synchronized void cacheInterfaceAdapter(Class cl, Object iadapter) - { + synchronized void cacheInterfaceAdapter(Class cl, Object iadapter) { if (cachingIsEnabled) { if (interfaceAdapterCache == null) { - interfaceAdapterCache = new ConcurrentHashMap,Object>(16, 0.75f, 1); + interfaceAdapterCache = new ConcurrentHashMap, Object>(16, 0.75f, 1); } interfaceAdapterCache.put(cl, iadapter); } diff --git a/src/org/mozilla/javascript/JavaMembers.java b/src/org/mozilla/javascript/JavaMembers.java index 48e08472f4..e9e564e71f 100644 --- a/src/org/mozilla/javascript/JavaMembers.java +++ b/src/org/mozilla/javascript/JavaMembers.java @@ -22,42 +22,35 @@ import java.util.Map; /** - * * @author Mike Shaver * @author Norris Boyd * @see NativeJavaObject * @see NativeJavaClass */ -class JavaMembers -{ - JavaMembers(Scriptable scope, Class cl) - { +class JavaMembers { + JavaMembers(Scriptable scope, Class cl) { this(scope, cl, false); } - JavaMembers(Scriptable scope, Class cl, boolean includeProtected) - { + JavaMembers(Scriptable scope, Class cl, boolean includeProtected) { try { Context cx = ContextFactory.getGlobal().enterContext(); ClassShutter shutter = cx.getClassShutter(); if (shutter != null && !shutter.visibleToScripts(cl.getName())) { - throw Context.reportRuntimeErrorById("msg.access.prohibited", - cl.getName()); + throw Context.reportRuntimeErrorById("msg.access.prohibited", cl.getName()); } - this.members = new HashMap(); - this.staticMembers = new HashMap(); + this.members = new HashMap(); + this.staticMembers = new HashMap(); this.cl = cl; - boolean includePrivate = cx.hasFeature( - Context.FEATURE_ENHANCED_JAVA_ACCESS); + boolean includePrivate = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS); reflect(scope, includeProtected, includePrivate); } finally { Context.exit(); } } - boolean has(String name, boolean isStatic) - { - Map ht = isStatic ? staticMembers : members; + boolean has(String name, boolean isStatic) { + Map ht = isStatic ? staticMembers : members; Object obj = ht.get(name); if (obj != null) { return true; @@ -65,20 +58,19 @@ boolean has(String name, boolean isStatic) return findExplicitFunction(name, isStatic) != null; } - Object get(Scriptable scope, String name, Object javaObject, - boolean isStatic) - { - Map ht = isStatic ? staticMembers : members; + Object get(Scriptable scope, String name, Object javaObject, boolean isStatic) { + Map ht = isStatic ? staticMembers : members; Object member = ht.get(name); if (!isStatic && member == null) { // Try to get static member from instance (LC3) member = staticMembers.get(name); } if (member == null) { - member = this.getExplicitFunction(scope, name, - javaObject, isStatic); - if (member == null) - return Scriptable.NOT_FOUND; + member = + this.getExplicitFunction( + scope, name, + javaObject, isStatic); + if (member == null) return Scriptable.NOT_FOUND; } if (member instanceof Scriptable) { return member; @@ -89,8 +81,7 @@ Object get(Scriptable scope, String name, Object javaObject, try { if (member instanceof BeanProperty) { BeanProperty bp = (BeanProperty) member; - if (bp.getter == null) - return Scriptable.NOT_FOUND; + if (bp.getter == null) return Scriptable.NOT_FOUND; rval = bp.getter.invoke(javaObject, Context.emptyArgs); type = bp.getter.method().getReturnType(); } else { @@ -106,17 +97,14 @@ Object get(Scriptable scope, String name, Object javaObject, return cx.getWrapFactory().wrap(cx, scope, rval, type); } - void put(Scriptable scope, String name, Object javaObject, - Object value, boolean isStatic) - { - Map ht = isStatic ? staticMembers : members; + void put(Scriptable scope, String name, Object javaObject, Object value, boolean isStatic) { + Map ht = isStatic ? staticMembers : members; Object member = ht.get(name); if (!isStatic && member == null) { // Try to get static member from instance (LC3) member = staticMembers.get(name); } - if (member == null) - throw reportMemberNotFound(name); + if (member == null) throw reportMemberNotFound(name); if (member instanceof FieldAndMethods) { FieldAndMethods fam = (FieldAndMethods) ht.get(name); member = fam.field; @@ -124,7 +112,7 @@ void put(Scriptable scope, String name, Object javaObject, // Is this a bean property "set"? if (member instanceof BeanProperty) { - BeanProperty bp = (BeanProperty)member; + BeanProperty bp = (BeanProperty) member; if (bp.setter == null) { throw reportMemberNotFound(name); } @@ -133,26 +121,27 @@ void put(Scriptable scope, String name, Object javaObject, // setter to use: if (bp.setters == null || value == null) { Class setType = bp.setter.argTypes[0]; - Object[] args = { Context.jsToJava(value, setType) }; + Object[] args = {Context.jsToJava(value, setType)}; try { bp.setter.invoke(javaObject, args); } catch (Exception ex) { - throw Context.throwAsScriptRuntimeEx(ex); + throw Context.throwAsScriptRuntimeEx(ex); } } else { - Object[] args = { value }; - bp.setters.call(Context.getContext(), - ScriptableObject.getTopLevelScope(scope), - scope, args); + Object[] args = {value}; + bp.setters.call( + Context.getContext(), + ScriptableObject.getTopLevelScope(scope), + scope, + args); } - } - else { + } else { if (!(member instanceof Field)) { - String str = (member == null) ? "msg.java.internal.private" - : "msg.java.method.assign"; + String str = + (member == null) ? "msg.java.internal.private" : "msg.java.method.assign"; throw Context.reportRuntimeErrorById(str, name); } - Field field = (Field)member; + Field field = (Field) member; Object javaValue = Context.jsToJava(value, field.getType()); try { field.set(javaObject, javaValue); @@ -164,21 +153,20 @@ void put(Scriptable scope, String name, Object javaObject, throw Context.throwAsScriptRuntimeEx(accessEx); } catch (IllegalArgumentException argEx) { throw Context.reportRuntimeErrorById( - "msg.java.internal.field.type", - value.getClass().getName(), field, - javaObject.getClass().getName()); + "msg.java.internal.field.type", + value.getClass().getName(), + field, + javaObject.getClass().getName()); } } } - Object[] getIds(boolean isStatic) - { - Map map = isStatic ? staticMembers : members; + Object[] getIds(boolean isStatic) { + Map map = isStatic ? staticMembers : members; return map.keySet().toArray(new Object[map.size()]); } - static String javaSignature(Class type) - { + static String javaSignature(Class type) { if (!type.isArray()) { return type.getName(); } @@ -202,10 +190,11 @@ static String javaSignature(Class type) return sb.toString(); } - static String liveConnectSignature(Class[] argTypes) - { + static String liveConnectSignature(Class[] argTypes) { int N = argTypes.length; - if (N == 0) { return "()"; } + if (N == 0) { + return "()"; + } StringBuilder sb = new StringBuilder(); sb.append('('); for (int i = 0; i != N; ++i) { @@ -218,12 +207,13 @@ static String liveConnectSignature(Class[] argTypes) return sb.toString(); } - private MemberBox findExplicitFunction(String name, boolean isStatic) - { + private MemberBox findExplicitFunction(String name, boolean isStatic) { int sigStart = name.indexOf('('); - if (sigStart < 0) { return null; } + if (sigStart < 0) { + return null; + } - Map ht = isStatic ? staticMembers : members; + Map ht = isStatic ? staticMembers : members; MemberBox[] methodsOrCtors = null; boolean isCtor = (isStatic && sigStart == 0); @@ -232,14 +222,14 @@ private MemberBox findExplicitFunction(String name, boolean isStatic) methodsOrCtors = ctors.methods; } else { // Explicit request for an overloaded method - String trueName = name.substring(0,sigStart); + String trueName = name.substring(0, sigStart); Object obj = ht.get(trueName); if (!isStatic && obj == null) { // Try to get static member from instance (LC3) obj = staticMembers.get(trueName); } if (obj instanceof NativeJavaMethod) { - NativeJavaMethod njm = (NativeJavaMethod)obj; + NativeJavaMethod njm = (NativeJavaMethod) obj; methodsOrCtors = njm.methods; } } @@ -249,8 +239,7 @@ private MemberBox findExplicitFunction(String name, boolean isStatic) Class[] type = methodsOrCtor.argTypes; String sig = liveConnectSignature(type); if (sigStart + sig.length() == name.length() - && name.regionMatches(sigStart, sig, 0, sig.length())) - { + && name.regionMatches(sigStart, sig, 0, sig.length())) { return methodsOrCtor; } } @@ -259,20 +248,17 @@ private MemberBox findExplicitFunction(String name, boolean isStatic) return null; } - private Object getExplicitFunction(Scriptable scope, String name, - Object javaObject, boolean isStatic) - { - Map ht = isStatic ? staticMembers : members; + private Object getExplicitFunction( + Scriptable scope, String name, Object javaObject, boolean isStatic) { + Map ht = isStatic ? staticMembers : members; Object member = null; MemberBox methodOrCtor = findExplicitFunction(name, isStatic); if (methodOrCtor != null) { - Scriptable prototype = - ScriptableObject.getFunctionPrototype(scope); + Scriptable prototype = ScriptableObject.getFunctionPrototype(scope); if (methodOrCtor.isCtor()) { - NativeJavaConstructor fun = - new NativeJavaConstructor(methodOrCtor); + NativeJavaConstructor fun = new NativeJavaConstructor(methodOrCtor); fun.setPrototype(prototype); member = fun; ht.put(name, fun); @@ -280,10 +266,9 @@ private Object getExplicitFunction(Scriptable scope, String name, String trueName = methodOrCtor.getName(); member = ht.get(trueName); - if (member instanceof NativeJavaMethod && - ((NativeJavaMethod)member).methods.length > 1 ) { - NativeJavaMethod fun = - new NativeJavaMethod(methodOrCtor, name); + if (member instanceof NativeJavaMethod + && ((NativeJavaMethod) member).methods.length > 1) { + NativeJavaMethod fun = new NativeJavaMethod(methodOrCtor, name); fun.setPrototype(prototype); ht.put(name, fun); member = fun; @@ -295,25 +280,23 @@ private Object getExplicitFunction(Scriptable scope, String name, } /** - * Retrieves mapping of methods to accessible methods for a class. - * In case the class is not public, retrieves methods with same - * signature as its public methods from public superclasses and - * interfaces (if they exist). Basically upcasts every method to the - * nearest accessible method. + * Retrieves mapping of methods to accessible methods for a class. In case the class is not + * public, retrieves methods with same signature as its public methods from public superclasses + * and interfaces (if they exist). Basically upcasts every method to the nearest accessible + * method. */ - private static Method[] discoverAccessibleMethods(Class clazz, - boolean includeProtected, - boolean includePrivate) - { - Map map = new HashMap(); + private static Method[] discoverAccessibleMethods( + Class clazz, boolean includeProtected, boolean includePrivate) { + Map map = new HashMap(); discoverAccessibleMethods(clazz, map, includeProtected, includePrivate); return map.values().toArray(new Method[map.size()]); } - private static void discoverAccessibleMethods(Class clazz, - Map map, boolean includeProtected, - boolean includePrivate) - { + private static void discoverAccessibleMethods( + Class clazz, + Map map, + boolean includeProtected, + boolean includePrivate) { if (isPublic(clazz.getModifiers()) || includePrivate) { try { if (includeProtected || includePrivate) { @@ -323,9 +306,7 @@ private static void discoverAccessibleMethods(Class clazz, for (Method method : methods) { int mods = method.getModifiers(); - if (isPublic(mods) - || isProtected(mods) - || includePrivate) { + if (isPublic(mods) || isProtected(mods) || includePrivate) { MethodSignature sig = new MethodSignature(method); if (!map.containsKey(sig)) { if (includePrivate && !method.isAccessible()) @@ -336,8 +317,8 @@ private static void discoverAccessibleMethods(Class clazz, } Class[] interfaces = clazz.getInterfaces(); for (Class intface : interfaces) { - discoverAccessibleMethods(intface, map, includeProtected, - includePrivate); + discoverAccessibleMethods( + intface, map, includeProtected, includePrivate); } clazz = clazz.getSuperclass(); } catch (SecurityException e) { @@ -347,11 +328,10 @@ private static void discoverAccessibleMethods(Class clazz, Method[] methods = clazz.getMethods(); for (Method method : methods) { MethodSignature sig = new MethodSignature(method); - if (!map.containsKey(sig)) - map.put(sig, method); + if (!map.containsKey(sig)) map.put(sig, method); } break; // getMethods gets superclass methods, no - // need to loop any more + // need to loop any more } } } else { @@ -359,16 +339,16 @@ private static void discoverAccessibleMethods(Class clazz, for (Method method : methods) { MethodSignature sig = new MethodSignature(method); // Array may contain methods with same signature but different return value! - if (!map.containsKey(sig)) - map.put(sig, method); + if (!map.containsKey(sig)) map.put(sig, method); } } return; } catch (SecurityException e) { Context.reportWarning( - "Could not discover accessible methods of class " + - clazz.getName() + " due to lack of privileges, " + - "attemping superclasses/interfaces."); + "Could not discover accessible methods of class " + + clazz.getName() + + " due to lack of privileges, " + + "attemping superclasses/interfaces."); // Fall through and attempt to discover superclass/interface // methods } @@ -376,64 +356,52 @@ private static void discoverAccessibleMethods(Class clazz, Class[] interfaces = clazz.getInterfaces(); for (Class intface : interfaces) { - discoverAccessibleMethods(intface, map, includeProtected, - includePrivate); + discoverAccessibleMethods(intface, map, includeProtected, includePrivate); } Class superclass = clazz.getSuperclass(); if (superclass != null) { - discoverAccessibleMethods(superclass, map, includeProtected, - includePrivate); + discoverAccessibleMethods(superclass, map, includeProtected, includePrivate); } } - private static final class MethodSignature - { + private static final class MethodSignature { private final String name; private final Class[] args; - private MethodSignature(String name, Class[] args) - { + private MethodSignature(String name, Class[] args) { this.name = name; this.args = args; } - MethodSignature(Method method) - { + MethodSignature(Method method) { this(method.getName(), method.getParameterTypes()); } @Override - public boolean equals(Object o) - { - if(o instanceof MethodSignature) - { - MethodSignature ms = (MethodSignature)o; + public boolean equals(Object o) { + if (o instanceof MethodSignature) { + MethodSignature ms = (MethodSignature) o; return ms.name.equals(name) && Arrays.equals(args, ms.args); } return false; } @Override - public int hashCode() - { + public int hashCode() { return name.hashCode() ^ args.length; } } - private void reflect(Scriptable scope, - boolean includeProtected, - boolean includePrivate) - { + private void reflect(Scriptable scope, boolean includeProtected, boolean includePrivate) { // We reflect methods first, because we want overloaded field/method // names to be allocated to the NativeJavaMethod before the field // gets in the way. - Method[] methods = discoverAccessibleMethods(cl, includeProtected, - includePrivate); + Method[] methods = discoverAccessibleMethods(cl, includeProtected, includePrivate); for (Method method : methods) { int mods = method.getModifiers(); boolean isStatic = Modifier.isStatic(mods); - Map ht = isStatic ? staticMembers : members; + Map ht = isStatic ? staticMembers : members; String name = method.getName(); Object value = ht.get(name); if (value == null) { @@ -441,7 +409,7 @@ private void reflect(Scriptable scope, } else { ObjArray overloadedMethods; if (value instanceof ObjArray) { - overloadedMethods = (ObjArray)value; + overloadedMethods = (ObjArray) value; } else { if (!(value instanceof Method)) Kit.codeBug(); // value should be instance of Method as at this stage @@ -458,20 +426,20 @@ private void reflect(Scriptable scope, // first in staticMembers and then in members for (int tableCursor = 0; tableCursor != 2; ++tableCursor) { boolean isStatic = (tableCursor == 0); - Map ht = isStatic ? staticMembers : members; - for (Map.Entry entry: ht.entrySet()) { + Map ht = isStatic ? staticMembers : members; + for (Map.Entry entry : ht.entrySet()) { MemberBox[] methodBoxes; Object value = entry.getValue(); if (value instanceof Method) { methodBoxes = new MemberBox[1]; - methodBoxes[0] = new MemberBox((Method)value); + methodBoxes[0] = new MemberBox((Method) value); } else { - ObjArray overloadedMethods = (ObjArray)value; + ObjArray overloadedMethods = (ObjArray) value; int N = overloadedMethods.size(); if (N < 2) Kit.codeBug(); methodBoxes = new MemberBox[N]; for (int i = 0; i != N; ++i) { - Method method = (Method)overloadedMethods.get(i); + Method method = (Method) overloadedMethods.get(i); methodBoxes[i] = new MemberBox(method); } } @@ -490,18 +458,17 @@ private void reflect(Scriptable scope, int mods = field.getModifiers(); try { boolean isStatic = Modifier.isStatic(mods); - Map ht = isStatic ? staticMembers : members; + Map ht = isStatic ? staticMembers : members; Object member = ht.get(name); if (member == null) { ht.put(name, field); } else if (member instanceof NativeJavaMethod) { NativeJavaMethod method = (NativeJavaMethod) member; - FieldAndMethods fam - = new FieldAndMethods(scope, method.methods, field); - Map fmht = isStatic ? staticFieldAndMethods - : fieldAndMethods; + FieldAndMethods fam = new FieldAndMethods(scope, method.methods, field); + Map fmht = + isStatic ? staticFieldAndMethods : fieldAndMethods; if (fmht == null) { - fmht = new HashMap(); + fmht = new HashMap(); if (isStatic) { staticFieldAndMethods = fmht; } else { @@ -518,9 +485,7 @@ private void reflect(Scriptable scope, // reflected. // For now, the first field found wins, unless another field // explicitly shadows it. - if (oldField.getDeclaringClass(). - isAssignableFrom(field.getDeclaringClass())) - { + if (oldField.getDeclaringClass().isAssignableFrom(field.getDeclaringClass())) { ht.put(name, field); } } else { @@ -529,9 +494,12 @@ private void reflect(Scriptable scope, } } catch (SecurityException e) { // skip this field - Context.reportWarning("Could not access field " - + name + " of class " + cl.getName() + - " due to lack of privileges."); + Context.reportWarning( + "Could not access field " + + name + + " of class " + + cl.getName() + + " due to lack of privileges."); } } @@ -539,23 +507,20 @@ private void reflect(Scriptable scope, // static members and then for instance members for (int tableCursor = 0; tableCursor != 2; ++tableCursor) { boolean isStatic = (tableCursor == 0); - Map ht = isStatic ? staticMembers : members; + Map ht = isStatic ? staticMembers : members; - Map toAdd = new HashMap(); + Map toAdd = new HashMap(); // Now, For each member, make "bean" properties. - for (String name: ht.keySet()) { + for (String name : ht.keySet()) { // Is this a getter? boolean memberIsGetMethod = name.startsWith("get"); boolean memberIsSetMethod = name.startsWith("set"); boolean memberIsIsMethod = name.startsWith("is"); - if (memberIsGetMethod || memberIsIsMethod - || memberIsSetMethod) { + if (memberIsGetMethod || memberIsIsMethod || memberIsSetMethod) { // Double check name component. - String nameComponent - = name.substring(memberIsIsMethod ? 2 : 3); - if (nameComponent.length() == 0) - continue; + String nameComponent = name.substring(memberIsIsMethod ? 2 : 3); + if (nameComponent.length() == 0) continue; // Make the bean property name. String beanPropertyName = nameComponent; @@ -566,23 +531,22 @@ private void reflect(Scriptable scope, } else { char ch1 = nameComponent.charAt(1); if (!Character.isUpperCase(ch1)) { - beanPropertyName = Character.toLowerCase(ch0) - +nameComponent.substring(1); + beanPropertyName = + Character.toLowerCase(ch0) + nameComponent.substring(1); } } } // If we already have a member by this name, don't do this // property. - if (toAdd.containsKey(beanPropertyName)) - continue; + if (toAdd.containsKey(beanPropertyName)) continue; Object v = ht.get(beanPropertyName); if (v != null) { // A private field shouldn't mask a public getter/setter - if (!includePrivate || !(v instanceof Member) || - !Modifier.isPrivate(((Member)v).getModifiers())) + if (!includePrivate + || !(v instanceof Member) + || !Modifier.isPrivate(((Member) v).getModifiers())) { - { continue; } } @@ -605,17 +569,15 @@ private void reflect(Scriptable scope, // Is this value a method? Object member = ht.get(setterName); if (member instanceof NativeJavaMethod) { - NativeJavaMethod njmSet = (NativeJavaMethod)member; + NativeJavaMethod njmSet = (NativeJavaMethod) member; if (getter != null) { // We have a getter. Now, do we have a matching // setter? Class type = getter.method().getReturnType(); - setter = extractSetMethod(type, njmSet.methods, - isStatic); + setter = extractSetMethod(type, njmSet.methods, isStatic); } else { // No getter, find any set method - setter = extractSetMethod(njmSet.methods, - isStatic); + setter = extractSetMethod(njmSet.methods, isStatic); } if (njmSet.methods.length > 1) { setters = njmSet; @@ -623,8 +585,7 @@ private void reflect(Scriptable scope, } } // Make the property. - BeanProperty bp = new BeanProperty(getter, setter, - setters); + BeanProperty bp = new BeanProperty(getter, setter, setters); toAdd.put(beanPropertyName, bp); } } @@ -642,28 +603,28 @@ private void reflect(Scriptable scope, ctors = new NativeJavaMethod(ctorMembers, cl.getSimpleName()); } - private Constructor[] getAccessibleConstructors(boolean includePrivate) - { - // The JVM currently doesn't allow changing access on java.lang.Class - // constructors, so don't try - if (includePrivate && cl != ScriptRuntime.ClassClass) { - try { - Constructor[] cons = cl.getDeclaredConstructors(); - AccessibleObject.setAccessible(cons, true); - - return cons; - } catch (SecurityException e) { - // Fall through to !includePrivate case - Context.reportWarning("Could not access constructor " + - " of class " + cl.getName() + - " due to lack of privileges."); - } - } - return cl.getConstructors(); + private Constructor[] getAccessibleConstructors(boolean includePrivate) { + // The JVM currently doesn't allow changing access on java.lang.Class + // constructors, so don't try + if (includePrivate && cl != ScriptRuntime.ClassClass) { + try { + Constructor[] cons = cl.getDeclaredConstructors(); + AccessibleObject.setAccessible(cons, true); + + return cons; + } catch (SecurityException e) { + // Fall through to !includePrivate case + Context.reportWarning( + "Could not access constructor " + + " of class " + + cl.getName() + + " due to lack of privileges."); + } + } + return cl.getConstructors(); } - private Field[] getAccessibleFields(boolean includeProtected, - boolean includePrivate) { + private Field[] getAccessibleFields(boolean includeProtected, boolean includePrivate) { if (includePrivate || includeProtected) { try { List fieldsList = new ArrayList(); @@ -676,8 +637,7 @@ private Field[] getAccessibleFields(boolean includeProtected, for (Field field : declared) { int mod = field.getModifiers(); if (includePrivate || isPublic(mod) || isProtected(mod)) { - if (!field.isAccessible()) - field.setAccessible(true); + if (!field.isAccessible()) field.setAccessible(true); fieldsList.add(field); } } @@ -694,9 +654,8 @@ private Field[] getAccessibleFields(boolean includeProtected, return cl.getFields(); } - private static MemberBox findGetter(boolean isStatic, Map ht, String prefix, - String propertyName) - { + private static MemberBox findGetter( + boolean isStatic, Map ht, String prefix, String propertyName) { String getterName = prefix.concat(propertyName); if (ht.containsKey(getterName)) { // Check that the getter is a method. @@ -709,9 +668,7 @@ private static MemberBox findGetter(boolean isStatic, Map ht, Str return null; } - private static MemberBox extractGetMethod(MemberBox[] methods, - boolean isStatic) - { + private static MemberBox extractGetMethod(MemberBox[] methods, boolean isStatic) { // Inspect the list of all MemberBox for the only one having no // parameters for (MemberBox method : methods) { @@ -728,9 +685,8 @@ private static MemberBox extractGetMethod(MemberBox[] methods, return null; } - private static MemberBox extractSetMethod(Class type, MemberBox[] methods, - boolean isStatic) - { + private static MemberBox extractSetMethod( + Class type, MemberBox[] methods, boolean isStatic) { // // Note: it may be preferable to allow NativeJavaMethod.findFunction() // to find the appropriate setter; unfortunately, it requires an @@ -761,9 +717,7 @@ private static MemberBox extractSetMethod(Class type, MemberBox[] methods, return null; } - private static MemberBox extractSetMethod(MemberBox[] methods, - boolean isStatic) - { + private static MemberBox extractSetMethod(MemberBox[] methods, boolean isStatic) { for (MemberBox method : methods) { if (!isStatic || method.isStatic()) { @@ -777,32 +731,28 @@ private static MemberBox extractSetMethod(MemberBox[] methods, return null; } - Map getFieldAndMethodsObjects(Scriptable scope, - Object javaObject, boolean isStatic) - { - Map ht = isStatic ? staticFieldAndMethods : fieldAndMethods; - if (ht == null) - return null; + Map getFieldAndMethodsObjects( + Scriptable scope, Object javaObject, boolean isStatic) { + Map ht = isStatic ? staticFieldAndMethods : fieldAndMethods; + if (ht == null) return null; int len = ht.size(); - Map result = new HashMap(len); - for (FieldAndMethods fam: ht.values()) { - FieldAndMethods famNew = new FieldAndMethods(scope, fam.methods, - fam.field); + Map result = new HashMap(len); + for (FieldAndMethods fam : ht.values()) { + FieldAndMethods famNew = new FieldAndMethods(scope, fam.methods, fam.field); famNew.javaObject = javaObject; result.put(fam.field.getName(), famNew); } return result; } - static JavaMembers lookupClass(Scriptable scope, Class dynamicType, - Class staticType, boolean includeProtected) - { + static JavaMembers lookupClass( + Scriptable scope, Class dynamicType, Class staticType, boolean includeProtected) { JavaMembers members; ClassCache cache = ClassCache.get(scope); - Map,JavaMembers> ct = cache.getClassCacheMap(); + Map, JavaMembers> ct = cache.getClassCacheMap(); Class cl = dynamicType; - for (;;) { + for (; ; ) { members = ct.get(cl); if (members != null) { if (cl != dynamicType) { @@ -813,8 +763,7 @@ static JavaMembers lookupClass(Scriptable scope, Class dynamicType, return members; } try { - members = new JavaMembers(cache.getAssociatedScope(), cl, - includeProtected); + members = new JavaMembers(cache.getAssociatedScope(), cl, includeProtected); break; } catch (SecurityException e) { // Reflection may fail for objects that are in a restricted @@ -850,24 +799,21 @@ static JavaMembers lookupClass(Scriptable scope, Class dynamicType, return members; } - RuntimeException reportMemberNotFound(String memberName) - { + RuntimeException reportMemberNotFound(String memberName) { return Context.reportRuntimeErrorById( - "msg.java.member.not.found", cl.getName(), memberName); + "msg.java.member.not.found", cl.getName(), memberName); } private Class cl; - private Map members; - private Map fieldAndMethods; - private Map staticMembers; - private Map staticFieldAndMethods; + private Map members; + private Map fieldAndMethods; + private Map staticMembers; + private Map staticFieldAndMethods; NativeJavaMethod ctors; // we use NativeJavaMethod for ctor overload resolution } -class BeanProperty -{ - BeanProperty(MemberBox getter, MemberBox setter, NativeJavaMethod setters) - { +class BeanProperty { + BeanProperty(MemberBox getter, MemberBox setter, NativeJavaMethod setters) { this.getter = getter; this.setter = setter; this.setters = setters; @@ -878,12 +824,10 @@ class BeanProperty NativeJavaMethod setters; } -class FieldAndMethods extends NativeJavaMethod -{ +class FieldAndMethods extends NativeJavaMethod { private static final long serialVersionUID = -9222428244284796755L; - FieldAndMethods(Scriptable scope, MemberBox[] methods, Field field) - { + FieldAndMethods(Scriptable scope, MemberBox[] methods, Field field) { super(methods); this.field = field; setParentScope(scope); @@ -891,20 +835,17 @@ class FieldAndMethods extends NativeJavaMethod } @Override - public Object getDefaultValue(Class hint) - { - if (hint == ScriptRuntime.FunctionClass) - return this; + public Object getDefaultValue(Class hint) { + if (hint == ScriptRuntime.FunctionClass) return this; Object rval; Class type; try { rval = field.get(javaObject); type = field.getType(); } catch (IllegalAccessException accEx) { - throw Context.reportRuntimeErrorById( - "msg.java.internal.private", field.getName()); + throw Context.reportRuntimeErrorById("msg.java.internal.private", field.getName()); } - Context cx = Context.getContext(); + Context cx = Context.getContext(); rval = cx.getWrapFactory().wrap(cx, this, rval, type); if (rval instanceof Scriptable) { rval = ((Scriptable) rval).getDefaultValue(hint); diff --git a/src/org/mozilla/javascript/NativeJavaMethod.java b/src/org/mozilla/javascript/NativeJavaMethod.java index a43577233b..0a4bf619da 100644 --- a/src/org/mozilla/javascript/NativeJavaMethod.java +++ b/src/org/mozilla/javascript/NativeJavaMethod.java @@ -12,50 +12,43 @@ import java.util.concurrent.CopyOnWriteArrayList; /** - * This class reflects Java methods into the JavaScript environment and - * handles overloading of methods. + * This class reflects Java methods into the JavaScript environment and handles overloading of + * methods. * * @author Mike Shaver * @see NativeJavaArray * @see NativeJavaPackage * @see NativeJavaClass */ +public class NativeJavaMethod extends BaseFunction { -public class NativeJavaMethod extends BaseFunction -{ private static final long serialVersionUID = -3440381785576412928L; - NativeJavaMethod(MemberBox[] methods) - { + NativeJavaMethod(MemberBox[] methods) { this.functionName = methods[0].getName(); this.methods = methods; } - NativeJavaMethod(MemberBox[] methods, String name) - { + NativeJavaMethod(MemberBox[] methods, String name) { this.functionName = name; this.methods = methods; } - NativeJavaMethod(MemberBox method, String name) - { + NativeJavaMethod(MemberBox method, String name) { this.functionName = name; - this.methods = new MemberBox[] { method }; + this.methods = new MemberBox[] {method}; } - public NativeJavaMethod(Method method, String name) - { + public NativeJavaMethod(Method method, String name) { this(new MemberBox(method), name); } @Override - public String getFunctionName() - { + public String getFunctionName() { return functionName; } - static String scriptSignature(Object[] values) - { + static String scriptSignature(Object[] values) { StringBuilder sig = new StringBuilder(); for (int i = 0; i != values.length; ++i) { Object value = values[i]; @@ -73,7 +66,7 @@ static String scriptSignature(Object[] values) if (value instanceof Undefined) { s = "undefined"; } else if (value instanceof Wrapper) { - Object wrapped = ((Wrapper)value).unwrap(); + Object wrapped = ((Wrapper) value).unwrap(); s = wrapped.getClass().getName(); } else if (value instanceof Function) { s = "function"; @@ -93,8 +86,7 @@ static String scriptSignature(Object[] values) } @Override - String decompile(int indent, int flags) - { + String decompile(int indent, int flags) { StringBuilder sb = new StringBuilder(); boolean justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); if (!justbody) { @@ -109,8 +101,7 @@ String decompile(int indent, int flags) } @Override - public String toString() - { + public String toString() { StringBuilder sb = new StringBuilder(); for (int i = 0, N = methods.length; i != N; ++i) { // Check member type, we also use this for overloaded constructors @@ -129,9 +120,7 @@ public String toString() } @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, - Object[] args) - { + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { // Find a method that matches the types given. if (methods.length == 0) { throw new RuntimeException("No methods defined for call"); @@ -140,8 +129,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, int index = findCachedFunction(cx, args); if (index < 0) { Class c = methods[0].method().getDeclaringClass(); - String sig = c.getName() + '.' + getFunctionName() + '(' + - scriptSignature(args) + ')'; + String sig = c.getName() + '.' + getFunctionName() + '(' + scriptSignature(args) + ')'; throw Context.reportRuntimeErrorById("msg.java.no_such_method", sig); } @@ -151,7 +139,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, if (meth.vararg) { // marshall the explicit parameters Object[] newArgs = new Object[argTypes.length]; - for (int i = 0; i < argTypes.length-1; i++) { + for (int i = 0; i < argTypes.length - 1; i++) { newArgs[i] = Context.jsToJava(args[i], argTypes[i]); } @@ -159,29 +147,24 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, // Handle special situation where a single variable parameter // is given and it is a Java or ECMA array or is null. - if (args.length == argTypes.length && - (args[args.length-1] == null || - args[args.length-1] instanceof NativeArray || - args[args.length-1] instanceof NativeJavaArray)) - { + if (args.length == argTypes.length + && (args[args.length - 1] == null + || args[args.length - 1] instanceof NativeArray + || args[args.length - 1] instanceof NativeJavaArray)) { // convert the ECMA array into a native array - varArgs = Context.jsToJava(args[args.length-1], - argTypes[argTypes.length - 1]); + varArgs = Context.jsToJava(args[args.length - 1], argTypes[argTypes.length - 1]); } else { // marshall the variable parameters - Class componentType = argTypes[argTypes.length - 1]. - getComponentType(); - varArgs = Array.newInstance(componentType, - args.length - argTypes.length + 1); + Class componentType = argTypes[argTypes.length - 1].getComponentType(); + varArgs = Array.newInstance(componentType, args.length - argTypes.length + 1); for (int i = 0; i < Array.getLength(varArgs); i++) { - Object value = Context.jsToJava(args[argTypes.length-1 + i], - componentType); + Object value = Context.jsToJava(args[argTypes.length - 1 + i], componentType); Array.set(varArgs, i, value); } } // add varargs - newArgs[argTypes.length-1] = varArgs; + newArgs[argTypes.length - 1] = varArgs; // replace the original args with the new one args = newArgs; } else { @@ -200,18 +183,20 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, } Object javaObject; if (meth.isStatic()) { - javaObject = null; // don't need an object + javaObject = null; // don't need an object } else { Scriptable o = thisObj; Class c = meth.getDeclaringClass(); - for (;;) { + for (; ; ) { if (o == null) { throw Context.reportRuntimeErrorById( - "msg.nonjava.method", getFunctionName(), - ScriptRuntime.toString(thisObj), c.getName()); + "msg.nonjava.method", + getFunctionName(), + ScriptRuntime.toString(thisObj), + c.getName()); } if (o instanceof Wrapper) { - javaObject = ((Wrapper)o).unwrap(); + javaObject = ((Wrapper) o).unwrap(); if (c.isInstance(javaObject)) { break; } @@ -227,20 +212,24 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, Class staticType = meth.method().getReturnType(); if (debug) { - Class actualType = (retval == null) ? null - : retval.getClass(); - System.err.println(" ----- Returned " + retval + - " actual = " + actualType + - " expect = " + staticType); + Class actualType = (retval == null) ? null : retval.getClass(); + System.err.println( + " ----- Returned " + + retval + + " actual = " + + actualType + + " expect = " + + staticType); } - Object wrapped = cx.getWrapFactory().wrap(cx, scope, - retval, staticType); + Object wrapped = + cx.getWrapFactory() + .wrap( + cx, scope, + retval, staticType); if (debug) { - Class actualType = (wrapped == null) ? null - : wrapped.getClass(); - System.err.println(" ----- Wrapped as " + wrapped + - " class = " + actualType); + Class actualType = (wrapped == null) ? null : wrapped.getClass(); + System.err.println(" ----- Wrapped as " + wrapped + " class = " + actualType); } if (wrapped == null && staticType == Void.TYPE) { @@ -269,13 +258,10 @@ int findCachedFunction(Context cx, Object[] args) { } /** - * Find the index of the correct function to call given the set of methods - * or constructors and the arguments. - * If no function can be found to call, return -1. + * Find the index of the correct function to call given the set of methods or constructors and + * the arguments. If no function can be found to call, return -1. */ - static int findFunction(Context cx, - MemberBox[] methodsOrCtors, Object[] args) - { + static int findFunction(Context cx, MemberBox[] methodsOrCtors, Object[] args) { if (methodsOrCtors.length == 0) { return -1; } else if (methodsOrCtors.length == 1) { @@ -285,7 +271,7 @@ static int findFunction(Context cx, if (member.vararg) { alength--; - if ( alength > args.length) { + if (alength > args.length) { return -1; } } else { @@ -295,8 +281,7 @@ static int findFunction(Context cx, } for (int j = 0; j != alength; ++j) { if (!NativeJavaObject.canConvert(args[j], argTypes[j])) { - if (debug) printDebug("Rejecting (args can't convert) ", - member, args); + if (debug) printDebug("Rejecting (args can't convert) ", member, args); return -1; } } @@ -308,14 +293,14 @@ static int findFunction(Context cx, int[] extraBestFits = null; int extraBestFitsCount = 0; - search: + search: for (int i = 0; i < methodsOrCtors.length; i++) { MemberBox member = methodsOrCtors[i]; Class[] argTypes = member.argTypes; int alength = argTypes.length; if (member.vararg) { alength--; - if ( alength > args.length) { + if (alength > args.length) { continue search; } } else { @@ -325,8 +310,7 @@ static int findFunction(Context cx, } for (int j = 0; j < alength; j++) { if (!NativeJavaObject.canConvert(args[j], argTypes[j])) { - if (debug) printDebug("Rejecting (args can't convert) ", - member, args); + if (debug) printDebug("Rejecting (args can't convert) ", member, args); continue search; } } @@ -339,9 +323,9 @@ static int findFunction(Context cx, // until extraBestFitsCount to avoid extraBestFits allocation // in the most common case of no ambiguity int betterCount = 0; // number of times member was prefered over - // best fits - int worseCount = 0; // number of times best fits were prefered - // over member + // best fits + int worseCount = 0; // number of times best fits were prefered + // over member for (int j = -1; j != extraBestFitsCount; ++j) { int bestFitIndex; if (j == -1) { @@ -350,21 +334,21 @@ static int findFunction(Context cx, bestFitIndex = extraBestFits[j]; } MemberBox bestFit = methodsOrCtors[bestFitIndex]; - if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) && - bestFit.isPublic() != member.isPublic()) - { + if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) + && bestFit.isPublic() != member.isPublic()) { // When FEATURE_ENHANCED_JAVA_ACCESS gives us access // to non-public members, continue to prefer public // methods in overloading - if (!bestFit.isPublic()) - ++betterCount; - else - ++worseCount; + if (!bestFit.isPublic()) ++betterCount; + else ++worseCount; } else { - int preference = preferSignature(args, argTypes, - member.vararg, - bestFit.argTypes, - bestFit.vararg ); + int preference = + preferSignature( + args, + argTypes, + member.vararg, + bestFit.argTypes, + bestFit.vararg); if (preference == PREFERENCE_AMBIGUOUS) { break; } else if (preference == PREFERENCE_FIRST_ARG) { @@ -378,26 +362,23 @@ static int findFunction(Context cx, // static methods of the class hierarchy, even if // a derived class's parameters match exactly. // We want to call the derived class's method. - if (bestFit.isStatic() && - bestFit.getDeclaringClass().isAssignableFrom( - member.getDeclaringClass())) - { + if (bestFit.isStatic() + && bestFit.getDeclaringClass() + .isAssignableFrom(member.getDeclaringClass())) { // On some JVMs, Class.getMethods will return all // static methods of the class hierarchy, even if // a derived class's parameters match exactly. // We want to call the derived class's method. - if (debug) printDebug( - "Substituting (overridden static)", - member, args); + if (debug) + printDebug("Substituting (overridden static)", member, args); if (j == -1) { firstBestFit = i; } else { extraBestFits[j] = i; } } else { - if (debug) printDebug( - "Ignoring same signature member ", - member, args); + if (debug) + printDebug("Ignoring same signature member ", member, args); } continue search; } @@ -405,18 +386,15 @@ static int findFunction(Context cx, } if (betterCount == 1 + extraBestFitsCount) { // member was prefered over all best fits - if (debug) printDebug( - "New first applicable ", member, args); + if (debug) printDebug("New first applicable ", member, args); firstBestFit = i; extraBestFitsCount = 0; } else if (worseCount == 1 + extraBestFitsCount) { // all best fits were prefered over member, ignore it - if (debug) printDebug( - "Rejecting (all current bests better) ", member, args); + if (debug) printDebug("Rejecting (all current bests better) ", member, args); } else { // some ambiguity was present, add member to best fit set - if (debug) printDebug( - "Added to best fit set ", member, args); + if (debug) printDebug("Added to best fit set ", member, args); if (extraBestFits == null) { // Allocate maximum possible array extraBestFits = new int[methodsOrCtors.length - 1]; @@ -454,36 +432,34 @@ static int findFunction(Context cx, if (methodsOrCtors[0].isCtor()) { throw Context.reportRuntimeErrorById( - "msg.constructor.ambiguous", - memberName, scriptSignature(args), buf.toString()); + "msg.constructor.ambiguous", memberName, scriptSignature(args), buf.toString()); } throw Context.reportRuntimeErrorById( - "msg.method.ambiguous", memberClass, - memberName, scriptSignature(args), buf.toString()); + "msg.method.ambiguous", + memberClass, + memberName, + scriptSignature(args), + buf.toString()); } /** Types are equal */ - private static final int PREFERENCE_EQUAL = 0; - private static final int PREFERENCE_FIRST_ARG = 1; + private static final int PREFERENCE_EQUAL = 0; + + private static final int PREFERENCE_FIRST_ARG = 1; private static final int PREFERENCE_SECOND_ARG = 2; /** No clear "easy" conversion */ - private static final int PREFERENCE_AMBIGUOUS = 3; + private static final int PREFERENCE_AMBIGUOUS = 3; /** - * Determine which of two signatures is the closer fit. - * Returns one of PREFERENCE_EQUAL, PREFERENCE_FIRST_ARG, - * PREFERENCE_SECOND_ARG, or PREFERENCE_AMBIGUOUS. + * Determine which of two signatures is the closer fit. Returns one of PREFERENCE_EQUAL, + * PREFERENCE_FIRST_ARG, PREFERENCE_SECOND_ARG, or PREFERENCE_AMBIGUOUS. */ - private static int preferSignature(Object[] args, - Class[] sig1, - boolean vararg1, - Class[] sig2, - boolean vararg2 ) - { + private static int preferSignature( + Object[] args, Class[] sig1, boolean vararg1, Class[] sig2, boolean vararg2) { int totalPreference = 0; for (int j = 0; j < args.length; j++) { - Class type1 = vararg1 && j >= sig1.length ? sig1[sig1.length-1] : sig1[j]; - Class type2 = vararg2 && j >= sig2.length ? sig2[sig2.length-1] : sig2[j]; + Class type1 = vararg1 && j >= sig1.length ? sig1[sig1.length - 1] : sig1[j]; + Class type2 = vararg2 && j >= sig2.length ? sig2[sig2.length - 1] : sig2[j]; if (type1 == type2) { continue; } @@ -523,12 +499,9 @@ private static int preferSignature(Object[] args, return totalPreference; } - private static final boolean debug = false; - private static void printDebug(String msg, MemberBox member, - Object[] args) - { + private static void printDebug(String msg, MemberBox member, Object[] args) { if (debug) { StringBuilder sb = new StringBuilder(); sb.append(" ----- "); @@ -548,7 +521,8 @@ private static void printDebug(String msg, MemberBox member, MemberBox[] methods; private String functionName; - private transient final CopyOnWriteArrayList overloadCache = new CopyOnWriteArrayList<>(); + private final transient CopyOnWriteArrayList overloadCache = + new CopyOnWriteArrayList<>(); } class ResolvedOverload { @@ -560,8 +534,7 @@ class ResolvedOverload { types = new Class[args.length]; for (int i = 0, l = args.length; i < l; i++) { Object arg = args[i]; - if (arg instanceof Wrapper) - arg = ((Wrapper)arg).unwrap(); + if (arg instanceof Wrapper) arg = ((Wrapper) arg).unwrap(); types[i] = arg == null ? null : arg.getClass(); } } @@ -572,8 +545,7 @@ boolean matches(Object[] args) { } for (int i = 0, l = args.length; i < l; i++) { Object arg = args[i]; - if (arg instanceof Wrapper) - arg = ((Wrapper)arg).unwrap(); + if (arg instanceof Wrapper) arg = ((Wrapper) arg).unwrap(); if (arg == null) { if (types[i] != null) return false; } else if (arg.getClass() != types[i]) { diff --git a/src/org/mozilla/javascript/NativeJavaObject.java b/src/org/mozilla/javascript/NativeJavaObject.java index 27dd0d4c44..2174120de2 100644 --- a/src/org/mozilla/javascript/NativeJavaObject.java +++ b/src/org/mozilla/javascript/NativeJavaObject.java @@ -18,36 +18,32 @@ import java.util.Map; /** - * This class reflects non-Array Java objects into the JavaScript environment. It - * reflect fields directly, and uses NativeJavaMethod objects to reflect (possibly - * overloaded) methods.

+ * This class reflects non-Array Java objects into the JavaScript environment. It reflect fields + * directly, and uses NativeJavaMethod objects to reflect (possibly overloaded) methods. + * + *

* * @author Mike Shaver * @see NativeJavaArray * @see NativeJavaPackage * @see NativeJavaClass */ +public class NativeJavaObject implements Scriptable, SymbolScriptable, Wrapper, Serializable { -public class NativeJavaObject - implements Scriptable, SymbolScriptable, Wrapper, Serializable -{ private static final long serialVersionUID = -6948590651130498591L; static void init(ScriptableObject scope, boolean sealed) { JavaIterableIterator.init(scope, sealed); } - public NativeJavaObject() { } + public NativeJavaObject() {} - public NativeJavaObject(Scriptable scope, Object javaObject, - Class staticType) - { + public NativeJavaObject(Scriptable scope, Object javaObject, Class staticType) { this(scope, javaObject, staticType, false); } - public NativeJavaObject(Scriptable scope, Object javaObject, - Class staticType, boolean isAdapter) - { + public NativeJavaObject( + Scriptable scope, Object javaObject, Class staticType, boolean isAdapter) { this.parent = scope; this.javaObject = javaObject; this.staticType = staticType; @@ -62,10 +58,8 @@ protected void initMembers() { } else { dynamicType = staticType; } - members = JavaMembers.lookupClass(parent, dynamicType, staticType, - isAdapter); - fieldAndMethods - = members.getFieldAndMethodsObjects(this, javaObject, false); + members = JavaMembers.lookupClass(parent, dynamicType, staticType, isAdapter); + fieldAndMethods = members.getFieldAndMethodsObjects(this, javaObject, false); } @Override @@ -120,8 +114,7 @@ public void put(String name, Scriptable start, Object value) { // we modify it in the prototype rather than copy it down. if (prototype == null || members.has(name, false)) members.put(this, name, javaObject, value, false); - else - prototype.put(name, prototype, value); + else prototype.put(name, prototype, value); } @Override @@ -133,7 +126,7 @@ public void put(Symbol symbol, Scriptable start, Object value) { if (prototype == null || members.has(name, false)) { members.put(this, name, javaObject, value, false); } else if (prototype instanceof SymbolScriptable) { - ((SymbolScriptable)prototype).put(symbol, prototype, value); + ((SymbolScriptable) prototype).put(symbol, prototype, value); } } @@ -149,46 +142,36 @@ public boolean hasInstance(Scriptable value) { } @Override - public void delete(String name) { - } + public void delete(String name) {} @Override - public void delete(Symbol key) { - } + public void delete(Symbol key) {} @Override - public void delete(int index) { - } + public void delete(int index) {} @Override public Scriptable getPrototype() { if (prototype == null && javaObject instanceof String) { return TopLevel.getBuiltinPrototype( - ScriptableObject.getTopLevelScope(parent), - TopLevel.Builtins.String); + ScriptableObject.getTopLevelScope(parent), TopLevel.Builtins.String); } return prototype; } - /** - * Sets the prototype of the object. - */ + /** Sets the prototype of the object. */ @Override public void setPrototype(Scriptable m) { prototype = m; } - /** - * Returns the parent (enclosing) scope of the object. - */ + /** Returns the parent (enclosing) scope of the object. */ @Override public Scriptable getParentScope() { return parent; } - /** - * Sets the parent (enclosing) scope of the object. - */ + /** Sets the parent (enclosing) scope of the object. */ @Override public void setParentScope(Scriptable m) { parent = m; @@ -201,7 +184,7 @@ public Object[] getIds() { /** * @deprecated Use {@link Context#getWrapFactory()} together with calling {@link - * WrapFactory#wrap(Context, Scriptable, Object, Class)} + * WrapFactory#wrap(Context, Scriptable, Object, Class)} */ @Deprecated public static Object wrap(Scriptable scope, Object obj, Class staticType) { @@ -221,8 +204,7 @@ public String getClassName() { } @Override - public Object getDefaultValue(Class hint) - { + public Object getDefaultValue(Class hint) { Object value; if (hint == null) { if (javaObject instanceof Boolean) { @@ -245,14 +227,16 @@ public Object getDefaultValue(Class hint) } Object converterObject = get(converterName, this); if (converterObject instanceof Function) { - Function f = (Function)converterObject; - value = f.call(Context.getContext(), f.getParentScope(), - this, ScriptRuntime.emptyArgs); + Function f = (Function) converterObject; + value = + f.call( + Context.getContext(), + f.getParentScope(), + this, + ScriptRuntime.emptyArgs); } else { - if (hint == ScriptRuntime.NumberClass - && javaObject instanceof Boolean) - { - boolean b = ((Boolean)javaObject).booleanValue(); + if (hint == ScriptRuntime.NumberClass && javaObject instanceof Boolean) { + boolean b = ((Boolean) javaObject).booleanValue(); value = b ? ScriptRuntime.wrapNumber(1.0) : ScriptRuntime.zeroObj; } else { value = javaObject.toString(); @@ -263,9 +247,9 @@ public Object getDefaultValue(Class hint) } /** - * Determine whether we can/should convert between the given type and the - * desired one. This should be superceded by a conversion-cost calculation - * function, but for now I'll hide behind precedent. + * Determine whether we can/should convert between the given type and the desired one. This + * should be superceded by a conversion-cost calculation function, but for now I'll hide behind + * precedent. */ public static boolean canConvert(Object fromObj, Class to) { int weight = getConversionWeight(fromObj, to); @@ -273,174 +257,152 @@ public static boolean canConvert(Object fromObj, Class to) { return (weight < CONVERSION_NONE); } - private static final int JSTYPE_UNDEFINED = 0; // undefined type - private static final int JSTYPE_NULL = 1; // null - private static final int JSTYPE_BOOLEAN = 2; // boolean - private static final int JSTYPE_NUMBER = 3; // number - private static final int JSTYPE_STRING = 4; // string - private static final int JSTYPE_JAVA_CLASS = 5; // JavaClass + private static final int JSTYPE_UNDEFINED = 0; // undefined type + private static final int JSTYPE_NULL = 1; // null + private static final int JSTYPE_BOOLEAN = 2; // boolean + private static final int JSTYPE_NUMBER = 3; // number + private static final int JSTYPE_STRING = 4; // string + private static final int JSTYPE_JAVA_CLASS = 5; // JavaClass private static final int JSTYPE_JAVA_OBJECT = 6; // JavaObject - private static final int JSTYPE_JAVA_ARRAY = 7; // JavaArray - private static final int JSTYPE_OBJECT = 8; // Scriptable + private static final int JSTYPE_JAVA_ARRAY = 7; // JavaArray + private static final int JSTYPE_OBJECT = 8; // Scriptable - static final byte CONVERSION_TRIVIAL = 1; - static final byte CONVERSION_NONTRIVIAL = 0; - static final byte CONVERSION_NONE = 99; + static final byte CONVERSION_TRIVIAL = 1; + static final byte CONVERSION_NONTRIVIAL = 0; + static final byte CONVERSION_NONE = 99; /** - * Derive a ranking based on how "natural" the conversion is. - * The special value CONVERSION_NONE means no conversion is possible, - * and CONVERSION_NONTRIVIAL signals that more type conformance testing - * is required. - * Based on - * - * "preferred method conversions" from Live Connect 3 + * Derive a ranking based on how "natural" the conversion is. The special value CONVERSION_NONE + * means no conversion is possible, and CONVERSION_NONTRIVIAL signals that more type conformance + * testing is required. Based on "preferred method + * conversions" from Live Connect 3 */ static int getConversionWeight(Object fromObj, Class to) { int fromCode = getJSTypeCode(fromObj); switch (fromCode) { + case JSTYPE_UNDEFINED: + if (to == ScriptRuntime.StringClass || to == ScriptRuntime.ObjectClass) { + return 1; + } + break; - case JSTYPE_UNDEFINED: - if (to == ScriptRuntime.StringClass || - to == ScriptRuntime.ObjectClass) { - return 1; - } - break; - - case JSTYPE_NULL: - if (!to.isPrimitive()) { - return 1; - } - break; - - case JSTYPE_BOOLEAN: - // "boolean" is #1 - if (to == Boolean.TYPE) { - return 1; - } - else if (to == ScriptRuntime.BooleanClass) { - return 2; - } - else if (to == ScriptRuntime.ObjectClass) { - return 3; - } - else if (to == ScriptRuntime.StringClass) { - return 4; - } - break; + case JSTYPE_NULL: + if (!to.isPrimitive()) { + return 1; + } + break; - case JSTYPE_NUMBER: - if (to.isPrimitive()) { - if (to == Double.TYPE) { + case JSTYPE_BOOLEAN: + // "boolean" is #1 + if (to == Boolean.TYPE) { return 1; + } else if (to == ScriptRuntime.BooleanClass) { + return 2; + } else if (to == ScriptRuntime.ObjectClass) { + return 3; + } else if (to == ScriptRuntime.StringClass) { + return 4; } - else if (to != Boolean.TYPE) { - return 1 + getSizeRank(to); + break; + + case JSTYPE_NUMBER: + if (to.isPrimitive()) { + if (to == Double.TYPE) { + return 1; + } else if (to != Boolean.TYPE) { + return 1 + getSizeRank(to); + } + } else { + if (to == ScriptRuntime.StringClass) { + // native numbers are #1-8 + return 9; + } else if (to == ScriptRuntime.ObjectClass) { + return 10; + } else if (ScriptRuntime.NumberClass.isAssignableFrom(to)) { + // "double" is #1 + return 2; + } } - } - else { + break; + + case JSTYPE_STRING: if (to == ScriptRuntime.StringClass) { - // native numbers are #1-8 - return 9; - } - else if (to == ScriptRuntime.ObjectClass) { - return 10; - } - else if (ScriptRuntime.NumberClass.isAssignableFrom(to)) { - // "double" is #1 + return 1; + } else if (to.isInstance(fromObj)) { return 2; + } else if (to.isPrimitive()) { + if (to == Character.TYPE) { + return 3; + } else if (to != Boolean.TYPE) { + return 4; + } } - } - break; + break; - case JSTYPE_STRING: - if (to == ScriptRuntime.StringClass) { - return 1; - } - else if (to.isInstance(fromObj)) { - return 2; - } - else if (to.isPrimitive()) { - if (to == Character.TYPE) { + case JSTYPE_JAVA_CLASS: + if (to == ScriptRuntime.ClassClass) { + return 1; + } else if (to == ScriptRuntime.ObjectClass) { return 3; - } else if (to != Boolean.TYPE) { + } else if (to == ScriptRuntime.StringClass) { return 4; } - } - break; - - case JSTYPE_JAVA_CLASS: - if (to == ScriptRuntime.ClassClass) { - return 1; - } - else if (to == ScriptRuntime.ObjectClass) { - return 3; - } - else if (to == ScriptRuntime.StringClass) { - return 4; - } - break; - - case JSTYPE_JAVA_OBJECT: - case JSTYPE_JAVA_ARRAY: - Object javaObj = fromObj; - if (javaObj instanceof Wrapper) { - javaObj = ((Wrapper)javaObj).unwrap(); - } - if (to.isInstance(javaObj)) { - return CONVERSION_NONTRIVIAL; - } - if (to == ScriptRuntime.StringClass) { - return 2; - } - else if (to.isPrimitive() && to != Boolean.TYPE) { - return (fromCode == JSTYPE_JAVA_ARRAY) - ? CONVERSION_NONE : 2 + getSizeRank(to); - } - break; + break; - case JSTYPE_OBJECT: - // Other objects takes #1-#3 spots - if (to != ScriptRuntime.ObjectClass && to.isInstance(fromObj)) { - // No conversion required, but don't apply for java.lang.Object - return 1; - } - if (to.isArray()) { - if (fromObj instanceof NativeArray) { - // This is a native array conversion to a java array - // Array conversions are all equal, and preferable to object - // and string conversion, per LC3. - return 2; + case JSTYPE_JAVA_OBJECT: + case JSTYPE_JAVA_ARRAY: + Object javaObj = fromObj; + if (javaObj instanceof Wrapper) { + javaObj = ((Wrapper) javaObj).unwrap(); } - } - else if (to == ScriptRuntime.ObjectClass) { - return 3; - } - else if (to == ScriptRuntime.StringClass) { - return 4; - } - else if (to == ScriptRuntime.DateClass) { - if (fromObj instanceof NativeDate) { - // This is a native date to java date conversion - return 1; + if (to.isInstance(javaObj)) { + return CONVERSION_NONTRIVIAL; } - } - else if (to.isInterface()) { + if (to == ScriptRuntime.StringClass) { + return 2; + } else if (to.isPrimitive() && to != Boolean.TYPE) { + return (fromCode == JSTYPE_JAVA_ARRAY) ? CONVERSION_NONE : 2 + getSizeRank(to); + } + break; - if (fromObj instanceof NativeFunction) { - // See comments in createInterfaceAdapter + case JSTYPE_OBJECT: + // Other objects takes #1-#3 spots + if (to != ScriptRuntime.ObjectClass && to.isInstance(fromObj)) { + // No conversion required, but don't apply for java.lang.Object return 1; } - if (fromObj instanceof NativeObject) { - return 2; + if (to.isArray()) { + if (fromObj instanceof NativeArray) { + // This is a native array conversion to a java array + // Array conversions are all equal, and preferable to object + // and string conversion, per LC3. + return 2; + } + } else if (to == ScriptRuntime.ObjectClass) { + return 3; + } else if (to == ScriptRuntime.StringClass) { + return 4; + } else if (to == ScriptRuntime.DateClass) { + if (fromObj instanceof NativeDate) { + // This is a native date to java date conversion + return 1; + } + } else if (to.isInterface()) { + + if (fromObj instanceof NativeFunction) { + // See comments in createInterfaceAdapter + return 1; + } + if (fromObj instanceof NativeObject) { + return 2; + } + return 12; + } else if (to.isPrimitive() && to != Boolean.TYPE) { + return 4 + getSizeRank(to); } - return 12; - } - else if (to.isPrimitive() && to != Boolean.TYPE) { - return 4 + getSizeRank(to); - } - break; + break; } return CONVERSION_NONE; @@ -449,29 +411,21 @@ else if (to.isPrimitive() && to != Boolean.TYPE) { static int getSizeRank(Class aType) { if (aType == Double.TYPE) { return 1; - } - else if (aType == Float.TYPE) { + } else if (aType == Float.TYPE) { return 2; - } - else if (aType == Long.TYPE) { + } else if (aType == Long.TYPE) { return 3; - } - else if (aType == Integer.TYPE) { + } else if (aType == Integer.TYPE) { return 4; - } - else if (aType == Short.TYPE) { + } else if (aType == Short.TYPE) { return 5; - } - else if (aType == Character.TYPE) { + } else if (aType == Character.TYPE) { return 6; - } - else if (aType == Byte.TYPE) { + } else if (aType == Byte.TYPE) { return 7; - } - else if (aType == Boolean.TYPE) { + } else if (aType == Boolean.TYPE) { return CONVERSION_NONE; - } - else { + } else { return 8; } } @@ -479,37 +433,27 @@ else if (aType == Boolean.TYPE) { private static int getJSTypeCode(Object value) { if (value == null) { return JSTYPE_NULL; - } - else if (value == Undefined.instance) { + } else if (value == Undefined.instance) { return JSTYPE_UNDEFINED; - } - else if (value instanceof CharSequence) { + } else if (value instanceof CharSequence) { return JSTYPE_STRING; - } - else if (value instanceof Number) { + } else if (value instanceof Number) { return JSTYPE_NUMBER; - } - else if (value instanceof Boolean) { + } else if (value instanceof Boolean) { return JSTYPE_BOOLEAN; - } - else if (value instanceof Scriptable) { + } else if (value instanceof Scriptable) { if (value instanceof NativeJavaClass) { return JSTYPE_JAVA_CLASS; - } - else if (value instanceof NativeJavaArray) { + } else if (value instanceof NativeJavaArray) { return JSTYPE_JAVA_ARRAY; - } - else if (value instanceof Wrapper) { + } else if (value instanceof Wrapper) { return JSTYPE_JAVA_OBJECT; - } - else { + } else { return JSTYPE_OBJECT; } - } - else if (value instanceof Class) { + } else if (value instanceof Class) { return JSTYPE_JAVA_CLASS; - } - else { + } else { Class valueClass = value.getClass(); if (valueClass.isArray()) { return JSTYPE_JAVA_ARRAY; @@ -519,207 +463,176 @@ else if (value instanceof Class) { } /** - * Not intended for public use. Callers should use the - * public API Context.toType. + * Not intended for public use. Callers should use the public API Context.toType. + * * @deprecated as of 1.5 Release 4 * @see org.mozilla.javascript.Context#jsToJava(Object, Class) */ @Deprecated - public static Object coerceType(Class type, Object value) - { + public static Object coerceType(Class type, Object value) { return coerceTypeImpl(type, value); } - /** - * Type-munging for field setting and method invocation. - * Conforms to LC3 specification - */ - static Object coerceTypeImpl(Class type, Object value) - { + /** Type-munging for field setting and method invocation. Conforms to LC3 specification */ + static Object coerceTypeImpl(Class type, Object value) { if (value != null && value.getClass() == type) { return value; } switch (getJSTypeCode(value)) { + case JSTYPE_NULL: + // raise error if type.isPrimitive() + if (type.isPrimitive()) { + reportConversionError(value, type); + } + return null; - case JSTYPE_NULL: - // raise error if type.isPrimitive() - if (type.isPrimitive()) { - reportConversionError(value, type); - } - return null; - - case JSTYPE_UNDEFINED: - if (type == ScriptRuntime.StringClass || - type == ScriptRuntime.ObjectClass) { - return "undefined"; - } - reportConversionError("undefined", type); - break; - - case JSTYPE_BOOLEAN: - // Under LC3, only JS Booleans can be coerced into a Boolean value - if (type == Boolean.TYPE || - type == ScriptRuntime.BooleanClass || - type == ScriptRuntime.ObjectClass) { - return value; - } - else if (type == ScriptRuntime.StringClass) { - return value.toString(); - } - else { - reportConversionError(value, type); - } - break; - - case JSTYPE_NUMBER: - if (type == ScriptRuntime.StringClass) { - return ScriptRuntime.toString(value); - } - else if (type == ScriptRuntime.ObjectClass) { - Context context = Context.getCurrentContext(); - if ((context != null) && - context.hasFeature(Context.FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE)) { - //to process numbers like 2.0 as 2 without decimal place - long roundedValue = Math.round(toDouble(value)); - if(roundedValue == toDouble(value)) { - return coerceToNumber(Long.TYPE, value); + case JSTYPE_UNDEFINED: + if (type == ScriptRuntime.StringClass || type == ScriptRuntime.ObjectClass) { + return "undefined"; + } + reportConversionError("undefined", type); + break; + + case JSTYPE_BOOLEAN: + // Under LC3, only JS Booleans can be coerced into a Boolean value + if (type == Boolean.TYPE + || type == ScriptRuntime.BooleanClass + || type == ScriptRuntime.ObjectClass) { + return value; + } else if (type == ScriptRuntime.StringClass) { + return value.toString(); + } else { + reportConversionError(value, type); + } + break; + + case JSTYPE_NUMBER: + if (type == ScriptRuntime.StringClass) { + return ScriptRuntime.toString(value); + } else if (type == ScriptRuntime.ObjectClass) { + Context context = Context.getCurrentContext(); + if ((context != null) + && context.hasFeature(Context.FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE)) { + // to process numbers like 2.0 as 2 without decimal place + long roundedValue = Math.round(toDouble(value)); + if (roundedValue == toDouble(value)) { + return coerceToNumber(Long.TYPE, value); + } } + return coerceToNumber(Double.TYPE, value); + } else if ((type.isPrimitive() && type != Boolean.TYPE) + || ScriptRuntime.NumberClass.isAssignableFrom(type)) { + return coerceToNumber(type, value); + } else { + reportConversionError(value, type); } - return coerceToNumber(Double.TYPE, value); - } - else if ((type.isPrimitive() && type != Boolean.TYPE) || - ScriptRuntime.NumberClass.isAssignableFrom(type)) { - return coerceToNumber(type, value); - } - else { - reportConversionError(value, type); - } - break; - - case JSTYPE_STRING: - if (type == ScriptRuntime.StringClass || type.isInstance(value)) { - return value.toString(); - } - else if (type == Character.TYPE - || type == ScriptRuntime.CharacterClass) - { - // Special case for converting a single char string to a - // character - // Placed here because it applies *only* to JS strings, - // not other JS objects converted to strings - if (((CharSequence)value).length() == 1) { - return Character.valueOf(((CharSequence)value).charAt(0)); + break; + + case JSTYPE_STRING: + if (type == ScriptRuntime.StringClass || type.isInstance(value)) { + return value.toString(); + } else if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) { + // Special case for converting a single char string to a + // character + // Placed here because it applies *only* to JS strings, + // not other JS objects converted to strings + if (((CharSequence) value).length() == 1) { + return Character.valueOf(((CharSequence) value).charAt(0)); + } + return coerceToNumber(type, value); + } else if ((type.isPrimitive() && type != Boolean.TYPE) + || ScriptRuntime.NumberClass.isAssignableFrom(type)) { + return coerceToNumber(type, value); + } else { + reportConversionError(value, type); } - return coerceToNumber(type, value); - } - else if ((type.isPrimitive() && type != Boolean.TYPE) - || ScriptRuntime.NumberClass.isAssignableFrom(type)) - { - return coerceToNumber(type, value); - } - else { - reportConversionError(value, type); - } - break; - - case JSTYPE_JAVA_CLASS: - if (value instanceof Wrapper) { - value = ((Wrapper)value).unwrap(); - } + break; - if (type == ScriptRuntime.ClassClass || - type == ScriptRuntime.ObjectClass) { - return value; - } - else if (type == ScriptRuntime.StringClass) { - return value.toString(); - } - else { - reportConversionError(value, type); - } - break; + case JSTYPE_JAVA_CLASS: + if (value instanceof Wrapper) { + value = ((Wrapper) value).unwrap(); + } - case JSTYPE_JAVA_OBJECT: - case JSTYPE_JAVA_ARRAY: - if (value instanceof Wrapper) { - value = ((Wrapper)value).unwrap(); - } - if (type.isPrimitive()) { - if (type == Boolean.TYPE) { + if (type == ScriptRuntime.ClassClass || type == ScriptRuntime.ObjectClass) { + return value; + } else if (type == ScriptRuntime.StringClass) { + return value.toString(); + } else { reportConversionError(value, type); } - return coerceToNumber(type, value); - } - if (type == ScriptRuntime.StringClass) { - return value.toString(); - } - if (type.isInstance(value)) { - return value; - } - reportConversionError(value, type); - break; + break; - case JSTYPE_OBJECT: - if (type == ScriptRuntime.StringClass) { - return ScriptRuntime.toString(value); - } - else if (type.isPrimitive()) { - if (type == Boolean.TYPE) { - reportConversionError(value, type); + case JSTYPE_JAVA_OBJECT: + case JSTYPE_JAVA_ARRAY: + if (value instanceof Wrapper) { + value = ((Wrapper) value).unwrap(); } - return coerceToNumber(type, value); - } - else if (type.isInstance(value)) { - return value; - } - else if (type == ScriptRuntime.DateClass - && value instanceof NativeDate) - { - double time = ((NativeDate)value).getJSTimeValue(); - // XXX: This will replace NaN by 0 - return new Date((long)time); - } - else if (type.isArray() && value instanceof NativeArray) { - // Make a new java array, and coerce the JS array components - // to the target (component) type. - NativeArray array = (NativeArray) value; - long length = array.getLength(); - Class arrayType = type.getComponentType(); - Object Result = Array.newInstance(arrayType, (int)length); - for (int i = 0 ; i < length ; ++i) { - try { - Array.set(Result, i, coerceTypeImpl( - arrayType, array.get(i, array))); - } - catch (EvaluatorException ee) { + if (type.isPrimitive()) { + if (type == Boolean.TYPE) { reportConversionError(value, type); } + return coerceToNumber(type, value); } - - return Result; - } - else if (value instanceof Wrapper) { - value = ((Wrapper)value).unwrap(); - if (type.isInstance(value)) + if (type == ScriptRuntime.StringClass) { + return value.toString(); + } + if (type.isInstance(value)) { return value; + } reportConversionError(value, type); - } - else if (type.isInterface() - && (value instanceof NativeObject - || (value instanceof Callable && value instanceof ScriptableObject))) { - // Try to use function/object as implementation of Java interface. - return createInterfaceAdapter(type, (ScriptableObject) value); - } else { - reportConversionError(value, type); - } - break; + break; + + case JSTYPE_OBJECT: + if (type == ScriptRuntime.StringClass) { + return ScriptRuntime.toString(value); + } else if (type.isPrimitive()) { + if (type == Boolean.TYPE) { + reportConversionError(value, type); + } + return coerceToNumber(type, value); + } else if (type.isInstance(value)) { + return value; + } else if (type == ScriptRuntime.DateClass && value instanceof NativeDate) { + double time = ((NativeDate) value).getJSTimeValue(); + // XXX: This will replace NaN by 0 + return new Date((long) time); + } else if (type.isArray() && value instanceof NativeArray) { + // Make a new java array, and coerce the JS array components + // to the target (component) type. + NativeArray array = (NativeArray) value; + long length = array.getLength(); + Class arrayType = type.getComponentType(); + Object Result = Array.newInstance(arrayType, (int) length); + for (int i = 0; i < length; ++i) { + try { + Array.set(Result, i, coerceTypeImpl(arrayType, array.get(i, array))); + } catch (EvaluatorException ee) { + reportConversionError(value, type); + } + } + + return Result; + } else if (value instanceof Wrapper) { + value = ((Wrapper) value).unwrap(); + if (type.isInstance(value)) return value; + reportConversionError(value, type); + } else if (type.isInterface() + && (value instanceof NativeObject + || (value instanceof Callable + && value instanceof ScriptableObject))) { + // Try to use function/object as implementation of Java interface. + return createInterfaceAdapter(type, (ScriptableObject) value); + } else { + reportConversionError(value, type); + } + break; } return value; } - protected static Object createInterfaceAdapter(Classtype, ScriptableObject so) { + protected static Object createInterfaceAdapter(Class type, ScriptableObject so) { // XXX: Currently only instances of ScriptableObject are // supported since the resulting interface proxies should // be reused next time conversion is made and generic @@ -739,8 +652,7 @@ protected static Object createInterfaceAdapter(Classtype, ScriptableObject so return glue; } - private static Object coerceToNumber(Class type, Object value) - { + private static Object coerceToNumber(Class type, Object value) { Class valueClass = value.getClass(); // Character @@ -748,18 +660,22 @@ private static Object coerceToNumber(Class type, Object value) if (valueClass == ScriptRuntime.CharacterClass) { return value; } - return Character.valueOf((char)toInteger(value, - ScriptRuntime.CharacterClass, - Character.MIN_VALUE, - Character.MAX_VALUE)); + return Character.valueOf( + (char) + toInteger( + value, + ScriptRuntime.CharacterClass, + Character.MIN_VALUE, + Character.MAX_VALUE)); } // Double, Float - if (type == ScriptRuntime.ObjectClass || - type == ScriptRuntime.DoubleClass || type == Double.TYPE) { + if (type == ScriptRuntime.ObjectClass + || type == ScriptRuntime.DoubleClass + || type == Double.TYPE) { return valueClass == ScriptRuntime.DoubleClass - ? value - : Double.valueOf(toDouble(value)); + ? value + : Double.valueOf(toDouble(value)); } if (type == ScriptRuntime.FloatClass || type == Float.TYPE) { @@ -767,22 +683,18 @@ private static Object coerceToNumber(Class type, Object value) return value; } double number = toDouble(value); - if (Double.isInfinite(number) || Double.isNaN(number) - || number == 0.0) { - return Float.valueOf((float)number); + if (Double.isInfinite(number) || Double.isNaN(number) || number == 0.0) { + return Float.valueOf((float) number); } double absNumber = Math.abs(number); if (absNumber < Float.MIN_VALUE) { return Float.valueOf((number > 0.0) ? +0.0f : -0.0f); - } - else if (absNumber > Float.MAX_VALUE) { - return Float.valueOf((number > 0.0) ? - Float.POSITIVE_INFINITY : - Float.NEGATIVE_INFINITY); - } - else { - return Float.valueOf((float)number); + } else if (absNumber > Float.MAX_VALUE) { + return Float.valueOf( + (number > 0.0) ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY); + } else { + return Float.valueOf((float) number); } } @@ -791,10 +703,13 @@ else if (absNumber > Float.MAX_VALUE) { if (valueClass == ScriptRuntime.IntegerClass) { return value; } - return Integer.valueOf((int)toInteger(value, - ScriptRuntime.IntegerClass, - Integer.MIN_VALUE, - Integer.MAX_VALUE)); + return Integer.valueOf( + (int) + toInteger( + value, + ScriptRuntime.IntegerClass, + Integer.MIN_VALUE, + Integer.MAX_VALUE)); } if (type == ScriptRuntime.LongClass || type == Long.TYPE) { @@ -810,71 +725,65 @@ else if (absNumber > Float.MAX_VALUE) { */ final double max = Double.longBitsToDouble(0x43dfffffffffffffL); final double min = Double.longBitsToDouble(0xc3e0000000000000L); - return Long.valueOf(toInteger(value, - ScriptRuntime.LongClass, - min, - max)); + return Long.valueOf(toInteger(value, ScriptRuntime.LongClass, min, max)); } if (type == ScriptRuntime.ShortClass || type == Short.TYPE) { if (valueClass == ScriptRuntime.ShortClass) { return value; } - return Short.valueOf((short)toInteger(value, - ScriptRuntime.ShortClass, - Short.MIN_VALUE, - Short.MAX_VALUE)); + return Short.valueOf( + (short) + toInteger( + value, + ScriptRuntime.ShortClass, + Short.MIN_VALUE, + Short.MAX_VALUE)); } if (type == ScriptRuntime.ByteClass || type == Byte.TYPE) { if (valueClass == ScriptRuntime.ByteClass) { return value; } - return Byte.valueOf((byte)toInteger(value, - ScriptRuntime.ByteClass, - Byte.MIN_VALUE, - Byte.MAX_VALUE)); + return Byte.valueOf( + (byte) + toInteger( + value, + ScriptRuntime.ByteClass, + Byte.MIN_VALUE, + Byte.MAX_VALUE)); } return Double.valueOf(toDouble(value)); } - - private static double toDouble(Object value) - { + private static double toDouble(Object value) { if (value instanceof Number) { - return ((Number)value).doubleValue(); - } - else if (value instanceof String) { - return ScriptRuntime.toNumber((String)value); - } - else if (value instanceof Scriptable) { + return ((Number) value).doubleValue(); + } else if (value instanceof String) { + return ScriptRuntime.toNumber((String) value); + } else if (value instanceof Scriptable) { if (value instanceof Wrapper) { // XXX: optimize tail-recursion? - return toDouble(((Wrapper)value).unwrap()); + return toDouble(((Wrapper) value).unwrap()); } return ScriptRuntime.toNumber(value); - } - else { + } else { Method meth; try { - meth = value.getClass().getMethod("doubleValue", (Class [])null); - } - catch (NoSuchMethodException e) { + meth = value.getClass().getMethod("doubleValue", (Class[]) null); + } catch (NoSuchMethodException e) { meth = null; - } - catch (SecurityException e) { + } catch (SecurityException e) { meth = null; } if (meth != null) { try { - return ((Number)meth.invoke(value, (Object [])null)).doubleValue(); - } - catch (IllegalAccessException e) { + return ((Number) meth.invoke(value, (Object[]) null)).doubleValue(); + } catch (IllegalAccessException e) { // XXX: ignore, or error message? reportConversionError(value, Double.TYPE); - } - catch (InvocationTargetException e) { + } catch (InvocationTargetException e) { // XXX: ignore, or error message? reportConversionError(value, Double.TYPE); } @@ -883,9 +792,7 @@ else if (value instanceof Scriptable) { } } - private static long toInteger(Object value, Class type, - double min, double max) - { + private static long toInteger(Object value, Class type, double min, double max) { double d = toDouble(value); if (Double.isInfinite(d) || Double.isNaN(d)) { @@ -895,8 +802,7 @@ private static long toInteger(Object value, Class type, if (d > 0.0) { d = Math.floor(d); - } - else { + } else { d = Math.ceil(d); } @@ -904,22 +810,19 @@ private static long toInteger(Object value, Class type, // Convert to string first, for more readable message reportConversionError(ScriptRuntime.toString(value), type); } - return (long)d; + return (long) d; } - static void reportConversionError(Object value, Class type) - { + static void reportConversionError(Object value, Class type) { // It uses String.valueOf(value), not value.toString() since // value can be null, bug 282447. throw Context.reportRuntimeErrorById( - "msg.conversion.not.allowed", - String.valueOf(value), - JavaMembers.javaSignature(type)); + "msg.conversion.not.allowed", + String.valueOf(value), + JavaMembers.javaSignature(type)); } - private void writeObject(ObjectOutputStream out) - throws IOException - { + private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeBoolean(isAdapter); @@ -927,7 +830,7 @@ private void writeObject(ObjectOutputStream out) if (adapter_writeAdapterObject == null) { throw new IOException(); } - Object[] args = { javaObject, out }; + Object[] args = {javaObject, out}; try { adapter_writeAdapterObject.invoke(null, args); } catch (Exception ex) { @@ -944,16 +847,13 @@ private void writeObject(ObjectOutputStream out) } } - private void readObject(ObjectInputStream in) - throws IOException, ClassNotFoundException - { + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); isAdapter = in.readBoolean(); if (isAdapter) { - if (adapter_readAdapterObject == null) - throw new ClassNotFoundException(); - Object[] args = { this, in }; + if (adapter_readAdapterObject == null) throw new ClassNotFoundException(); + Object[] args = {this, in}; try { javaObject = adapter_readAdapterObject.invoke(null, args); } catch (Exception ex) { @@ -963,7 +863,7 @@ private void readObject(ObjectInputStream in) javaObject = in.readObject(); } - String className = (String)in.readObject(); + String className = (String) in.readObject(); if (className != null) { staticType = Class.forName(className); } else { @@ -973,16 +873,17 @@ private void readObject(ObjectInputStream in) initMembers(); } - private static Callable symbol_iterator = (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> { - if (!(thisObj instanceof NativeJavaObject)) { - throw ScriptRuntime.typeErrorById("msg.incompat.call", SymbolKey.ITERATOR); - } - Object javaObject = ((NativeJavaObject)thisObj).javaObject; - if (!(javaObject instanceof Iterable)) { - throw ScriptRuntime.typeErrorById("msg.incompat.call", SymbolKey.ITERATOR); - } - return new JavaIterableIterator(scope, (Iterable)javaObject); - }; + private static Callable symbol_iterator = + (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> { + if (!(thisObj instanceof NativeJavaObject)) { + throw ScriptRuntime.typeErrorById("msg.incompat.call", SymbolKey.ITERATOR); + } + Object javaObject = ((NativeJavaObject) thisObj).javaObject; + if (!(javaObject instanceof Iterable)) { + throw ScriptRuntime.typeErrorById("msg.incompat.call", SymbolKey.ITERATOR); + } + return new JavaIterableIterator(scope, (Iterable) javaObject); + }; private static final class JavaIterableIterator extends ES6Iterator { private static final long serialVersionUID = 1L; @@ -992,9 +893,7 @@ static void init(ScriptableObject scope, boolean sealed) { ES6Iterator.init(scope, sealed, new JavaIterableIterator(), ITERATOR_TAG); } - /** - * Only for constructing the prototype object. - */ + /** Only for constructing the prototype object. */ private JavaIterableIterator() { super(); } @@ -1030,21 +929,17 @@ protected String getTag() { private Iterator iterator; } - /** - * The prototype of this object. - */ + /** The prototype of this object. */ protected Scriptable prototype; - /** - * The parent scope of this object. - */ + /** The parent scope of this object. */ protected Scriptable parent; protected transient Object javaObject; protected transient Class staticType; protected transient JavaMembers members; - private transient Map fieldAndMethods; + private transient Map fieldAndMethods; protected transient boolean isAdapter; private static final Object COERCED_INTERFACE_KEY = "Coerced Interface"; @@ -1059,13 +954,11 @@ protected String getTag() { try { sig2[0] = ScriptRuntime.ObjectClass; sig2[1] = Kit.classOrNull("java.io.ObjectOutputStream"); - adapter_writeAdapterObject = cl.getMethod("writeAdapterObject", - sig2); + adapter_writeAdapterObject = cl.getMethod("writeAdapterObject", sig2); sig2[0] = ScriptRuntime.ScriptableClass; sig2[1] = Kit.classOrNull("java.io.ObjectInputStream"); - adapter_readAdapterObject = cl.getMethod("readAdapterObject", - sig2); + adapter_readAdapterObject = cl.getMethod("readAdapterObject", sig2); } catch (NoSuchMethodException e) { adapter_writeAdapterObject = null; @@ -1073,5 +966,4 @@ protected String getTag() { } } } - } From c7c99d62bf5bab861aeec96ad3c6ecb59adf6122 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 5 Oct 2021 16:55:45 +0200 Subject: [PATCH 04/14] intermediate update - waiting for other PR --- src/org/mozilla/javascript/ClassCache.java | 13 +- src/org/mozilla/javascript/JavaTypes.java | 218 +++++++++++++++++ .../mozilla/javascript/NativeJavaList.java | 19 +- src/org/mozilla/javascript/NativeJavaMap.java | 184 +++++++------- .../mozilla/javascript/NativeJavaObject.java | 16 +- src/org/mozilla/javascript/ScriptRuntime.java | 48 ---- src/org/mozilla/javascript/WrapFactory.java | 1 + .../javascript/tests/JavaTypesTest.java | 224 ++++++++++++++++++ .../javascript/tests/NativeJavaMapTest.java | 15 +- 9 files changed, 580 insertions(+), 158 deletions(-) create mode 100644 src/org/mozilla/javascript/JavaTypes.java create mode 100644 testsrc/org/mozilla/javascript/tests/JavaTypesTest.java diff --git a/src/org/mozilla/javascript/ClassCache.java b/src/org/mozilla/javascript/ClassCache.java index 19672cde49..cabc7f47c7 100644 --- a/src/org/mozilla/javascript/ClassCache.java +++ b/src/org/mozilla/javascript/ClassCache.java @@ -7,6 +7,7 @@ package org.mozilla.javascript; import java.io.Serializable; +import java.lang.reflect.Type; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -25,6 +26,7 @@ public class ClassCache implements Serializable { private transient Map classTable; private transient Map> classAdapterCache; private transient Map, Object> interfaceAdapterCache; + private transient Map typeCache; private int generatedClassSerial; private Scriptable associatedScope; @@ -136,19 +138,24 @@ Map getClassCacheMap() { if (classTable == null) { // Use 1 as concurrency level here and for other concurrent hash maps // as we don't expect high levels of sustained concurrent writes. - classTable = new ConcurrentHashMap(16, 0.75f, 1); + classTable = new ConcurrentHashMap<>(16, 0.75f, 1); } return classTable; } Map> getInterfaceAdapterCacheMap() { if (classAdapterCache == null) { - classAdapterCache = - new ConcurrentHashMap>(16, 0.75f, 1); + classAdapterCache = new ConcurrentHashMap<>(16, 0.75f, 1); } return classAdapterCache; } + Map getTypeCacheMap() { + if (typeCache == null) { + typeCache = new ConcurrentHashMap<>(16, 0.75f, 1); + } + return typeCache; + } /** * @deprecated The method always returns false. * @see #setInvokerOptimizationEnabled(boolean enabled) diff --git a/src/org/mozilla/javascript/JavaTypes.java b/src/org/mozilla/javascript/JavaTypes.java new file mode 100644 index 0000000000..e8859f2b3a --- /dev/null +++ b/src/org/mozilla/javascript/JavaTypes.java @@ -0,0 +1,218 @@ +/* + * Licensed Materials - Property of FOCONIS AG + * (C) Copyright FOCONIS AG. + */ + +package org.mozilla.javascript; + +import java.lang.reflect.Array; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * Type lookup helper. This is used to read the generic info of Lists / Maps in NativeJavaList/Map. + * + *

If we have a class that implements List<SomeType>, we need to extract + * SomeType, so that we can convert the value to the appropriate data type. + * + *

For example, if you have a List<Integer>, you expect, that the list only + * contains integers. But there is no type checking at runtime. Rhino tries to read the type + * information and tries to convert the values with {@link Context#jsToJava(Object, Class)}. + * + *

Example 1: integerList[1] = '123' will convert the String '123' to an + * integer, so the underlying java list will contain the correct data type. + * + *

Note 1: Type information is stored only on fields and classes. If you define your + * List<Integer> myList = new ArrayList<>(); as local variable, no type + * information is present, so this feature makes more sense on beans. + * + *

Note 2: Conversion is limited to "common" datatypes, like String, Integer, Double or + * Long + * + *

Searching the type can be a complex task (see + * https://www.javacodegeeks.com/2013/12/advanced-java-generics-retreiving-generic-type-arguments.html) + * You might have simple declarations like List<Integer> or + *

  • ArrayList<Integer>. But you may have generic classes that implements + *
  • List<E> and you have to figure out what type E is. + * + * @author Roland Praml, FOCONIS AG + */ +public class JavaTypes { + + private static final Type[] NOT_FOUND = new Type[0]; + + public static Type[] lookupType( + Scriptable scope, Type type1, Type type2, Class classOfInterest) { + ClassCache cache = ClassCache.get(scope); + Map tc = cache.getTypeCacheMap(); + + Type[] types1 = NOT_FOUND; + Type[] types2 = NOT_FOUND; + if (type1 != null) { + types1 = tc.computeIfAbsent(type1, t -> lookupType(t, classOfInterest)); + } + if (type2 != null) { + types2 = tc.computeIfAbsent(type2, t -> lookupType(t, classOfInterest)); + } + if (types1 == NOT_FOUND && types2 == NOT_FOUND) { + return null; + } else if (types1 == NOT_FOUND) { + return types2; + } else if (types2 == NOT_FOUND) { + return types1; + } else { + Type[] ret = new Type[types1.length]; + for (int i = 0; i < types1.length; i++) { + if (getRawType(types1[i]).isAssignableFrom(getRawType(types2[i]))) { + ret[i] = types2[i]; + } else { + ret[i] = types1[i]; + } + } + return ret; + } + } + + private static Type[] lookupType(Type type, Class classOfInterest) { + return lookupType(type, classOfInterest, null); + } + + private static Type[] lookupType( + Type type, Class classOfInterest, Map, Type> typeVarAssigns) { + if (type instanceof ParameterizedType) { + return getParametrizedTypeArguments( + (ParameterizedType) type, classOfInterest, typeVarAssigns); + } else if (type instanceof Class) { + return getClassArguments((Class) type, classOfInterest, typeVarAssigns); + } else { + return NOT_FOUND; + } + } + + private static Type[] getClassArguments( + Class clazz, Class classOfInterest, Map, Type> typeVarAssigns) { + if (!classOfInterest.isAssignableFrom(clazz)) { + return NOT_FOUND; + } + if (typeVarAssigns == null) { + typeVarAssigns = new HashMap<>(); + } + if (classOfInterest.isInterface()) { + for (Type interfaceType : clazz.getGenericInterfaces()) { + Type[] ret = lookupType(interfaceType, classOfInterest, typeVarAssigns); + if (ret != NOT_FOUND) { + return resolveTypes(ret, typeVarAssigns); + } + } + } + Type[] ret = lookupType(clazz.getGenericSuperclass(), classOfInterest, typeVarAssigns); + if (ret == NOT_FOUND) { + ret = new Type[classOfInterest.getTypeParameters().length]; + Arrays.fill(ret, Object.class); + } + return ret; + } + + private static Type[] getParametrizedTypeArguments( + ParameterizedType parameterizedType, + Class classOfInterest, + Map, Type> typeVarAssigns) { + Class clazz = (Class) parameterizedType.getRawType(); + if (clazz == classOfInterest) { + // Type is List and classOfInterest is List + return resolveTypes(parameterizedType.getActualTypeArguments(), typeVarAssigns); + } + if (!classOfInterest.isAssignableFrom(clazz)) { + return NOT_FOUND; + } + if (typeVarAssigns == null) { + typeVarAssigns = new HashMap<>(); + } + // get the subject parameterized type's arguments + final Type[] typeArgs = parameterizedType.getActualTypeArguments(); + // and get the corresponding type variables from the raw class + final TypeVariable[] typeParams = clazz.getTypeParameters(); + + // map the arguments to their respective type variables + for (int i = 0; i < typeParams.length; i++) { + final Type typeArg = typeArgs[i]; + typeVarAssigns.put(typeParams[i], typeVarAssigns.getOrDefault(typeArg, typeArg)); + } + if (classOfInterest.isInterface()) { + for (Type interfaceType : clazz.getGenericInterfaces()) { + Type[] ret = lookupType(interfaceType, classOfInterest, typeVarAssigns); + if (ret != NOT_FOUND) { + return resolveTypes(ret, typeVarAssigns); + } + } + } + return lookupType(clazz.getGenericSuperclass(), classOfInterest, typeVarAssigns); + } + + /** + * @param actualTypeArguments + * @param typeVarAssigns + * @return + */ + private static Type[] resolveTypes(Type[] ret, Map, Type> typeVarAssigns) { + for (int i = 0; i < ret.length; i++) { + if (ret[i] instanceof TypeVariable) { + ret[i] = typeVarAssigns.getOrDefault(ret[i], Object.class); + } else if (ret[i] instanceof WildcardType) { + WildcardType wildcard = (WildcardType) ret[i]; + Type[] bound = wildcard.getLowerBounds(); + if (bound.length > 0) { + ret[i] = bound[0]; + } else { + bound = wildcard.getUpperBounds(); + if (bound.length > 0) { + ret[i] = bound[0]; + } else { + ret[i] = Object.class; + } + } + } + } + return ret; + } + + /** returns the raw type. Taken from google guice. */ + public static Class getRawType(Type type) { + if (type == null) { + return null; + + } else if (type instanceof Class) { + // Type is a normal class. + return (Class) type; + + } else if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + return (Class) parameterizedType.getRawType(); + + } else if (type instanceof GenericArrayType) { + Type componentType = ((GenericArrayType) type).getGenericComponentType(); + return Array.newInstance(getRawType(componentType), 0).getClass(); + + } else if (type instanceof TypeVariable || type instanceof WildcardType) { + // We could use the variable's bounds, but that won't work if there + // are multiple. Having a raw type that's more general than + // necessary is okay. + return Object.class; + + } else { + String className = type.getClass().getName(); + throw new IllegalArgumentException( + "Expected a Class, " + + "ParameterizedType, or GenericArrayType, but <" + + type + + "> is of type " + + className); + } + } +} diff --git a/src/org/mozilla/javascript/NativeJavaList.java b/src/org/mozilla/javascript/NativeJavaList.java index 70ad8238c4..9c35dd62ac 100644 --- a/src/org/mozilla/javascript/NativeJavaList.java +++ b/src/org/mozilla/javascript/NativeJavaList.java @@ -5,7 +5,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.javascript; -import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; @@ -14,25 +13,19 @@ public class NativeJavaList extends NativeJavaObject { private static final long serialVersionUID = 6403865639690547921L; - private List list; + protected final List list; - private Class valueType; + protected final Class valueType; @SuppressWarnings("unchecked") public NativeJavaList(Scriptable scope, Object list, Type staticType) { super(scope, list, staticType); assert list instanceof List; this.list = (List) list; - if (staticType == null) { - staticType = list.getClass().getGenericSuperclass(); - } - if (staticType instanceof ParameterizedType) { - Type[] types = ((ParameterizedType) staticType).getActualTypeArguments(); - // types[0] contains the T of 'List' - this.valueType = ScriptRuntime.getRawType(types[0]); - } else { - this.valueType = Object.class; - } + + Type[] types = JavaTypes.lookupType(scope, list.getClass(), staticType, List.class); + + this.valueType = types == null ? Object.class : JavaTypes.getRawType(types[0]); } @Override diff --git a/src/org/mozilla/javascript/NativeJavaMap.java b/src/org/mozilla/javascript/NativeJavaMap.java index ac8d39479d..9438ab28a8 100644 --- a/src/org/mozilla/javascript/NativeJavaMap.java +++ b/src/org/mozilla/javascript/NativeJavaMap.java @@ -5,22 +5,24 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.javascript; -import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; -import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; +import java.util.function.IntFunction; public class NativeJavaMap extends NativeJavaObject { private static final long serialVersionUID = 46513864372878618L; - private Map map; - private Class keyType; - private Class valueType; - private transient Map keyTranslationMap; + private final Map map; + + private final Class keyType; + + private final Class valueType; static void init(ScriptableObject scope, boolean sealed) { NativeJavaMapIterator.init(scope, sealed); @@ -31,17 +33,9 @@ public NativeJavaMap(Scriptable scope, Object map, Type staticType) { super(scope, map, staticType); assert map instanceof Map; this.map = (Map) map; - if (staticType == null) { - staticType = map.getClass().getGenericSuperclass(); - } - if (staticType instanceof ParameterizedType) { - Type[] types = ((ParameterizedType) staticType).getActualTypeArguments(); - this.keyType = ScriptRuntime.getRawType(types[0]); - this.valueType = ScriptRuntime.getRawType(types[1]); - } else { - this.keyType = Object.class; - this.valueType = Object.class; - } + Type[] types = JavaTypes.lookupType(scope, map.getClass(), staticType, Map.class); + this.keyType = types == null ? Object.class : JavaTypes.getRawType(types[0]); + this.valueType = types == null ? Object.class : JavaTypes.getRawType(types[1]); } @Override @@ -53,7 +47,8 @@ public String getClassName() { public boolean has(String name, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - if (map.containsKey(toKey(name, false))) { + Object key = toKey(name); + if (key != null && map.containsKey(key)) { return true; } } @@ -64,7 +59,8 @@ public boolean has(String name, Scriptable start) { public boolean has(int index, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - if (map.containsKey(toKey(index, false))) { + Object key = toKey(index); + if (key != null && map.containsKey(key)) { return true; } } @@ -83,8 +79,8 @@ public boolean has(Symbol key, Scriptable start) { public Object get(String name, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = toKey(name, false); - if (map.containsKey(key)) { + Object key = toKey(name); + if (key != null && map.containsKey(key)) { Object obj = map.get(key); return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); } @@ -96,66 +92,16 @@ public Object get(String name, Scriptable start) { public Object get(int index, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = toKey(Integer.valueOf(index), false); - if (map.containsKey(key)) { + Object key = toKey(index); + if (key != null && map.containsKey(key)) { Object obj = map.get(key); return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); } + return Scriptable.NOT_FOUND; // do not report "memberNotFound" in this case. } return super.get(index, start); } - @SuppressWarnings("unchecked") - private Object toKey(Object key, boolean translateNew) { - if (keyType == String.class || map.containsKey(key)) { - // fast exit, if we know, that there are only string keys in the map o - return key; - } - String strKey = ScriptRuntime.toString(key); - if (map.containsKey(strKey)) { - // second fast exit, if the key is present as string. - return strKey; - } - - // TODO: There is no change detection yet. The keys in the wrapped map could theoretically - // change though other java code. To reduce this risk, we clear the keyTranslationMap on - // unwrap. An approach to track if the underlying map was changed may be to read the - // 'modCount' property of HashMap, but this is not part of the Map interface. - // So for now, wrapped maps must not be changed by external code. - if (keyTranslationMap == null) { - keyTranslationMap = new HashMap<>(); - map.keySet().forEach(k -> keyTranslationMap.put(ScriptRuntime.toString(k), k)); - } - Object ret = keyTranslationMap.get(strKey); - if (ret == null) { - if (translateNew) { - // we do not have the key, and we need a new one, (due PUT operation e.g.) - if (keyType == Object.class) { - // if we do not know the keyType, just pass through the key - ret = key; - } else if (Enum.class.isAssignableFrom(keyType)) { - // for enums use "valueOf" method - ret = Enum.valueOf((Class) keyType, strKey); - } else { - // for all other use jsToJava (which might run into a conversionError) - ret = Context.jsToJava(key, keyType); - } - keyTranslationMap.put(strKey, ret); - } else { - ret = key; - } - } - return ret; - } - - private Object toValue(Object value) { - if (valueType == Object.class) { - return value; - } else { - return Context.jsToJava(value, valueType); - } - } - @Override public Object get(Symbol key, Scriptable start) { if (SymbolKey.ITERATOR.equals(key)) { @@ -168,7 +114,11 @@ public Object get(Symbol key, Scriptable start) { public void put(String name, Scriptable start, Object value) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - map.put(toKey(name, true), toValue(value)); + Object key = toKey(name); + if (key == null) { + reportConversionError(name, keyType); + } + map.put(key, Context.jsToJava(value, valueType)); } else { super.put(name, start, value); } @@ -178,19 +128,16 @@ public void put(String name, Scriptable start, Object value) { public void put(int index, Scriptable start, Object value) { Context cx = Context.getContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - map.put(toKey(index, true), toValue(value)); + Object key = toKey(Integer.valueOf(index)); + if (key == null) { + reportConversionError(Integer.valueOf(index), keyType); + } + map.put(key, Context.jsToJava(value, valueType)); } else { super.put(index, start, value); } } - @Override - public Object unwrap() { - // clear keyTranslationMap on unwrap, as native java code may modify the object now - keyTranslationMap = null; - return super.unwrap(); - } - @Override public Object[] getIds() { Context cx = Context.getCurrentContext(); @@ -208,6 +155,77 @@ public Object[] getIds() { return super.getIds(); } + private static final Map, Function> STRING_CONVERTERS = + new LinkedHashMap<>(); + private static final Map, IntFunction> INT_CONVERTERS = + new LinkedHashMap<>(); + + { + STRING_CONVERTERS.put(String.class, Function.identity()); + STRING_CONVERTERS.put(Integer.class, Integer::valueOf); + STRING_CONVERTERS.put(Long.class, Long::valueOf); + STRING_CONVERTERS.put(Double.class, Double::valueOf); + + INT_CONVERTERS.put(String.class, Integer::toString); + INT_CONVERTERS.put(Integer.class, Integer::valueOf); + INT_CONVERTERS.put(Long.class, Long::valueOf); + INT_CONVERTERS.put(Double.class, Double::valueOf); + } + /** + * Converts the key, which is either as String or an Integer to the `keyType` + * + * @return + */ + @SuppressWarnings("unchecked") + protected Object toKey(Object key) { + try { + if (key instanceof String) { + // 1. if we have an enum, try to convert the value + if (keyType.isEnum()) return Enum.valueOf((Class) keyType, (String) key); + + Function converter = STRING_CONVERTERS.get(keyType); + if (converter != null) { + return converter.apply((String) key); + } else { + return findStringKey(key); + } + + } else { // could be either String or Integer + int index = ((Integer) key).intValue(); + IntFunction converter = INT_CONVERTERS.get(keyType); + if (converter != null) { + return converter.apply(index); + } else { + return findIndexKey(index); + } + } + } catch (IllegalArgumentException ex) { + return null; // cannot convert key + } + } + + protected Object findStringKey(Object key) { + for (Function converter : STRING_CONVERTERS.values()) { + try { + Object testKey = converter.apply((String) key); + if (map.containsKey(testKey)) return testKey; + } catch (IllegalArgumentException ex) { + } + } + return key; + } + + protected Object findIndexKey(int index) { + for (IntFunction converter : INT_CONVERTERS.values()) { + try { + Object testKey = converter.apply(index); + if (map.containsKey(testKey)) return testKey; + } catch (IllegalArgumentException ex) { + } + } + return Integer.valueOf(index); + } + private static Callable symbol_iterator = (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> { if (!(thisObj instanceof NativeJavaMap)) { diff --git a/src/org/mozilla/javascript/NativeJavaObject.java b/src/org/mozilla/javascript/NativeJavaObject.java index ca2b91d189..444502ed4d 100644 --- a/src/org/mozilla/javascript/NativeJavaObject.java +++ b/src/org/mozilla/javascript/NativeJavaObject.java @@ -47,7 +47,7 @@ public NativeJavaObject( Scriptable scope, Object javaObject, Type staticType, boolean isAdapter) { this.parent = scope; this.javaObject = javaObject; - this.staticType = ScriptRuntime.getRawType(staticType); + this.staticRawType = JavaTypes.getRawType(staticType); this.isAdapter = isAdapter; initMembers(); } @@ -57,9 +57,9 @@ protected void initMembers() { if (javaObject != null) { dynamicType = javaObject.getClass(); } else { - dynamicType = staticType; + dynamicType = staticRawType; } - members = JavaMembers.lookupClass(parent, dynamicType, staticType, isAdapter); + members = JavaMembers.lookupClass(parent, dynamicType, staticRawType, isAdapter); fieldAndMethods = members.getFieldAndMethodsObjects(this, javaObject, false); } @@ -841,8 +841,8 @@ private void writeObject(ObjectOutputStream out) throws IOException { out.writeObject(javaObject); } - if (staticType != null) { - out.writeObject(staticType.getName()); + if (staticRawType != null) { + out.writeObject(staticRawType.getName()); } else { out.writeObject(null); } @@ -866,9 +866,9 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE String className = (String) in.readObject(); if (className != null) { - staticType = Class.forName(className); + staticRawType = Class.forName(className); } else { - staticType = null; + staticRawType = null; } initMembers(); @@ -938,7 +938,7 @@ protected String getTag() { protected transient Object javaObject; - protected transient Class staticType; + protected transient Class staticRawType; protected transient JavaMembers members; private transient Map fieldAndMethods; protected transient boolean isAdapter; diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 57a7e036fb..642012b20a 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -7,13 +7,7 @@ package org.mozilla.javascript; import java.io.Serializable; -import java.lang.reflect.Array; import java.lang.reflect.Constructor; -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.lang.reflect.WildcardType; import java.math.BigDecimal; import java.math.BigInteger; import java.math.MathContext; @@ -2878,48 +2872,6 @@ public static String typeofName(Scriptable scope, String id) { return typeof(getObjectProp(val, id, cx)); } - /** returns the raw type. Taken from google guice. */ - public static Class getRawType(Type type) { - if (type == null) { - return null; - - } else if (type instanceof Class) { - // Type is a normal class. - return (Class) type; - - } else if (type instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) type; - - // I'm not exactly sure why getRawType() returns Type instead of - // Class. Neal isn't either but suspects some pathological case - // related to nested classes exists. - Type rawType = parameterizedType.getRawType(); - if (!(rawType instanceof Class)) { - throw new IllegalArgumentException(); - } - return (Class) rawType; - - } else if (type instanceof GenericArrayType) { - Type componentType = ((GenericArrayType) type).getGenericComponentType(); - return Array.newInstance(getRawType(componentType), 0).getClass(); - - } else if (type instanceof TypeVariable || type instanceof WildcardType) { - // We could use the variable's bounds, but that won't work if there - // are multiple. Having a raw type that's more general than - // necessary is okay. - return Object.class; - - } else { - String className = type.getClass().getName(); - throw new IllegalArgumentException( - "Expected a Class, " - + "ParameterizedType, or GenericArrayType, but <" - + type - + "> is of type " - + className); - } - } - public static boolean isObject(Object value) { if (value == null) { return false; diff --git a/src/org/mozilla/javascript/WrapFactory.java b/src/org/mozilla/javascript/WrapFactory.java index 43fe4bcc17..1c2387d300 100644 --- a/src/org/mozilla/javascript/WrapFactory.java +++ b/src/org/mozilla/javascript/WrapFactory.java @@ -113,6 +113,7 @@ public Scriptable wrapNewObject(Context cx, Scriptable scope, Object obj) { */ public Scriptable wrapAsJavaObject( Context cx, Scriptable scope, Object javaObject, Type staticType) { + if (javaObject instanceof List) { return new NativeJavaList(scope, javaObject, staticType); } else if (javaObject instanceof Map) { diff --git a/testsrc/org/mozilla/javascript/tests/JavaTypesTest.java b/testsrc/org/mozilla/javascript/tests/JavaTypesTest.java new file mode 100644 index 0000000000..7a6327736d --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/JavaTypesTest.java @@ -0,0 +1,224 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mozilla.javascript.tests; + +import static org.junit.Assert.assertArrayEquals; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import org.junit.Test; +import org.mozilla.javascript.ClassCache; +import org.mozilla.javascript.JavaTypes; +import org.mozilla.javascript.NativeObject; +import org.mozilla.javascript.ScriptableObject; + +/** + * Test the type resolver. + * + * @author Roland Praml, FOCONIS AG + */ +@SuppressWarnings({"rawtypes", "serial"}) +public class JavaTypesTest { + + private Type getType(String name) throws Exception { + return getClass().getDeclaredField(name).getGenericType(); + } + + private Type[] check(String name, Class interestingClass) throws Exception { + Object value = getClass().getDeclaredField(name).get(this); + ScriptableObject scope = new NativeObject(); + new ClassCache().associate(scope); + return JavaTypes.lookupType( + scope, value == null ? null : value.getClass(), getType(name), interestingClass); + } + + List list1; + /** Tests, if we can read type arguments from a normal List<String>. */ + @Test + public void testList1() throws Exception { + assertArrayEquals(new Type[] {String.class}, check("list1", List.class)); + assertArrayEquals(new Type[] {String.class}, check("list1", Collection.class)); + assertArrayEquals(null, check("list1", ArrayList.class)); + } + + List> list2; + /** Tests, if we can read special generic type arguments like List<List<String>>. */ + @Test + public void testList2() throws Exception { + assertArrayEquals(new Type[] {getType("list1")}, check("list2", List.class)); + assertArrayEquals(new Type[] {getType("list1")}, check("list2", Collection.class)); + assertArrayEquals(null, check("list2", ArrayList.class)); + } + + ArrayList list3; + + /** Tests, if we can read type arguments if it is a class instead of an interface. */ + @Test + public void testList3() throws Exception { + assertArrayEquals(new Type[] {String.class}, check("list3", List.class)); + } + + // some test classes + static class TestList4 extends ArrayList { + private static final long serialVersionUID = 1L; + } + + TestList4 list4; + /** Tests, if we can read type argument, if class inherits from a generic class. */ + @Test + public void testList4() throws Exception { + assertArrayEquals(new Type[] {String.class}, check("list4", List.class)); + } + + static class TestList5A extends ArrayList { + private static final long serialVersionUID = 1L; + } + + static class TestList5 extends TestList5A { + private static final long serialVersionUID = 1L; + } + + TestList5 list5; + /** Tests, if we can read type arguments, if inheritance chain introduces new type arguments. */ + @Test + public void testList5() throws Exception { + assertArrayEquals(new Type[] {String.class}, check("list5", List.class)); + } + + static class TestList6 extends TestList5A + implements Collection, List { + private static final long serialVersionUID = 1L; + } + + TestList6 list6; + /** Tests, if we can read type arguments, if wildcards are involved. */ + @Test + public void testList6() throws Exception { + assertArrayEquals(new Type[] {String.class}, check("list6", List.class)); + } + + List list7; + /** Tests, if we can read type arguments, if wildcards are involved. */ + @Test + public void testList7() throws Exception { + assertArrayEquals(new Type[] {Object.class}, check("list7", List.class)); + assertArrayEquals(null, check("list7", Map.class)); + } + + List list8; + /** Tests, if we can read type arguments, if raw types are involved. */ + @Test + public void testList8() throws Exception { + assertArrayEquals(new Type[] {Object.class}, check("list8", List.class)); + assertArrayEquals(null, check("list8", Map.class)); + } + + List list9; + /** Tests, if we can read wildcard type arguments with lowerBound. */ + @Test + public void testList9() throws Exception { + assertArrayEquals(new Type[] {Number.class}, check("list9", List.class)); + assertArrayEquals(null, check("list9", Map.class)); + } + + List list10; + /** Tests, if we can read wildcard type arguments with upperBound. */ + @Test + public void testList10() throws Exception { + assertArrayEquals(new Type[] {Number.class}, check("list10", List.class)); + assertArrayEquals(null, check("list10", Map.class)); + } + + List list11; + /** Tests, if we can read array types. */ + @Test + public void testList11() throws Exception { + assertArrayEquals(new Type[] {new Number[0].getClass()}, check("list11", List.class)); + } + + List list12 = new ArrayList() {}; + /** Tests, if we take the "best" type, if there are different possibilities. */ + @Test + public void testList12() throws Exception { + assertArrayEquals(new Type[] {String.class}, check("list12", List.class)); + } + + Object list13 = new ArrayList() {}; + /** Tests, if we take the "best" type, if there are different possibilities. */ + @Test + public void testList13() throws Exception { + assertArrayEquals(new Type[] {String.class}, check("list13", List.class)); + } + + List list14 = new ArrayList() {}; + /** Tests, if we take the "best" type, if there are different possibilities. */ + @Test + public void testList14() throws Exception { + assertArrayEquals(new Type[] {Integer.class}, check("list14", List.class)); + } + + List list15 = new ArrayList() {}; + /** Tests, if we take the "best" type, if there are different possibilities. */ + @Test + public void testList15() throws Exception { + assertArrayEquals(new Type[] {Integer.class}, check("list15", List.class)); + } + + abstract static class TwoInterfaces implements Iterator, Map {} + + TwoInterfaces twoInterfaces1; + /** Tests, if we can read the type info of each interface. */ + @Test + public void testTwoInterfaces1() throws Exception { + assertArrayEquals(new Type[] {Integer.class}, check("twoInterfaces1", Iterator.class)); + assertArrayEquals( + new Type[] {String.class, Double.class}, check("twoInterfaces1", Map.class)); + assertArrayEquals(new Type[] {}, check("twoInterfaces1", TwoInterfaces.class)); + } + + abstract static class TwoGenericInterfaces implements Iterator, Map {} + + TwoGenericInterfaces twoInterfaces2; + /** Tests, if we can read the type info of each generic interface. */ + @Test + public void testTwoInterfaces2() throws Exception { + assertArrayEquals(new Type[] {Integer.class}, check("twoInterfaces2", Iterator.class)); + assertArrayEquals( + new Type[] {String.class, Double.class}, check("twoInterfaces2", Map.class)); + assertArrayEquals( + new Type[] {Double.class, Integer.class, String.class}, + check("twoInterfaces2", TwoGenericInterfaces.class)); + } + + TwoGenericInterfaces twoInterfaces3; + /** Tests, if we can read the type info of each generic interface. */ + @Test + public void testTwoInterfaces3() throws Exception { + assertArrayEquals(new Type[] {Integer.class}, check("twoInterfaces3", Iterator.class)); + assertArrayEquals( + new Type[] {String.class, Number.class}, check("twoInterfaces3", Map.class)); + assertArrayEquals( + new Type[] {Number.class, Integer.class, String.class}, + check("twoInterfaces3", TwoGenericInterfaces.class)); + } + + TwoGenericInterfaces twoInterfaces4; + /** Tests, if we can read the type info of each generic interface. */ + @Test + public void testTwoInterfaces4() throws Exception { + assertArrayEquals(new Type[] {Object.class}, check("twoInterfaces4", Iterator.class)); + assertArrayEquals( + new Type[] {String.class, Number.class}, check("twoInterfaces4", Map.class)); + assertArrayEquals( + new Type[] {Number.class, Object.class, String.class}, + check("twoInterfaces4", TwoGenericInterfaces.class)); + } +} diff --git a/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java b/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java index ac4d94cf6e..4f2913fcc9 100644 --- a/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java +++ b/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java @@ -19,6 +19,7 @@ import org.mozilla.javascript.NativeArray; import org.mozilla.javascript.NativeJavaMethod; import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.Undefined; import org.mozilla.javascript.tools.shell.Global; /** From @makusuko (Markus Sunela), imported from PR https://github.com/mozilla/rhino/pull/561 */ @@ -69,15 +70,23 @@ public void testAccessingJavaMapIntegerValues() { public void testAccessingJavaMapLongValues() { Map map = new HashMap<>(); + map.put(0L, 1); map.put(1L, 2); map.put(2L, 3); assertEquals(2, runScriptAsInt("value[1]", map, true)); assertEquals(3, runScriptAsInt("value[2]", map, true)); + assertEquals(Undefined.instance, runScript("value.foo", map, true)); runScriptAsString("value[4] = 4.01", map, true); - assertEquals(Double.valueOf(4.01), map.get(4)); + runScriptAsString("value[2] = 2.01", map, true); + assertEquals(4.01, map.get(4)); assertEquals(null, map.get(4L)); + assertEquals(null, map.get(4.0D)); + // overwrite existing key. + assertEquals(null, map.get(2)); + assertEquals(2.01, map.get(2L)); + assertEquals(null, map.get(2.0D)); } public void testAccessingJavaMapEnumValuesWithGeneric() { @@ -101,9 +110,9 @@ public void testAccessingJavaMapEnumValuesWithGeneric() { runScriptAsString("value['D'] = 4.0", map, true); fail(); ; - } catch (IllegalArgumentException ex) { + } catch (EvaluatorException ex) { assertEquals( - "No enum constant org.mozilla.javascript.tests.NativeJavaMapTest.MyEnum.D", + "Cannot convert D to org.mozilla.javascript.tests.NativeJavaMapTest$MyEnum (#1)", ex.getMessage()); } } From 7308fb1dc9021cf45d77b7d1274bc32fc6ddc350 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 19 Oct 2021 14:27:34 +0200 Subject: [PATCH 05/14] Fix: SecurityControllerTest --- .../org/mozilla/javascript/tests/SecurityControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsrc/org/mozilla/javascript/tests/SecurityControllerTest.java b/testsrc/org/mozilla/javascript/tests/SecurityControllerTest.java index 3d143e13d3..a28df34cd7 100644 --- a/testsrc/org/mozilla/javascript/tests/SecurityControllerTest.java +++ b/testsrc/org/mozilla/javascript/tests/SecurityControllerTest.java @@ -75,7 +75,7 @@ public void testBarAccess() { String script = "f = new com.example.securitytest.SomeFactory();\n" + "var i = f.create();\n" - + "i.size();\n" + + "i.foo();\n" // i is SomeInterface - you can only access foo() + "i.bar();"; // try in allowed scope From 73fe18d6cff4bee3a094ea3a7fa16cd005c5b687 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Fri, 22 Oct 2021 14:29:30 +0200 Subject: [PATCH 06/14] Added some javadoc --- src/org/mozilla/javascript/NativeJavaMap.java | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/org/mozilla/javascript/NativeJavaMap.java b/src/org/mozilla/javascript/NativeJavaMap.java index e89a20c04a..d27bc8122d 100644 --- a/src/org/mozilla/javascript/NativeJavaMap.java +++ b/src/org/mozilla/javascript/NativeJavaMap.java @@ -166,9 +166,9 @@ public Object[] getIds() { return super.getIds(); } - private static final Map, Function> STRING_CONVERTERS = + public static final Map, Function> STRING_CONVERTERS = new LinkedHashMap<>(); - private static final Map, IntFunction> INT_CONVERTERS = + public static final Map, IntFunction> INT_CONVERTERS = new LinkedHashMap<>(); { @@ -183,7 +183,23 @@ public Object[] getIds() { INT_CONVERTERS.put(Double.class, Double::valueOf); } /** - * Converts the key, which is either as String or an Integer to the `keyType` + * Converts the key, which is either as String or an Integer to the `keyType`. + * + *

    When accessing java lists with javascript notation like var x = map[42] or + * var x = map['key'], the key could be either a string or an integer. There are + * cases where you do not have a Map<String, ?>> or + * Map<Integer, ?>> but a Map<Long, ?>>. In this case, it is + * impossible to access the map value with index based access. + * + *

    The default implementation can handle maps, when key is either an Enum (EnumMap), String, + * Integer, Long or Double. You may add additional converters, e.g. with + * STRING_CONVERTERS.put(UUID.class, UUID::fromString), then you can also use maps, that + * has UUIDs as key. + * + *

    Note 1: Adding new converters is not synchronized + * + *

    Note 2: This conversion takes only place, when FEATURE_ENABLE_JAVA_MAP_ACCESS + * is set in context. * * @return */ From 8371646646f897ffb85eaff019e68621274c7748 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 3 Nov 2021 14:09:21 +0100 Subject: [PATCH 07/14] Extracted key conversion to separate class --- .../mozilla/javascript/MapKeyConverter.java | 36 ++++ src/org/mozilla/javascript/NativeJavaMap.java | 176 +++++++++--------- 2 files changed, 119 insertions(+), 93 deletions(-) create mode 100644 src/org/mozilla/javascript/MapKeyConverter.java diff --git a/src/org/mozilla/javascript/MapKeyConverter.java b/src/org/mozilla/javascript/MapKeyConverter.java new file mode 100644 index 0000000000..6afe6dd8e3 --- /dev/null +++ b/src/org/mozilla/javascript/MapKeyConverter.java @@ -0,0 +1,36 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +package org.mozilla.javascript; + +import java.util.Map; + +/** + * Converter for index based java-map access. + * + *

    When accessing java maps with javascript notation like var x = map[42] or + * var x = map['key'], the key could be either a string or an integer. There are cases where + * you do not have a Map<String, ?>> or + * Map<Integer, ?>> but a Map<Long, ?>>. In this case, it is + * impossible to access the map value with index based access. + * + *

    Note: This conversion takes only place, when FEATURE_ENABLE_JAVA_MAP_ACCESS is + * set in context. + * + * @author Roland Praml, FOCONIS AG + */ +@FunctionalInterface +public interface MapKeyConverter { + + /** + * Converts the key to the keyType. The returned key should be + * compatible with the passed map + * + * @param key the key (could be either String or Integer) + * @param keyType the desired type + * @param map the map. + */ + Object toKey(Object key, Class keyType, Map map); +} diff --git a/src/org/mozilla/javascript/NativeJavaMap.java b/src/org/mozilla/javascript/NativeJavaMap.java index d27bc8122d..a952e089db 100644 --- a/src/org/mozilla/javascript/NativeJavaMap.java +++ b/src/org/mozilla/javascript/NativeJavaMap.java @@ -29,12 +29,17 @@ public class NativeJavaMap extends NativeJavaObject { private static final long serialVersionUID = 46513864372878618L; + private static final MapKeyConverter DEFAULT_MAP_KEY_CONVERTER = + new NativeJavaMapKeyConverter(); + private final Map map; private final Class keyType; private final Class valueType; + private final MapKeyConverter converter; + static void init(ScriptableObject scope, boolean sealed) { NativeJavaMapIterator.init(scope, sealed); } @@ -47,6 +52,8 @@ public NativeJavaMap(Scriptable scope, Object map, Type staticType) { Type[] types = JavaTypes.lookupType(scope, map.getClass(), staticType, Map.class); this.keyType = types == null ? Object.class : JavaTypes.getRawType(types[0]); this.valueType = types == null ? Object.class : JavaTypes.getRawType(types[1]); + // TODO: This should be configurable. (cx.getMapKeyConverter) + this.converter = DEFAULT_MAP_KEY_CONVERTER; } @Override @@ -58,7 +65,7 @@ public String getClassName() { public boolean has(String name, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = toKey(name); + Object key = converter.toKey(name, keyType, map); if (key != null && map.containsKey(key)) { return true; } @@ -70,7 +77,7 @@ public boolean has(String name, Scriptable start) { public boolean has(int index, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = toKey(index); + Object key = converter.toKey(Integer.valueOf(index), keyType, map); if (key != null && map.containsKey(key)) { return true; } @@ -90,7 +97,7 @@ public boolean has(Symbol key, Scriptable start) { public Object get(String name, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = toKey(name); + Object key = converter.toKey(name, keyType, map); if (key != null && map.containsKey(key)) { Object obj = map.get(key); return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); @@ -103,7 +110,7 @@ public Object get(String name, Scriptable start) { public Object get(int index, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = toKey(index); + Object key = converter.toKey(Integer.valueOf(index), keyType, map); if (key != null && map.containsKey(key)) { Object obj = map.get(key); return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); @@ -125,7 +132,7 @@ public Object get(Symbol key, Scriptable start) { public void put(String name, Scriptable start, Object value) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = toKey(name); + Object key = converter.toKey(name, keyType, map); if (key == null) { reportConversionError(name, keyType); } @@ -139,7 +146,7 @@ public void put(String name, Scriptable start, Object value) { public void put(int index, Scriptable start, Object value) { Context cx = Context.getContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = toKey(Integer.valueOf(index)); + Object key = converter.toKey(Integer.valueOf(index), keyType, map); if (key == null) { reportConversionError(Integer.valueOf(index), keyType); } @@ -166,93 +173,6 @@ public Object[] getIds() { return super.getIds(); } - public static final Map, Function> STRING_CONVERTERS = - new LinkedHashMap<>(); - public static final Map, IntFunction> INT_CONVERTERS = - new LinkedHashMap<>(); - - { - STRING_CONVERTERS.put(String.class, Function.identity()); - STRING_CONVERTERS.put(Integer.class, Integer::valueOf); - STRING_CONVERTERS.put(Long.class, Long::valueOf); - STRING_CONVERTERS.put(Double.class, Double::valueOf); - - INT_CONVERTERS.put(String.class, Integer::toString); - INT_CONVERTERS.put(Integer.class, Integer::valueOf); - INT_CONVERTERS.put(Long.class, Long::valueOf); - INT_CONVERTERS.put(Double.class, Double::valueOf); - } - /** - * Converts the key, which is either as String or an Integer to the `keyType`. - * - *

    When accessing java lists with javascript notation like var x = map[42] or - * var x = map['key'], the key could be either a string or an integer. There are - * cases where you do not have a Map<String, ?>> or - * Map<Integer, ?>> but a Map<Long, ?>>. In this case, it is - * impossible to access the map value with index based access. - * - *

    The default implementation can handle maps, when key is either an Enum (EnumMap), String, - * Integer, Long or Double. You may add additional converters, e.g. with - * STRING_CONVERTERS.put(UUID.class, UUID::fromString), then you can also use maps, that - * has UUIDs as key. - * - *

    Note 1: Adding new converters is not synchronized - * - *

    Note 2: This conversion takes only place, when FEATURE_ENABLE_JAVA_MAP_ACCESS - * is set in context. - * - * @return - */ - @SuppressWarnings("unchecked") - protected Object toKey(Object key) { - try { - if (key instanceof String) { - // 1. if we have an enum, try to convert the value - if (keyType.isEnum()) return Enum.valueOf((Class) keyType, (String) key); - - Function converter = STRING_CONVERTERS.get(keyType); - if (converter != null) { - return converter.apply((String) key); - } else { - return findStringKey(key); - } - - } else { // could be either String or Integer - int index = ((Integer) key).intValue(); - IntFunction converter = INT_CONVERTERS.get(keyType); - if (converter != null) { - return converter.apply(index); - } else { - return findIndexKey(index); - } - } - } catch (IllegalArgumentException ex) { - return null; // cannot convert key - } - } - - protected Object findStringKey(Object key) { - for (Function converter : STRING_CONVERTERS.values()) { - try { - Object testKey = converter.apply((String) key); - if (map.containsKey(testKey)) return testKey; - } catch (IllegalArgumentException ex) { - } - } - return key; - } - - protected Object findIndexKey(int index) { - for (IntFunction converter : INT_CONVERTERS.values()) { - try { - Object testKey = converter.apply(index); - if (map.containsKey(testKey)) return testKey; - } catch (IllegalArgumentException ex) { - } - } - return Integer.valueOf(index); - } - private static Callable symbol_iterator = (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> { if (!(thisObj instanceof NativeJavaMap)) { @@ -305,4 +225,74 @@ protected String getTag() { private Iterator> iterator; } + + public static class NativeJavaMapKeyConverter implements MapKeyConverter { + protected static final Map, Function> STRING_CONVERTERS = + new LinkedHashMap<>(); + protected static final Map, IntFunction> INT_CONVERTERS = + new LinkedHashMap<>(); + + { + STRING_CONVERTERS.put(String.class, Function.identity()); + STRING_CONVERTERS.put(Integer.class, Integer::valueOf); + STRING_CONVERTERS.put(Long.class, Long::valueOf); + STRING_CONVERTERS.put(Double.class, Double::valueOf); + + INT_CONVERTERS.put(String.class, Integer::toString); + INT_CONVERTERS.put(Integer.class, Integer::valueOf); + INT_CONVERTERS.put(Long.class, Long::valueOf); + INT_CONVERTERS.put(Double.class, Double::valueOf); + } + + @SuppressWarnings("unchecked") + @Override + public Object toKey(Object key, Class keyType, Map map) { + try { + if (key instanceof String) { + // 1. if we have an enum, try to convert the value + if (keyType.isEnum()) return Enum.valueOf((Class) keyType, (String) key); + + Function converter = STRING_CONVERTERS.get(keyType); + if (converter != null) { + return converter.apply((String) key); + } else { + return findStringKey(map, key); + } + + } else { // could be either String or Integer + int index = ((Integer) key).intValue(); + IntFunction converter = INT_CONVERTERS.get(keyType); + if (converter != null) { + return converter.apply(index); + } else { + return findIndexKey(map, index); + } + } + } catch (IllegalArgumentException ex) { + return null; // cannot convert key + } + } + + protected Object findStringKey(Map map, Object key) { + for (Function converter : STRING_CONVERTERS.values()) { + try { + Object testKey = converter.apply((String) key); + if (map.containsKey(testKey)) return testKey; + } catch (IllegalArgumentException ex) { + } + } + return key; + } + + protected Object findIndexKey(Map map, int index) { + for (IntFunction converter : INT_CONVERTERS.values()) { + try { + Object testKey = converter.apply(index); + if (map.containsKey(testKey)) return testKey; + } catch (IllegalArgumentException ex) { + } + } + return Integer.valueOf(index); + } + } } From 07652551d8a36f6207540be237adb27f26c936ff Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 3 Nov 2021 14:14:32 +0100 Subject: [PATCH 08/14] spotbugs --- src/org/mozilla/javascript/NativeJavaMap.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/mozilla/javascript/NativeJavaMap.java b/src/org/mozilla/javascript/NativeJavaMap.java index a952e089db..68cffc7993 100644 --- a/src/org/mozilla/javascript/NativeJavaMap.java +++ b/src/org/mozilla/javascript/NativeJavaMap.java @@ -227,9 +227,9 @@ protected String getTag() { } public static class NativeJavaMapKeyConverter implements MapKeyConverter { - protected static final Map, Function> STRING_CONVERTERS = + private static final Map, Function> STRING_CONVERTERS = new LinkedHashMap<>(); - protected static final Map, IntFunction> INT_CONVERTERS = + private static final Map, IntFunction> INT_CONVERTERS = new LinkedHashMap<>(); { From afa56633182a1f089ce4f793acb166e7691d48c7 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 3 Nov 2021 15:00:22 +0100 Subject: [PATCH 09/14] Split into two methods --- .../mozilla/javascript/MapKeyConverter.java | 5 ++- src/org/mozilla/javascript/NativeJavaMap.java | 44 ++++++++++--------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/org/mozilla/javascript/MapKeyConverter.java b/src/org/mozilla/javascript/MapKeyConverter.java index 6afe6dd8e3..13c101e8a4 100644 --- a/src/org/mozilla/javascript/MapKeyConverter.java +++ b/src/org/mozilla/javascript/MapKeyConverter.java @@ -21,7 +21,6 @@ * * @author Roland Praml, FOCONIS AG */ -@FunctionalInterface public interface MapKeyConverter { /** @@ -32,5 +31,7 @@ public interface MapKeyConverter { * @param keyType the desired type * @param map the map. */ - Object toKey(Object key, Class keyType, Map map); + Object toKey(String key, Class keyType, Map map); + + Object toKey(int index, Class keyType, Map map); } diff --git a/src/org/mozilla/javascript/NativeJavaMap.java b/src/org/mozilla/javascript/NativeJavaMap.java index 68cffc7993..3282195a3f 100644 --- a/src/org/mozilla/javascript/NativeJavaMap.java +++ b/src/org/mozilla/javascript/NativeJavaMap.java @@ -110,7 +110,7 @@ public Object get(String name, Scriptable start) { public Object get(int index, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = converter.toKey(Integer.valueOf(index), keyType, map); + Object key = converter.toKey(index, keyType, map); if (key != null && map.containsKey(key)) { Object obj = map.get(key); return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); @@ -246,27 +246,29 @@ public static class NativeJavaMapKeyConverter implements MapKeyConverter { @SuppressWarnings("unchecked") @Override - public Object toKey(Object key, Class keyType, Map map) { + public Object toKey(String key, Class keyType, Map map) { + try { + if (keyType.isEnum()) return Enum.valueOf((Class) keyType, (String) key); + + Function converter = STRING_CONVERTERS.get(keyType); + if (converter != null) { + return converter.apply((String) key); + } else { + return findStringKey(map, key); + } + } catch (IllegalArgumentException ex) { + return null; // cannot convert key + } + } + + @Override + public Object toKey(int index, Class keyType, Map map) { try { - if (key instanceof String) { - // 1. if we have an enum, try to convert the value - if (keyType.isEnum()) return Enum.valueOf((Class) keyType, (String) key); - - Function converter = STRING_CONVERTERS.get(keyType); - if (converter != null) { - return converter.apply((String) key); - } else { - return findStringKey(map, key); - } - - } else { // could be either String or Integer - int index = ((Integer) key).intValue(); - IntFunction converter = INT_CONVERTERS.get(keyType); - if (converter != null) { - return converter.apply(index); - } else { - return findIndexKey(map, index); - } + IntFunction converter = INT_CONVERTERS.get(keyType); + if (converter != null) { + return converter.apply(index); + } else { + return findIndexKey(map, index); } } catch (IllegalArgumentException ex) { return null; // cannot convert key From ec72919829939d3c92cf57c9c5b0311add5a554d Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Fri, 5 Nov 2021 11:42:00 +0100 Subject: [PATCH 10/14] FEATURE_ENABLE_JAVA_MAP_ACCESS supports only Map and Map --- .../mozilla/javascript/MapKeyConverter.java | 37 ----- src/org/mozilla/javascript/NativeJavaMap.java | 126 ++++-------------- .../javascript/tests/NativeJavaMapTest.java | 117 ++++++---------- 3 files changed, 62 insertions(+), 218 deletions(-) delete mode 100644 src/org/mozilla/javascript/MapKeyConverter.java diff --git a/src/org/mozilla/javascript/MapKeyConverter.java b/src/org/mozilla/javascript/MapKeyConverter.java deleted file mode 100644 index 13c101e8a4..0000000000 --- a/src/org/mozilla/javascript/MapKeyConverter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mozilla.javascript; - -import java.util.Map; - -/** - * Converter for index based java-map access. - * - *

    When accessing java maps with javascript notation like var x = map[42] or - * var x = map['key'], the key could be either a string or an integer. There are cases where - * you do not have a Map<String, ?>> or - * Map<Integer, ?>> but a Map<Long, ?>>. In this case, it is - * impossible to access the map value with index based access. - * - *

    Note: This conversion takes only place, when FEATURE_ENABLE_JAVA_MAP_ACCESS is - * set in context. - * - * @author Roland Praml, FOCONIS AG - */ -public interface MapKeyConverter { - - /** - * Converts the key to the keyType. The returned key should be - * compatible with the passed map - * - * @param key the key (could be either String or Integer) - * @param keyType the desired type - * @param map the map. - */ - Object toKey(String key, Class keyType, Map map); - - Object toKey(int index, Class keyType, Map map); -} diff --git a/src/org/mozilla/javascript/NativeJavaMap.java b/src/org/mozilla/javascript/NativeJavaMap.java index 3282195a3f..75e912d8b1 100644 --- a/src/org/mozilla/javascript/NativeJavaMap.java +++ b/src/org/mozilla/javascript/NativeJavaMap.java @@ -8,11 +8,8 @@ import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.function.Function; -import java.util.function.IntFunction; /** * NativeJavaMap is a wrapper for java objects implementing java.util.Map @@ -23,23 +20,18 @@ * for .. of. * *

    Limitations: The wrapped map should have String or Integer as - * key. Otherwise, property based access may not work properly. + * key. Otherwise, property based access may not work. */ public class NativeJavaMap extends NativeJavaObject { private static final long serialVersionUID = 46513864372878618L; - private static final MapKeyConverter DEFAULT_MAP_KEY_CONVERTER = - new NativeJavaMapKeyConverter(); - private final Map map; private final Class keyType; private final Class valueType; - private final MapKeyConverter converter; - static void init(ScriptableObject scope, boolean sealed) { NativeJavaMapIterator.init(scope, sealed); } @@ -52,8 +44,6 @@ public NativeJavaMap(Scriptable scope, Object map, Type staticType) { Type[] types = JavaTypes.lookupType(scope, map.getClass(), staticType, Map.class); this.keyType = types == null ? Object.class : JavaTypes.getRawType(types[0]); this.valueType = types == null ? Object.class : JavaTypes.getRawType(types[1]); - // TODO: This should be configurable. (cx.getMapKeyConverter) - this.converter = DEFAULT_MAP_KEY_CONVERTER; } @Override @@ -65,8 +55,7 @@ public String getClassName() { public boolean has(String name, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = converter.toKey(name, keyType, map); - if (key != null && map.containsKey(key)) { + if (map.containsKey(name)) { return true; } } @@ -77,8 +66,7 @@ public boolean has(String name, Scriptable start) { public boolean has(int index, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = converter.toKey(Integer.valueOf(index), keyType, map); - if (key != null && map.containsKey(key)) { + if (map.containsKey(Integer.valueOf(index)) || map.containsKey(String.valueOf(index))) { return true; } } @@ -97,9 +85,8 @@ public boolean has(Symbol key, Scriptable start) { public Object get(String name, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = converter.toKey(name, keyType, map); - if (key != null && map.containsKey(key)) { - Object obj = map.get(key); + if (map.containsKey(name)) { + Object obj = map.get(name); return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); } } @@ -110,8 +97,13 @@ public Object get(String name, Scriptable start) { public Object get(int index, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = converter.toKey(index, keyType, map); - if (key != null && map.containsKey(key)) { + Object key = Integer.valueOf(index); + if (map.containsKey(key)) { + Object obj = map.get(key); + return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); + } + key = String.valueOf(index); // try again with String + if (map.containsKey(key)) { Object obj = map.get(key); return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); } @@ -132,11 +124,11 @@ public Object get(Symbol key, Scriptable start) { public void put(String name, Scriptable start, Object value) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = converter.toKey(name, keyType, map); - if (key == null) { + if (keyType.isAssignableFrom(String.class)) { + map.put(name, Context.jsToJava(value, valueType)); + } else { reportConversionError(name, keyType); } - map.put(key, Context.jsToJava(value, valueType)); } else { super.put(name, start, value); } @@ -146,11 +138,13 @@ public void put(String name, Scriptable start, Object value) { public void put(int index, Scriptable start, Object value) { Context cx = Context.getContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - Object key = converter.toKey(Integer.valueOf(index), keyType, map); - if (key == null) { - reportConversionError(Integer.valueOf(index), keyType); + if (keyType.isAssignableFrom(Integer.class)) { + map.put(Integer.valueOf(index), Context.jsToJava(value, valueType)); + } else if (keyType.isAssignableFrom(String.class)) { + map.put(String.valueOf(index), Context.jsToJava(value, valueType)); + } else { + reportConversionError(index, keyType); } - map.put(key, Context.jsToJava(value, valueType)); } else { super.put(index, start, value); } @@ -164,9 +158,9 @@ public Object[] getIds() { for (Object key : map.keySet()) { if (key instanceof Integer) { ids.add(key); - } else { - ids.add(ScriptRuntime.toString(key)); - } + } else if (key instanceof String) { + ids.add(key); + } // else skip all other types, as you may not able to access them } return ids.toArray(); } @@ -225,76 +219,4 @@ protected String getTag() { private Iterator> iterator; } - - public static class NativeJavaMapKeyConverter implements MapKeyConverter { - private static final Map, Function> STRING_CONVERTERS = - new LinkedHashMap<>(); - private static final Map, IntFunction> INT_CONVERTERS = - new LinkedHashMap<>(); - - { - STRING_CONVERTERS.put(String.class, Function.identity()); - STRING_CONVERTERS.put(Integer.class, Integer::valueOf); - STRING_CONVERTERS.put(Long.class, Long::valueOf); - STRING_CONVERTERS.put(Double.class, Double::valueOf); - - INT_CONVERTERS.put(String.class, Integer::toString); - INT_CONVERTERS.put(Integer.class, Integer::valueOf); - INT_CONVERTERS.put(Long.class, Long::valueOf); - INT_CONVERTERS.put(Double.class, Double::valueOf); - } - - @SuppressWarnings("unchecked") - @Override - public Object toKey(String key, Class keyType, Map map) { - try { - if (keyType.isEnum()) return Enum.valueOf((Class) keyType, (String) key); - - Function converter = STRING_CONVERTERS.get(keyType); - if (converter != null) { - return converter.apply((String) key); - } else { - return findStringKey(map, key); - } - } catch (IllegalArgumentException ex) { - return null; // cannot convert key - } - } - - @Override - public Object toKey(int index, Class keyType, Map map) { - try { - IntFunction converter = INT_CONVERTERS.get(keyType); - if (converter != null) { - return converter.apply(index); - } else { - return findIndexKey(map, index); - } - } catch (IllegalArgumentException ex) { - return null; // cannot convert key - } - } - - protected Object findStringKey(Map map, Object key) { - for (Function converter : STRING_CONVERTERS.values()) { - try { - Object testKey = converter.apply((String) key); - if (map.containsKey(testKey)) return testKey; - } catch (IllegalArgumentException ex) { - } - } - return key; - } - - protected Object findIndexKey(Map map, int index) { - for (IntFunction converter : INT_CONVERTERS.values()) { - try { - Object testKey = converter.apply(index); - if (map.containsKey(testKey)) return testKey; - } catch (IllegalArgumentException ex) { - } - } - return Integer.valueOf(index); - } - } } diff --git a/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java b/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java index 4f2913fcc9..0cec89f9e3 100644 --- a/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java +++ b/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java @@ -8,6 +8,8 @@ import static org.junit.Assert.*; +import java.nio.file.AccessMode; +import java.util.EnumMap; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -19,7 +21,6 @@ import org.mozilla.javascript.NativeArray; import org.mozilla.javascript.NativeJavaMethod; import org.mozilla.javascript.Scriptable; -import org.mozilla.javascript.Undefined; import org.mozilla.javascript.tools.shell.Global; /** From @makusuko (Markus Sunela), imported from PR https://github.com/mozilla/rhino/pull/561 */ @@ -40,15 +41,6 @@ public NativeJavaMapTest() { global.init(ContextFactory.getGlobal()); } - public static enum MyEnum { - A, - B, - C, - X, - Y, - Z - } - public void testAccessingNullValues() { Map map = new HashMap<>(); map.put("a", null); @@ -68,74 +60,6 @@ public void testAccessingJavaMapIntegerValues() { assertEquals(3, runScriptAsInt("value[2]", map, true)); } - public void testAccessingJavaMapLongValues() { - Map map = new HashMap<>(); - - map.put(0L, 1); - map.put(1L, 2); - map.put(2L, 3); - - assertEquals(2, runScriptAsInt("value[1]", map, true)); - assertEquals(3, runScriptAsInt("value[2]", map, true)); - assertEquals(Undefined.instance, runScript("value.foo", map, true)); - runScriptAsString("value[4] = 4.01", map, true); - runScriptAsString("value[2] = 2.01", map, true); - assertEquals(4.01, map.get(4)); - assertEquals(null, map.get(4L)); - assertEquals(null, map.get(4.0D)); - // overwrite existing key. - assertEquals(null, map.get(2)); - assertEquals(2.01, map.get(2L)); - assertEquals(null, map.get(2.0D)); - } - - public void testAccessingJavaMapEnumValuesWithGeneric() { - // genrate inner class, that contains type information. - Map map = - new HashMap() { - private static final long serialVersionUID = 1L; - }; - - map.put(MyEnum.A, 1); - map.put(MyEnum.B, 2); - map.put(MyEnum.C, 3); - - assertEquals(2, runScriptAsInt("value['B']", map, true)); - assertEquals(3, runScriptAsInt("value['C']", map, true)); - runScriptAsString("value['X'] = 4.01", map, true); - // we know the type info and can convert the key to Long and the value is rounded to Integer - assertEquals(Integer.valueOf(4), map.get(MyEnum.X)); - - try { - runScriptAsString("value['D'] = 4.0", map, true); - fail(); - ; - } catch (EvaluatorException ex) { - assertEquals( - "Cannot convert D to org.mozilla.javascript.tests.NativeJavaMapTest$MyEnum (#1)", - ex.getMessage()); - } - } - - public void testAccessingJavaMapLongValuesWithGeneric() { - // genrate inner class, that contains type information. - Map map = - new HashMap() { - private static final long serialVersionUID = 1L; - }; - - map.put(0L, 1); - map.put(1L, 2); - map.put(2L, 3); - - assertEquals(2, runScriptAsInt("value[1]", map, true)); - assertEquals(3, runScriptAsInt("value[2]", map, true)); - runScriptAsInt("value[4] = 4.0", map, true); - // we know the type info and can convert the key to Long and the value to Integer - assertEquals(Integer.valueOf(4), map.get(4L)); - assertEquals(null, map.get(4)); - } - public void testJavaMethodCalls() { Map map = new HashMap<>(); map.put("a", 1); @@ -237,8 +161,43 @@ public void testJavaMapWithoutAccessEntries() { assertEquals(true, runScript("Object.keys(value).includes('getClass')", map, false)); } + public void testStringIntMap() { + Map stringMap = new HashMap() {}; + stringMap.put("42", "foo"); + + Map intMap = new HashMap() {}; + intMap.put(42, "foo"); + + assertEquals("foo", runScriptAsString("value['42']", stringMap, true)); + assertEquals("foo", runScriptAsString("value[42]", stringMap, true)); + assertEquals("42", runScriptAsString("Object.keys(value)", stringMap, true)); + assertEquals("foo", runScriptAsString("value['42']", intMap, true)); + assertEquals("foo", runScriptAsString("value[42]", intMap, true)); + assertEquals("42", runScriptAsString("Object.keys(value)", intMap, true)); + + runScriptAsString("value[43]='bar'", intMap, true); + runScriptAsString("value[43]='bar'", stringMap, true); + assertEquals("bar", intMap.get(43)); + assertEquals("bar", stringMap.get("43")); + } + + public void testEnumMap() { + Map enumMap = new EnumMap(AccessMode.class) {}; + enumMap.put(AccessMode.READ, "foo"); + + assertEquals( + "foo", + runScriptAsString("value.get(java.nio.file.AccessMode.READ)", enumMap, true)); + assertEquals( + "undefined", + runScriptAsString("value[java.nio.file.AccessMode.READ]", enumMap, true)); + assertThrows( + EvaluatorException.class, + () -> runScript("value[java.nio.file.AccessMode.READ] = 'bar'", enumMap, true)); + } + public void testSymbolIterator() { - Map map = new LinkedHashMap(); + Map map = new LinkedHashMap<>(); String script = "var a = [];\n" + "for (var [key, value] of value) a.push(key, value);\n" + "a"; From cd392facfdefccc6ac0093156d6857b449ddf0fe Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Mon, 8 Nov 2021 14:10:31 +0100 Subject: [PATCH 11/14] Changed to cached java type info --- src/org/mozilla/javascript/ClassCache.java | 4 +- src/org/mozilla/javascript/JavaMembers.java | 19 +- src/org/mozilla/javascript/JavaTypeInfo.java | 164 ++++++++++ .../mozilla/javascript/JavaTypeResolver.java | 88 ++++++ src/org/mozilla/javascript/JavaTypes.java | 218 -------------- src/org/mozilla/javascript/MemberBox.java | 13 + .../mozilla/javascript/NativeJavaClass.java | 131 ++++---- .../mozilla/javascript/NativeJavaList.java | 4 +- src/org/mozilla/javascript/NativeJavaMap.java | 5 +- .../mozilla/javascript/NativeJavaMethod.java | 53 ++-- .../mozilla/javascript/NativeJavaObject.java | 16 +- .../javascript/tests/GenericAccessTest.java | 283 ++++++++++++++++++ .../javascript/tests/JavaListAccessTest.java | 105 ------- ...vaTypesTest.java => JavaTypeInfoTest.java} | 103 ++++--- 14 files changed, 714 insertions(+), 492 deletions(-) create mode 100644 src/org/mozilla/javascript/JavaTypeInfo.java create mode 100644 src/org/mozilla/javascript/JavaTypeResolver.java delete mode 100644 src/org/mozilla/javascript/JavaTypes.java create mode 100644 testsrc/org/mozilla/javascript/tests/GenericAccessTest.java delete mode 100644 testsrc/org/mozilla/javascript/tests/JavaListAccessTest.java rename testsrc/org/mozilla/javascript/tests/{JavaTypesTest.java => JavaTypeInfoTest.java} (56%) diff --git a/src/org/mozilla/javascript/ClassCache.java b/src/org/mozilla/javascript/ClassCache.java index cabc7f47c7..68dc0ebe08 100644 --- a/src/org/mozilla/javascript/ClassCache.java +++ b/src/org/mozilla/javascript/ClassCache.java @@ -26,7 +26,7 @@ public class ClassCache implements Serializable { private transient Map classTable; private transient Map> classAdapterCache; private transient Map, Object> interfaceAdapterCache; - private transient Map typeCache; + private transient Map typeCache; private int generatedClassSerial; private Scriptable associatedScope; @@ -150,7 +150,7 @@ Map> getInterfaceAdapterCacheMap() { return classAdapterCache; } - Map getTypeCacheMap() { + Map getTypeCacheMap() { if (typeCache == null) { typeCache = new ConcurrentHashMap<>(16, 0.75f, 1); } diff --git a/src/org/mozilla/javascript/JavaMembers.java b/src/org/mozilla/javascript/JavaMembers.java index 91c9e53cae..c7c3d2fe67 100644 --- a/src/org/mozilla/javascript/JavaMembers.java +++ b/src/org/mozilla/javascript/JavaMembers.java @@ -108,7 +108,13 @@ Object get(Scriptable scope, String name, Object javaObject, boolean isStatic) { return cx.getWrapFactory().wrap(cx, scope, rval, type); } - void put(Scriptable scope, String name, Object javaObject, Object value, boolean isStatic) { + void put( + Scriptable scope, + String name, + Object javaObject, + Object value, + boolean isStatic, + JavaTypeResolver typeResolver) { Map ht = isStatic ? staticMembers : members; Object member = ht.get(name); if (!isStatic && member == null) { @@ -132,6 +138,9 @@ void put(Scriptable scope, String name, Object javaObject, Object value, boolean // setter to use: if (bp.setters == null || value == null) { Class setType = bp.setter.argTypes[0]; + if (typeResolver != null && bp.setter.genericArgTypes != null) { + setType = typeResolver.resolve(bp.setter.genericArgTypes[0]); + } Object[] args = {Context.jsToJava(value, setType)}; try { bp.setter.invoke(javaObject, args); @@ -153,7 +162,13 @@ void put(Scriptable scope, String name, Object javaObject, Object value, boolean throw Context.reportRuntimeErrorById(str, name); } Field field = (Field) member; - Object javaValue = Context.jsToJava(value, field.getType()); + Class fieldType; + if (typeResolver != null) { + fieldType = typeResolver.resolve(field.getGenericType()); + } else { + fieldType = field.getType(); + } + Object javaValue = Context.jsToJava(value, fieldType); try { field.set(javaObject, javaValue); } catch (IllegalAccessException accessEx) { diff --git a/src/org/mozilla/javascript/JavaTypeInfo.java b/src/org/mozilla/javascript/JavaTypeInfo.java new file mode 100644 index 0000000000..1b270468a1 --- /dev/null +++ b/src/org/mozilla/javascript/JavaTypeInfo.java @@ -0,0 +1,164 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.javascript; + +import java.lang.reflect.Array; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Carries the reflection info per JavaObject. + * + * @author Roland Praml, FOCONIS AG + */ +public class JavaTypeInfo { + + public static final JavaTypeInfo xEMPTY = new JavaTypeInfo(); + + private final Map, Type[]> typeCache; + private final Map resolved; + private final Map reverseResolved; + + JavaTypeInfo() { + typeCache = Collections.emptyMap(); + resolved = Collections.emptyMap(); + reverseResolved = Collections.emptyMap(); + } + + JavaTypeInfo(Type type) { + typeCache = new HashMap<>(); + resolved = new HashMap<>(); + reverseResolved = new HashMap<>(); + reflect(type, null); + // remove unnecessary info. + Iterator it = resolved.values().iterator(); + while (it.hasNext()) { + if (it.next() == Object.class) { + // it.remove(); + } + } + } + + /** Returns the resolved type argument for classOfInterest. */ + public Class resolve(Class classOfInterest, int index) { + Type[] entry = typeCache.get(classOfInterest); + if (entry == null) { + return null; + } else { + return getRawType(entry[index]); + } + } + + public Type reverseResolve(Type type) { + return reverseResolved.getOrDefault(type, type); + } + + public Class resolve(Type type) { + Type ret = resolved.get(type); + if (ret instanceof Class) { + return (Class) ret; + } else { + return Object.class; + } + } + + private void reflect(Type type, Type[] typeArgs) { + if (type == null) { + return; + } else if (type instanceof Class) { + Class cls = (Class) type; + TypeVariable[] params = cls.getTypeParameters(); + if (params.length != 0) { + Type[] resolvedParams = new Type[params.length]; + for (int i = 0; i < params.length; i++) { + if (typeArgs == null) { + resolvedParams[i] = params[i]; + } else { + resolvedParams[i] = resolved.getOrDefault(typeArgs[i], typeArgs[i]); + resolved.put(params[i], resolvedParams[i]); + if (resolvedParams[i] instanceof TypeVariable) { + reverseResolved.put(resolvedParams[i], params[i]); + } + } + } + // if (isResolved(resolvedParams)) { + typeCache.put(cls, resolvedParams); + // } + } + for (Type iface : cls.getGenericInterfaces()) { + reflect(iface, null); + } + reflect(cls.getGenericSuperclass(), null); + } else if (type instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) type; + reflect(pt.getRawType(), pt.getActualTypeArguments()); + } + } + + public static JavaTypeInfo get(Scriptable scope, Type type) { + if (type == null) { + return null; + } + ClassCache cache = ClassCache.get(scope); + return cache.getTypeCacheMap().computeIfAbsent(type, JavaTypeInfo::new); + } + + /** returns the raw type. Taken from google guice. */ + public static Class getRawType(Type type) { + if (type == null) { + return null; + + } else if (type instanceof Class) { + // Type is a normal class. + return (Class) type; + + } else if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + return (Class) parameterizedType.getRawType(); + + } else if (type instanceof GenericArrayType) { + Type componentType = ((GenericArrayType) type).getGenericComponentType(); + return Array.newInstance(getRawType(componentType), 0).getClass(); + + } else if (type instanceof WildcardType) { + Type[] bound = ((WildcardType) type).getLowerBounds(); + if (bound.length == 1) { + return getRawType(bound[0]); + } else { + bound = ((WildcardType) type).getUpperBounds(); + if (bound.length == 1) { + return getRawType(bound[0]); + } else { + return Object.class; + } + } + } else if (type instanceof TypeVariable) { + Type[] bound = ((TypeVariable) type).getBounds(); + if (bound.length == 1) { + return getRawType(bound[0]); + } else { + return Object.class; + } + + } else { + String className = type.getClass().getName(); + throw new IllegalArgumentException( + "Expected a Class, " + + "ParameterizedType, or GenericArrayType, but <" + + type + + "> is of type " + + className); + } + } +} diff --git a/src/org/mozilla/javascript/JavaTypeResolver.java b/src/org/mozilla/javascript/JavaTypeResolver.java new file mode 100644 index 0000000000..bbfd082517 --- /dev/null +++ b/src/org/mozilla/javascript/JavaTypeResolver.java @@ -0,0 +1,88 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.javascript; + +import java.lang.reflect.Type; + +/** + * Carries the reflection info per JavaObject. + * + * @author Roland Praml, FOCONIS AG + */ +public class JavaTypeResolver { + + private final JavaTypeInfo staticTypeInfo; + private final JavaTypeInfo dynamicTypeInfo; + private Type st; + private Type dt; + + public JavaTypeResolver(Scriptable scope, Type staticType, Type dynamicType) { + st = staticType; + dt = dynamicType; + staticTypeInfo = JavaTypeInfo.get(scope, staticType); + dynamicTypeInfo = JavaTypeInfo.get(scope, dynamicType); + } + + /** + * Returns the 'best' candidate of the generic type for a given classOfInterest. + * Check both (staticTypeInfo and dynamicTypeInfo) and returns the narrowest type. + */ + public Class resolve(Class classOfInterest, int index) { + Class staticType = null; + if (staticTypeInfo != null) { + staticType = staticTypeInfo.resolve(classOfInterest, index); + } + + Class dynamicType = null; + if (dynamicTypeInfo != null) { + dynamicType = dynamicTypeInfo.resolve(classOfInterest, index); + } + + return narrowType(staticType, dynamicType); + } + + public Class resolve(Type type) { + if (type instanceof Class) { + return (Class) type; + } + Class dynamicType = null; + if (dynamicTypeInfo != null) { + type = dynamicTypeInfo.reverseResolve(type); + dynamicType = dynamicTypeInfo.resolve(type); + } + Class staticType = null; + if (staticTypeInfo != null) { + staticType = staticTypeInfo.resolve(type); + } + Class resolved = narrowType(staticType, dynamicType); + if (resolved == null) { + return JavaTypeInfo.getRawType(type); + } else { + return resolved; + } + } + + private Class narrowType(Class staticType, Class dynamicType) { + if (staticType == null) { + return dynamicType; + } else if (dynamicType == null) { + return staticType; + } else if (staticType.isAssignableFrom(dynamicType)) { + return dynamicType; + } else { + return staticType; + } + } + + public Class[] resolve(Type[] types) { + Class[] ret = new Class[types.length]; + for (int i = 0; i < types.length; i++) { + ret[i] = resolve(types[i]); + } + return ret; + } +} diff --git a/src/org/mozilla/javascript/JavaTypes.java b/src/org/mozilla/javascript/JavaTypes.java deleted file mode 100644 index e8859f2b3a..0000000000 --- a/src/org/mozilla/javascript/JavaTypes.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Licensed Materials - Property of FOCONIS AG - * (C) Copyright FOCONIS AG. - */ - -package org.mozilla.javascript; - -import java.lang.reflect.Array; -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.lang.reflect.WildcardType; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -/** - * Type lookup helper. This is used to read the generic info of Lists / Maps in NativeJavaList/Map. - * - *

    If we have a class that implements List<SomeType>, we need to extract - * SomeType, so that we can convert the value to the appropriate data type. - * - *

    For example, if you have a List<Integer>, you expect, that the list only - * contains integers. But there is no type checking at runtime. Rhino tries to read the type - * information and tries to convert the values with {@link Context#jsToJava(Object, Class)}. - * - *

    Example 1: integerList[1] = '123' will convert the String '123' to an - * integer, so the underlying java list will contain the correct data type. - * - *

    Note 1: Type information is stored only on fields and classes. If you define your - * List<Integer> myList = new ArrayList<>(); as local variable, no type - * information is present, so this feature makes more sense on beans. - * - *

    Note 2: Conversion is limited to "common" datatypes, like String, Integer, Double or - * Long - * - *

    Searching the type can be a complex task (see - * https://www.javacodegeeks.com/2013/12/advanced-java-generics-retreiving-generic-type-arguments.html) - * You might have simple declarations like List<Integer> or - *

  • ArrayList<Integer>. But you may have generic classes that implements - *
  • List<E> and you have to figure out what type E is. - * - * @author Roland Praml, FOCONIS AG - */ -public class JavaTypes { - - private static final Type[] NOT_FOUND = new Type[0]; - - public static Type[] lookupType( - Scriptable scope, Type type1, Type type2, Class classOfInterest) { - ClassCache cache = ClassCache.get(scope); - Map tc = cache.getTypeCacheMap(); - - Type[] types1 = NOT_FOUND; - Type[] types2 = NOT_FOUND; - if (type1 != null) { - types1 = tc.computeIfAbsent(type1, t -> lookupType(t, classOfInterest)); - } - if (type2 != null) { - types2 = tc.computeIfAbsent(type2, t -> lookupType(t, classOfInterest)); - } - if (types1 == NOT_FOUND && types2 == NOT_FOUND) { - return null; - } else if (types1 == NOT_FOUND) { - return types2; - } else if (types2 == NOT_FOUND) { - return types1; - } else { - Type[] ret = new Type[types1.length]; - for (int i = 0; i < types1.length; i++) { - if (getRawType(types1[i]).isAssignableFrom(getRawType(types2[i]))) { - ret[i] = types2[i]; - } else { - ret[i] = types1[i]; - } - } - return ret; - } - } - - private static Type[] lookupType(Type type, Class classOfInterest) { - return lookupType(type, classOfInterest, null); - } - - private static Type[] lookupType( - Type type, Class classOfInterest, Map, Type> typeVarAssigns) { - if (type instanceof ParameterizedType) { - return getParametrizedTypeArguments( - (ParameterizedType) type, classOfInterest, typeVarAssigns); - } else if (type instanceof Class) { - return getClassArguments((Class) type, classOfInterest, typeVarAssigns); - } else { - return NOT_FOUND; - } - } - - private static Type[] getClassArguments( - Class clazz, Class classOfInterest, Map, Type> typeVarAssigns) { - if (!classOfInterest.isAssignableFrom(clazz)) { - return NOT_FOUND; - } - if (typeVarAssigns == null) { - typeVarAssigns = new HashMap<>(); - } - if (classOfInterest.isInterface()) { - for (Type interfaceType : clazz.getGenericInterfaces()) { - Type[] ret = lookupType(interfaceType, classOfInterest, typeVarAssigns); - if (ret != NOT_FOUND) { - return resolveTypes(ret, typeVarAssigns); - } - } - } - Type[] ret = lookupType(clazz.getGenericSuperclass(), classOfInterest, typeVarAssigns); - if (ret == NOT_FOUND) { - ret = new Type[classOfInterest.getTypeParameters().length]; - Arrays.fill(ret, Object.class); - } - return ret; - } - - private static Type[] getParametrizedTypeArguments( - ParameterizedType parameterizedType, - Class classOfInterest, - Map, Type> typeVarAssigns) { - Class clazz = (Class) parameterizedType.getRawType(); - if (clazz == classOfInterest) { - // Type is List and classOfInterest is List - return resolveTypes(parameterizedType.getActualTypeArguments(), typeVarAssigns); - } - if (!classOfInterest.isAssignableFrom(clazz)) { - return NOT_FOUND; - } - if (typeVarAssigns == null) { - typeVarAssigns = new HashMap<>(); - } - // get the subject parameterized type's arguments - final Type[] typeArgs = parameterizedType.getActualTypeArguments(); - // and get the corresponding type variables from the raw class - final TypeVariable[] typeParams = clazz.getTypeParameters(); - - // map the arguments to their respective type variables - for (int i = 0; i < typeParams.length; i++) { - final Type typeArg = typeArgs[i]; - typeVarAssigns.put(typeParams[i], typeVarAssigns.getOrDefault(typeArg, typeArg)); - } - if (classOfInterest.isInterface()) { - for (Type interfaceType : clazz.getGenericInterfaces()) { - Type[] ret = lookupType(interfaceType, classOfInterest, typeVarAssigns); - if (ret != NOT_FOUND) { - return resolveTypes(ret, typeVarAssigns); - } - } - } - return lookupType(clazz.getGenericSuperclass(), classOfInterest, typeVarAssigns); - } - - /** - * @param actualTypeArguments - * @param typeVarAssigns - * @return - */ - private static Type[] resolveTypes(Type[] ret, Map, Type> typeVarAssigns) { - for (int i = 0; i < ret.length; i++) { - if (ret[i] instanceof TypeVariable) { - ret[i] = typeVarAssigns.getOrDefault(ret[i], Object.class); - } else if (ret[i] instanceof WildcardType) { - WildcardType wildcard = (WildcardType) ret[i]; - Type[] bound = wildcard.getLowerBounds(); - if (bound.length > 0) { - ret[i] = bound[0]; - } else { - bound = wildcard.getUpperBounds(); - if (bound.length > 0) { - ret[i] = bound[0]; - } else { - ret[i] = Object.class; - } - } - } - } - return ret; - } - - /** returns the raw type. Taken from google guice. */ - public static Class getRawType(Type type) { - if (type == null) { - return null; - - } else if (type instanceof Class) { - // Type is a normal class. - return (Class) type; - - } else if (type instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) type; - return (Class) parameterizedType.getRawType(); - - } else if (type instanceof GenericArrayType) { - Type componentType = ((GenericArrayType) type).getGenericComponentType(); - return Array.newInstance(getRawType(componentType), 0).getClass(); - - } else if (type instanceof TypeVariable || type instanceof WildcardType) { - // We could use the variable's bounds, but that won't work if there - // are multiple. Having a raw type that's more general than - // necessary is okay. - return Object.class; - - } else { - String className = type.getClass().getName(); - throw new IllegalArgumentException( - "Expected a Class, " - + "ParameterizedType, or GenericArrayType, but <" - + type - + "> is of type " - + className); - } - } -} diff --git a/src/org/mozilla/javascript/MemberBox.java b/src/org/mozilla/javascript/MemberBox.java index b8c95c73ff..37720d55b7 100644 --- a/src/org/mozilla/javascript/MemberBox.java +++ b/src/org/mozilla/javascript/MemberBox.java @@ -15,6 +15,7 @@ import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Type; /** * Wrapper class for Method and Constructor instances to cache getParameterTypes() results, recover @@ -27,6 +28,7 @@ final class MemberBox implements Serializable { private transient Member memberObject; transient Class[] argTypes; + transient Type[] genericArgTypes; transient boolean vararg; transient Function asGetterFunction; @@ -44,15 +46,26 @@ final class MemberBox implements Serializable { private void init(Method method) { this.memberObject = method; this.argTypes = method.getParameterTypes(); + this.genericArgTypes = filterGenerics(method.getGenericParameterTypes()); this.vararg = method.isVarArgs(); } private void init(Constructor constructor) { this.memberObject = constructor; this.argTypes = constructor.getParameterTypes(); + this.genericArgTypes = filterGenerics(constructor.getGenericParameterTypes()); this.vararg = constructor.isVarArgs(); } + private Type[] filterGenerics(Type[] params) { + for (Type param : params) { + if (!(param instanceof Class)) { + return params; + } + } + return null; + } + Method method() { return (Method) memberObject; } diff --git a/src/org/mozilla/javascript/NativeJavaClass.java b/src/org/mozilla/javascript/NativeJavaClass.java index e9601a93b8..c880056cf4 100644 --- a/src/org/mozilla/javascript/NativeJavaClass.java +++ b/src/org/mozilla/javascript/NativeJavaClass.java @@ -11,30 +11,24 @@ import java.util.Map; /** - * This class reflects Java classes into the JavaScript environment, mainly - * for constructors and static members. We lazily reflect properties, - * and currently do not guarantee that a single j.l.Class is only - * reflected once into the JS environment, although we should. - * The only known case where multiple reflections - * are possible occurs when a j.l.Class is wrapped as part of a - * method return or property access, rather than by walking the - * Packages/java tree. + * This class reflects Java classes into the JavaScript environment, mainly for constructors and + * static members. We lazily reflect properties, and currently do not guarantee that a single + * j.l.Class is only reflected once into the JS environment, although we should. The only known case + * where multiple reflections are possible occurs when a j.l.Class is wrapped as part of a method + * return or property access, rather than by walking the Packages/java tree. * * @author Mike Shaver * @see NativeJavaArray * @see NativeJavaObject * @see NativeJavaPackage */ - -public class NativeJavaClass extends NativeJavaObject implements Function -{ +public class NativeJavaClass extends NativeJavaObject implements Function { private static final long serialVersionUID = -6460763940409461664L; // Special property for getting the underlying Java class object. static final String javaClassPropertyName = "__javaObject__"; - public NativeJavaClass() { - } + public NativeJavaClass() {} public NativeJavaClass(Scriptable scope, Class cl) { this(scope, cl, false); @@ -46,7 +40,7 @@ public NativeJavaClass(Scriptable scope, Class cl, boolean isAdapter) { @Override protected void initMembers() { - Class cl = (Class)javaObject; + Class cl = (Class) javaObject; members = JavaMembers.lookupClass(parent, cl, cl, isAdapter); staticFieldAndMethods = members.getFieldAndMethodsObjects(this, cl, true); } @@ -67,13 +61,11 @@ public Object get(String name, Scriptable start) { // for our prototype to create an object of the correct type. // We don't really care what the object is, since we're returning // one constructed out of whole cloth, so we return null. - if (name.equals("prototype")) - return null; + if (name.equals("prototype")) return null; - if (staticFieldAndMethods != null) { + if (staticFieldAndMethods != null) { Object result = staticFieldAndMethods.get(name); - if (result != null) - return result; + if (result != null) return result; } if (members.has(name, true)) { @@ -85,16 +77,14 @@ public Object get(String name, Scriptable start) { WrapFactory wrapFactory = cx.getWrapFactory(); if (javaClassPropertyName.equals(name)) { - return wrapFactory.wrap(cx, scope, javaObject, - ScriptRuntime.ClassClass); + return wrapFactory.wrap(cx, scope, javaObject, ScriptRuntime.ClassClass); } // experimental: look for nested classes by appending $name to // current class' name. Class nestedClass = findNestedClass(getClassObject(), name); if (nestedClass != null) { - Scriptable nestedValue = wrapFactory.wrapJavaClass(cx, scope, - nestedClass); + Scriptable nestedValue = wrapFactory.wrapJavaClass(cx, scope, nestedClass); nestedValue.setParentScope(this); return nestedValue; } @@ -104,7 +94,7 @@ public Object get(String name, Scriptable start) { @Override public void put(String name, Scriptable start, Object value) { - members.put(this, name, javaObject, value, true); + members.put(this, name, javaObject, value, true, null); } @Override @@ -118,19 +108,14 @@ public Class getClassObject() { @Override public Object getDefaultValue(Class hint) { - if (hint == null || hint == ScriptRuntime.StringClass) - return this.toString(); - if (hint == ScriptRuntime.BooleanClass) - return Boolean.TRUE; - if (hint == ScriptRuntime.NumberClass) - return ScriptRuntime.NaNobj; + if (hint == null || hint == ScriptRuntime.StringClass) return this.toString(); + if (hint == ScriptRuntime.BooleanClass) return Boolean.TRUE; + if (hint == ScriptRuntime.NumberClass) return ScriptRuntime.NaNobj; return this; } @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, - Object[] args) - { + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { // If it looks like a "cast" of an object to this class type, // walk the prototype chain to see if there's a wrapper of a // object that's an instanceof this class. @@ -140,8 +125,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, do { if (p instanceof Wrapper) { Object o = ((Wrapper) p).unwrap(); - if (c.isInstance(o)) - return p; + if (c.isInstance(o)) return p; } p = p.getPrototype(); } while (p != null); @@ -150,19 +134,16 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, } @Override - public Scriptable construct(Context cx, Scriptable scope, Object[] args) - { + public Scriptable construct(Context cx, Scriptable scope, Object[] args) { Class classObject = getClassObject(); int modifiers = classObject.getModifiers(); - if (! (Modifier.isInterface(modifiers) || - Modifier.isAbstract(modifiers))) - { + if (!(Modifier.isInterface(modifiers) || Modifier.isAbstract(modifiers))) { NativeJavaMethod ctors = members.ctors; int index = ctors.findCachedFunction(cx, args); if (index < 0) { String sig = NativeJavaMethod.scriptSignature(args); throw Context.reportRuntimeErrorById( - "msg.no.java.ctor", classObject.getName(), sig); + "msg.no.java.ctor", classObject.getName(), sig); } // Found the constructor, so try invoking it. @@ -176,10 +157,10 @@ public Scriptable construct(Context cx, Scriptable scope, Object[] args) try { // When running on Android create an InterfaceAdapter since our // bytecode generation won't work on Dalvik VM. - if ("Dalvik".equals(System.getProperty("java.vm.name")) - && classObject.isInterface()) { - Object obj = createInterfaceAdapter(classObject, - ScriptableObject.ensureScriptableObject(args[0])); + if ("Dalvik".equals(System.getProperty("java.vm.name")) && classObject.isInterface()) { + Object obj = + createInterfaceAdapter( + classObject, ScriptableObject.ensureScriptableObject(args[0])); return cx.getWrapFactory().wrapAsJavaObject(cx, scope, obj, null); } // use JavaAdapter to construct a new class on the fly that @@ -188,22 +169,19 @@ public Scriptable construct(Context cx, Scriptable scope, Object[] args) if (v != NOT_FOUND) { Function f = (Function) v; // Args are (interface, js object) - Object[] adapterArgs = { this, args[0] }; + Object[] adapterArgs = {this, args[0]}; return f.construct(cx, topLevel, adapterArgs); } } catch (Exception ex) { // fall through to error String m = ex.getMessage(); - if (m != null) - msg = m; + if (m != null) msg = m; } - throw Context.reportRuntimeErrorById( - "msg.cant.instantiate", msg, classObject.getName()); + throw Context.reportRuntimeErrorById("msg.cant.instantiate", msg, classObject.getName()); } - static Scriptable constructSpecific(Context cx, Scriptable scope, - Object[] args, MemberBox ctor) - { + static Scriptable constructSpecific( + Context cx, Scriptable scope, Object[] args, MemberBox ctor) { Object instance = constructInternal(args, ctor); // we need to force this to be wrapped, because construct _has_ // to return a scriptable @@ -211,14 +189,13 @@ static Scriptable constructSpecific(Context cx, Scriptable scope, return cx.getWrapFactory().wrapNewObject(cx, topLevel, instance); } - static Object constructInternal(Object[] args, MemberBox ctor) - { + static Object constructInternal(Object[] args, MemberBox ctor) { Class[] argTypes = ctor.argTypes; if (ctor.vararg) { // marshall the explicit parameter Object[] newArgs = new Object[argTypes.length]; - for (int i = 0; i < argTypes.length-1; i++) { + for (int i = 0; i < argTypes.length - 1; i++) { newArgs[i] = Context.jsToJava(args[i], argTypes[i]); } @@ -226,29 +203,24 @@ static Object constructInternal(Object[] args, MemberBox ctor) // Handle special situation where a single variable parameter // is given and it is a Java or ECMA array. - if (args.length == argTypes.length && - (args[args.length-1] == null || - args[args.length-1] instanceof NativeArray || - args[args.length-1] instanceof NativeJavaArray)) - { + if (args.length == argTypes.length + && (args[args.length - 1] == null + || args[args.length - 1] instanceof NativeArray + || args[args.length - 1] instanceof NativeJavaArray)) { // convert the ECMA array into a native array - varArgs = Context.jsToJava(args[args.length-1], - argTypes[argTypes.length - 1]); + varArgs = Context.jsToJava(args[args.length - 1], argTypes[argTypes.length - 1]); } else { // marshall the variable parameter - Class componentType = argTypes[argTypes.length - 1]. - getComponentType(); - varArgs = Array.newInstance(componentType, - args.length - argTypes.length + 1); - for (int i=0; i < Array.getLength(varArgs); i++) { - Object value = Context.jsToJava(args[argTypes.length-1 + i], - componentType); + Class componentType = argTypes[argTypes.length - 1].getComponentType(); + varArgs = Array.newInstance(componentType, args.length - argTypes.length + 1); + for (int i = 0; i < Array.getLength(varArgs); i++) { + Object value = Context.jsToJava(args[argTypes.length - 1 + i], componentType); Array.set(varArgs, i, value); } } // add varargs - newArgs[argTypes.length-1] = varArgs; + newArgs[argTypes.length - 1] = varArgs; // replace the original args with the new one args = newArgs; } else { @@ -274,19 +246,16 @@ public String toString() { } /** - * Determines if prototype is a wrapped Java object and performs - * a Java "instanceof". - * Exception: if value is an instance of NativeJavaClass, it isn't - * considered an instance of the Java class; this forestalls any - * name conflicts between java.lang.Class's methods and the - * static methods exposed by a JavaNativeClass. + * Determines if prototype is a wrapped Java object and performs a Java "instanceof". Exception: + * if value is an instance of NativeJavaClass, it isn't considered an instance of the Java + * class; this forestalls any name conflicts between java.lang.Class's methods and the static + * methods exposed by a JavaNativeClass. */ @Override public boolean hasInstance(Scriptable value) { - if (value instanceof Wrapper && - !(value instanceof NativeJavaClass)) { - Object instance = ((Wrapper)value).unwrap(); + if (value instanceof Wrapper && !(value instanceof NativeJavaClass)) { + Object instance = ((Wrapper) value).unwrap(); return getClassObject().isInstance(instance); } @@ -308,5 +277,5 @@ private static Class findNestedClass(Class parentClass, String name) { return Kit.classOrNull(loader, nestedClassName); } - private Map staticFieldAndMethods; + private Map staticFieldAndMethods; } diff --git a/src/org/mozilla/javascript/NativeJavaList.java b/src/org/mozilla/javascript/NativeJavaList.java index c903fb90ea..d24a3a4f9a 100644 --- a/src/org/mozilla/javascript/NativeJavaList.java +++ b/src/org/mozilla/javascript/NativeJavaList.java @@ -58,9 +58,7 @@ public NativeJavaList(Scriptable scope, Object list, Type staticType) { assert list instanceof List; this.list = (List) list; - Type[] types = JavaTypes.lookupType(scope, list.getClass(), staticType, List.class); - - this.valueType = types == null ? Object.class : JavaTypes.getRawType(types[0]); + this.valueType = typeResolver.resolve(List.class, 0); } @Override diff --git a/src/org/mozilla/javascript/NativeJavaMap.java b/src/org/mozilla/javascript/NativeJavaMap.java index 75e912d8b1..7e07feaec2 100644 --- a/src/org/mozilla/javascript/NativeJavaMap.java +++ b/src/org/mozilla/javascript/NativeJavaMap.java @@ -41,9 +41,8 @@ public NativeJavaMap(Scriptable scope, Object map, Type staticType) { super(scope, map, staticType); assert map instanceof Map; this.map = (Map) map; - Type[] types = JavaTypes.lookupType(scope, map.getClass(), staticType, Map.class); - this.keyType = types == null ? Object.class : JavaTypes.getRawType(types[0]); - this.valueType = types == null ? Object.class : JavaTypes.getRawType(types[1]); + this.keyType = typeResolver.resolve(Map.class, 0); + this.valueType = typeResolver.resolve(Map.class, 1); } @Override diff --git a/src/org/mozilla/javascript/NativeJavaMethod.java b/src/org/mozilla/javascript/NativeJavaMethod.java index 8eefbfa2c5..7592a6cf8f 100644 --- a/src/org/mozilla/javascript/NativeJavaMethod.java +++ b/src/org/mozilla/javascript/NativeJavaMethod.java @@ -136,6 +136,35 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] ar MemberBox meth = methods[index]; Class[] argTypes = meth.argTypes; + Object javaObject; + if (meth.isStatic()) { + javaObject = null; // don't need an object + } else { + Scriptable o = thisObj; + Class c = meth.getDeclaringClass(); + for (; ; ) { + if (o == null) { + throw Context.reportRuntimeErrorById( + "msg.nonjava.method", + getFunctionName(), + ScriptRuntime.toString(thisObj), + c.getName()); + } + if (o instanceof Wrapper) { + javaObject = ((Wrapper) o).unwrap(); + if (c.isInstance(javaObject)) { + if (o instanceof NativeJavaObject) { + JavaTypeResolver tr = ((NativeJavaObject) o).getTypeResolver(); + if (tr != null && meth.genericArgTypes != null) { + argTypes = tr.resolve(meth.genericArgTypes); + } + } + break; + } + } + o = o.getPrototype(); + } + } if (meth.vararg) { // marshall the explicit parameters @@ -182,29 +211,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] ar } } } - Object javaObject; - if (meth.isStatic()) { - javaObject = null; // don't need an object - } else { - Scriptable o = thisObj; - Class c = meth.getDeclaringClass(); - for (; ; ) { - if (o == null) { - throw Context.reportRuntimeErrorById( - "msg.nonjava.method", - getFunctionName(), - ScriptRuntime.toString(thisObj), - c.getName()); - } - if (o instanceof Wrapper) { - javaObject = ((Wrapper) o).unwrap(); - if (c.isInstance(javaObject)) { - break; - } - } - o = o.getPrototype(); - } - } + if (debug) { printDebug("Calling ", meth, args); } diff --git a/src/org/mozilla/javascript/NativeJavaObject.java b/src/org/mozilla/javascript/NativeJavaObject.java index 88b3ef79b0..f784b00ee9 100644 --- a/src/org/mozilla/javascript/NativeJavaObject.java +++ b/src/org/mozilla/javascript/NativeJavaObject.java @@ -48,8 +48,10 @@ public NativeJavaObject( Scriptable scope, Object javaObject, Type staticType, boolean isAdapter) { this.parent = scope; this.javaObject = javaObject; - this.staticRawType = JavaTypes.getRawType(staticType); + this.staticRawType = JavaTypeInfo.getRawType(staticType); this.isAdapter = isAdapter; + this.typeResolver = new JavaTypeResolver(scope, staticType, javaObject.getClass()); + initMembers(); } @@ -115,7 +117,7 @@ public void put(String name, Scriptable start, Object value) { // prototype. Since we can't add a property to a Java object, // we modify it in the prototype rather than copy it down. if (prototype == null || members.has(name, false)) - members.put(this, name, javaObject, value, false); + members.put(this, name, javaObject, value, false, typeResolver); else prototype.put(name, prototype, value); } @@ -126,7 +128,7 @@ public void put(Symbol symbol, Scriptable start, Object value) { // we modify it in the prototype rather than copy it down. String name = symbol.toString(); if (prototype == null || members.has(name, false)) { - members.put(this, name, javaObject, value, false); + members.put(this, name, javaObject, value, false, typeResolver); } else if (prototype instanceof SymbolScriptable) { ((SymbolScriptable) prototype).put(symbol, prototype, value); } @@ -184,6 +186,11 @@ public Object[] getIds() { return members.getIds(false); } + /** returns the current type resolver for generic method access */ + public JavaTypeResolver getTypeResolver() { + return typeResolver; + } + /** * @deprecated Use {@link Context#getWrapFactory()} together with calling {@link * WrapFactory#wrap(Context, Scriptable, Object, Type)} @@ -943,6 +950,9 @@ protected String getTag() { protected transient JavaMembers members; private transient Map fieldAndMethods; protected transient boolean isAdapter; + // TODO: There is currently no serialization/deserialization support + // as this may break existing serialized content + protected transient JavaTypeResolver typeResolver; private static final Object COERCED_INTERFACE_KEY = "Coerced Interface"; private static Method adapter_writeAdapterObject; diff --git a/testsrc/org/mozilla/javascript/tests/GenericAccessTest.java b/testsrc/org/mozilla/javascript/tests/GenericAccessTest.java new file mode 100644 index 0000000000..7be5a60126 --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/GenericAccessTest.java @@ -0,0 +1,283 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.javascript.tests; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import junit.framework.TestCase; +import org.junit.Test; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ContextFactory; +import org.mozilla.javascript.ScriptableObject; + +/* + * This testcase tests the basic access to java List and Map with [] + */ +public class GenericAccessTest extends TestCase { + + @Test + public void testBeanAccess() { + String js = + "bean.integers[0] = 3;\n" + + "bean.doubles[0] = 3;" + + "bean.doubles[0].getClass().getSimpleName() + ' ' " + + "+ bean.integers[0].getClass().getSimpleName()\n"; + testIt(js, "Double Integer"); + } + + @Test + public void testListAccess() { + String js = + "intList[0] = 3;\n" + + "dblList[0] = 3;" + + "dblList[0].getClass().getSimpleName() + ' ' " + + "+ intList[0].getClass().getSimpleName()\n"; + testIt(js, "Double Integer"); + } + + @Test + public void testIntListIncrement() { + String js = + "intList[0] = 3.5;\n" + + "intList[0]++;\n" + + "intList[0].getClass().getSimpleName() + ' ' + intList[0]\n"; + testIt(js, "Integer 4"); + } + + @Test + public void testDblListIncrement() { + String js = + "dblList[0] = 3.5;\n" + + "dblList[0]++;\n" + + "dblList[0].getClass().getSimpleName() + ' ' + dblList[0]\n"; + testIt(js, "Double 4.5"); + } + + @Test + public void testListAdd() { + String js = + "intList.add(3);\n" + + "dblList.add(3);" + + "dblList[0].getClass().getSimpleName() + ' ' " + + "+ intList[0].getClass().getSimpleName()\n"; + testIt(js, "Double Integer"); + } + + @Test + public void testBeanListAdd() { + String js = + "bean.integers.add(3);\n" + + "bean.doubles.add(3);" + + "bean.doubles[0].getClass().getSimpleName() + ' ' " + + "+ bean.integers[0].getClass().getSimpleName()\n"; + testIt(js, "Double Integer"); + } + + @Test + public void testGenericProperty() { + String js = + "bean.intBean1.value = 3;\n" + + "bean.dblBean1.value = 3;\n" + + "bean.intBean2.value = 3;\n" + + "bean.dblBean2.value = 3;\n" + + "bean.intBean1.value.getClass().getSimpleName() + ' ' + " + + "bean.dblBean1.value.getClass().getSimpleName() + ' ' + " + + "bean.intBean2.value.getClass().getSimpleName() + ' ' + " + + "bean.dblBean2.value.getClass().getSimpleName()"; + testIt(js, "Integer Double Integer Double"); + } + + @Test + public void testGenericPropertyNoSetter() { + String js = + "bean.intBean1.publicValue = 3;\n" + + "bean.dblBean1.publicValue = 3;\n" + + "bean.intBean2.publicValue = 3;\n" + + "bean.dblBean2.publicValue = 3;\n" + + "bean.intBean1.publicValue.getClass().getSimpleName() + ' ' + " + + "bean.dblBean1.publicValue.getClass().getSimpleName() + ' ' + " + + "bean.intBean2.publicValue.getClass().getSimpleName() + ' ' + " + + "bean.dblBean2.publicValue.getClass().getSimpleName()"; + testIt(js, "Integer Double Integer Double"); + } + + @Test + public void testGenericMultipleSetters() { + String js = + "bean.intBean1.valueMultipleSetters = 3;\n" + + "bean.dblBean1.valueMultipleSetters = 3;\n" + + "bean.intBean2.valueMultipleSetters = 3;\n" + + "bean.dblBean2.valueMultipleSetters = 3;\n" + + "bean.intBean1.valueMultipleSetters.getClass().getSimpleName() + ' ' + " + + "bean.dblBean1.valueMultipleSetters.getClass().getSimpleName() + ' ' + " + + "bean.intBean2.valueMultipleSetters.getClass().getSimpleName() + ' ' + " + + "bean.dblBean2.valueMultipleSetters.getClass().getSimpleName()"; + testIt(js, "Integer Double Integer Double"); + } + + @Test + public void testGenericSetter() { + String js = + "bean.intBean1.setValue(3);\n" + + "bean.dblBean1.setValue(3);\n" + + "bean.intBean2.setValue(3);\n" + + "bean.dblBean2.setValue(3);\n" + + "bean.intBean1.value.getClass().getSimpleName() + ' ' + " + + "bean.dblBean1.value.getClass().getSimpleName() + ' ' + " + + "bean.intBean2.value.getClass().getSimpleName() + ' ' + " + + "bean.dblBean2.value.getClass().getSimpleName()"; + testIt(js, "Integer Double Integer Double"); + } + + // Test what happens, when writing and reading to map + private static final String TEST_MAP_INT = + "m[4] = 2;\n" + + "var key = m.keySet().iterator().next();\n" + + "var value = m.values().iterator().next();\n" + + "key.getClass().getSimpleName() + ' ' + key + ' ' + value.getClass().getSimpleName() + ' ' + value + ' ' + m[4]"; + + private static final String TEST_MAP_STRING = + "m['foo'] = 'bar';\n" + + "var key = m.keySet().iterator().next();\n" + + "var value = m.values().iterator().next();\n" + + "key.getClass().getSimpleName() + ' ' + key + ' ' + value.getClass().getSimpleName() + ' ' + value + ' ' + m['foo']"; + + @Test + public void testStringString() { + String js = "var m = bean.stringStringMap;\n" + TEST_MAP_INT; + testIt(js, "String 4 String 2 2"); + js = "var m = bean.stringStringMap;\n" + TEST_MAP_STRING; + testIt(js, "String foo String bar bar"); + } + + @Test + public void testIntStringMap1() { + String js = "var m = bean.intStringMap;\n" + TEST_MAP_INT; + testIt(js, "Integer 4 String 2 2"); + } + + @Test + public void testIntStringMapWriteStringKey() { + String js = + "var m = bean.intStringMap;\n" + + "try { " + + TEST_MAP_STRING + + "} catch (e) { e.toString() }"; + testIt( + js, + "InternalError: Cannot convert foo to java.lang.Integer (GenericAccessTest.js#2)"); + } + + @Test + public void testIntIntMap() { + String js = "var m = bean.intIntMap;\n" + TEST_MAP_INT; + testIt(js, "Integer 4 Integer 2 2"); + } + + @Test + public void testIntLongMap() { + String js = "var m = bean.intLongMap;\n" + TEST_MAP_INT; + testIt(js, "Integer 4 Long 2 2"); + } + + public static class Bean { + public List integers = new ArrayList<>(); + private List doubles = new ArrayList<>(); + + public List getDoubles() { + return doubles; + } + + public List numbers = new ArrayList<>(); + + public Map stringStringMap = new HashMap<>(); + public Map intStringMap = new HashMap<>(); + public Map intIntMap = new HashMap<>(); + public Map intLongMap = new HashMap<>(); + public GenericBean intBean1 = new GenericBean<>(); + public GenericBean dblBean1 = new GenericBean<>(); + public GenericBean intBean2 = new GenericBean() {}; + public GenericBean dblBean2 = new GenericBean() {}; + } + + public static class GenericBean { + private M value; + + public M publicValue; + + private M valueMultipleSetters; + + public M getValue() { + return value; + } + + public void setValue(M value) { + this.value = value; + } + + public M getValueMultipleSetters() { + return valueMultipleSetters; + } + + public void setValueMultipleSetters(M valueMultipleSetters) { + this.valueMultipleSetters = valueMultipleSetters; + } + + public void setValueMultipleSetters(String s) { + throw new UnsupportedOperationException("Should not be called"); + } + } + + private List createIntegerList() { + List list = new ArrayList() {}; + + list.add(42); + list.add(7); + return list; + } + + private List createDoubleList() { + List list = new ArrayList() {}; + + list.add(42.5); + list.add(7.5); + return list; + } + + private List createNumberList() { + List list = new ArrayList() {}; + + list.add(42); + list.add(7.5); + return list; + } + + private void testIt(String script, String expected) { + Utils.runWithAllOptimizationLevels( + new ContextFactory() { + @Override + protected boolean hasFeature(Context cx, int featureIndex) { + switch (featureIndex) { + case Context.FEATURE_ENABLE_JAVA_MAP_ACCESS: + return true; + } + return super.hasFeature(cx, featureIndex); + } + }, + cx -> { + final ScriptableObject scope = cx.initStandardObjects(); + scope.put("intList", scope, createIntegerList()); + scope.put("dblList", scope, createDoubleList()); + scope.put("numList", scope, createNumberList()); + scope.put("bean", scope, new Bean()); + Object o = cx.evaluateString(scope, script, "GenericAccessTest.js", 1, null); + assertEquals(expected, o); + + return null; + }); + } +} diff --git a/testsrc/org/mozilla/javascript/tests/JavaListAccessTest.java b/testsrc/org/mozilla/javascript/tests/JavaListAccessTest.java deleted file mode 100644 index a3da30a278..0000000000 --- a/testsrc/org/mozilla/javascript/tests/JavaListAccessTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.javascript.tests; - -import java.util.ArrayList; -import java.util.List; -import junit.framework.TestCase; -import org.junit.Test; -import org.mozilla.javascript.ScriptableObject; - -/* - * This testcase tests the basic access to java List with [] - */ -public class JavaListAccessTest extends TestCase { - - @Test - public void testBeanAccess() { - String js = - "bean.integers[0] = 3;\n" - + "bean.doubles[0] = 3;" - + "bean.doubles[0].getClass().getSimpleName() + ' ' " - + "+ bean.integers[0].getClass().getSimpleName()\n"; - testIt(js, "Double Integer"); - } - - @Test - public void testListAccess() { - String js = - "intList[0] = 3;\n" - + "dblList[0] = 3;" - + "dblList[0].getClass().getSimpleName() + ' ' " - + "+ intList[0].getClass().getSimpleName()\n"; - testIt(js, "Double Integer"); - } - - @Test - public void testIntListIncrement() { - String js = - "intList[0] = 3.5;\n" - + "intList[0]++;\n" - + "intList[0].getClass().getSimpleName() + ' ' + intList[0]\n"; - testIt(js, "Integer 4"); - } - - @Test - public void testDblListIncrement() { - String js = - "dblList[0] = 3.5;\n" - + "dblList[0]++;\n" - + "dblList[0].getClass().getSimpleName() + ' ' + dblList[0]\n"; - testIt(js, "Double 4.5"); - } - - public static class Bean { - public List integers = new ArrayList<>(); - private List doubles = new ArrayList<>(); - - public List getDoubles() { - return doubles; - } - - public List numbers = new ArrayList<>(); - } - - private List createIntegerList() { - List list = new ArrayList() {}; - - list.add(42); - list.add(7); - return list; - } - - private List createDoubleList() { - List list = new ArrayList() {}; - - list.add(42.5); - list.add(7.5); - return list; - } - - private List createNumberList() { - List list = new ArrayList() {}; - - list.add(42); - list.add(7.5); - return list; - } - - private void testIt(String script, String expected) { - Utils.runWithAllOptimizationLevels( - cx -> { - final ScriptableObject scope = cx.initStandardObjects(); - scope.put("intList", scope, createIntegerList()); - scope.put("dblList", scope, createDoubleList()); - scope.put("numList", scope, createNumberList()); - scope.put("bean", scope, new Bean()); - Object o = cx.evaluateString(scope, script, "testJavaArrayIterate.js", 1, null); - assertEquals(expected, o); - - return null; - }); - } -} diff --git a/testsrc/org/mozilla/javascript/tests/JavaTypesTest.java b/testsrc/org/mozilla/javascript/tests/JavaTypeInfoTest.java similarity index 56% rename from testsrc/org/mozilla/javascript/tests/JavaTypesTest.java rename to testsrc/org/mozilla/javascript/tests/JavaTypeInfoTest.java index 7a6327736d..c65ff3089f 100644 --- a/testsrc/org/mozilla/javascript/tests/JavaTypesTest.java +++ b/testsrc/org/mozilla/javascript/tests/JavaTypeInfoTest.java @@ -6,7 +6,7 @@ */ package org.mozilla.javascript.tests; -import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import java.lang.reflect.Type; import java.util.ArrayList; @@ -16,46 +16,45 @@ import java.util.Map; import org.junit.Test; import org.mozilla.javascript.ClassCache; -import org.mozilla.javascript.JavaTypes; +import org.mozilla.javascript.JavaTypeResolver; import org.mozilla.javascript.NativeObject; import org.mozilla.javascript.ScriptableObject; /** - * Test the type resolver. + * Test the java type info resolver. * * @author Roland Praml, FOCONIS AG */ @SuppressWarnings({"rawtypes", "serial"}) -public class JavaTypesTest { +public class JavaTypeInfoTest { private Type getType(String name) throws Exception { return getClass().getDeclaredField(name).getGenericType(); } - private Type[] check(String name, Class interestingClass) throws Exception { + private JavaTypeResolver check(String name) throws Exception { Object value = getClass().getDeclaredField(name).get(this); ScriptableObject scope = new NativeObject(); new ClassCache().associate(scope); - return JavaTypes.lookupType( - scope, value == null ? null : value.getClass(), getType(name), interestingClass); + return new JavaTypeResolver(scope, value == null ? null : value.getClass(), getType(name)); } List list1; /** Tests, if we can read type arguments from a normal List<String>. */ @Test public void testList1() throws Exception { - assertArrayEquals(new Type[] {String.class}, check("list1", List.class)); - assertArrayEquals(new Type[] {String.class}, check("list1", Collection.class)); - assertArrayEquals(null, check("list1", ArrayList.class)); + assertEquals(String.class, check("list1").resolve(List.class, 0)); + assertEquals(String.class, check("list1").resolve(Collection.class, 0)); + assertEquals(null, check("list1").resolve(ArrayList.class, 0)); } List> list2; /** Tests, if we can read special generic type arguments like List<List<String>>. */ @Test public void testList2() throws Exception { - assertArrayEquals(new Type[] {getType("list1")}, check("list2", List.class)); - assertArrayEquals(new Type[] {getType("list1")}, check("list2", Collection.class)); - assertArrayEquals(null, check("list2", ArrayList.class)); + assertEquals(List.class, check("list2").resolve(List.class, 0)); + assertEquals(List.class, check("list2").resolve(Collection.class, 0)); + assertEquals(null, check("list2").resolve(ArrayList.class, 0)); } ArrayList list3; @@ -63,7 +62,7 @@ public void testList2() throws Exception { /** Tests, if we can read type arguments if it is a class instead of an interface. */ @Test public void testList3() throws Exception { - assertArrayEquals(new Type[] {String.class}, check("list3", List.class)); + assertEquals(String.class, check("list3").resolve(List.class, 0)); } // some test classes @@ -75,7 +74,7 @@ static class TestList4 extends ArrayList { /** Tests, if we can read type argument, if class inherits from a generic class. */ @Test public void testList4() throws Exception { - assertArrayEquals(new Type[] {String.class}, check("list4", List.class)); + assertEquals(String.class, check("list4").resolve(List.class, 0)); } static class TestList5A extends ArrayList { @@ -90,7 +89,7 @@ static class TestList5 extends TestList5A { /** Tests, if we can read type arguments, if inheritance chain introduces new type arguments. */ @Test public void testList5() throws Exception { - assertArrayEquals(new Type[] {String.class}, check("list5", List.class)); + assertEquals(String.class, check("list5").resolve(List.class, 0)); } static class TestList6 extends TestList5A @@ -102,74 +101,74 @@ static class TestList6 extends TestList5A /** Tests, if we can read type arguments, if wildcards are involved. */ @Test public void testList6() throws Exception { - assertArrayEquals(new Type[] {String.class}, check("list6", List.class)); + assertEquals(String.class, check("list6").resolve(List.class, 0)); } List list7; /** Tests, if we can read type arguments, if wildcards are involved. */ @Test public void testList7() throws Exception { - assertArrayEquals(new Type[] {Object.class}, check("list7", List.class)); - assertArrayEquals(null, check("list7", Map.class)); + assertEquals(Object.class, check("list7").resolve(List.class, 0)); + assertEquals(null, check("list7").resolve(Map.class, 0)); } List list8; /** Tests, if we can read type arguments, if raw types are involved. */ @Test public void testList8() throws Exception { - assertArrayEquals(new Type[] {Object.class}, check("list8", List.class)); - assertArrayEquals(null, check("list8", Map.class)); + assertEquals(Object.class, check("list8").resolve(List.class, 0)); + assertEquals(null, check("list8").resolve(Map.class, 0)); } List list9; /** Tests, if we can read wildcard type arguments with lowerBound. */ @Test public void testList9() throws Exception { - assertArrayEquals(new Type[] {Number.class}, check("list9", List.class)); - assertArrayEquals(null, check("list9", Map.class)); + assertEquals(Number.class, check("list9").resolve(List.class, 0)); + assertEquals(null, check("list9").resolve(Map.class, 0)); } List list10; /** Tests, if we can read wildcard type arguments with upperBound. */ @Test public void testList10() throws Exception { - assertArrayEquals(new Type[] {Number.class}, check("list10", List.class)); - assertArrayEquals(null, check("list10", Map.class)); + assertEquals(Number.class, check("list10").resolve(List.class, 0)); + assertEquals(null, check("list10").resolve(Map.class, 0)); } List list11; /** Tests, if we can read array types. */ @Test public void testList11() throws Exception { - assertArrayEquals(new Type[] {new Number[0].getClass()}, check("list11", List.class)); + assertEquals(new Number[0].getClass(), check("list11").resolve(List.class, 0)); } List list12 = new ArrayList() {}; /** Tests, if we take the "best" type, if there are different possibilities. */ @Test public void testList12() throws Exception { - assertArrayEquals(new Type[] {String.class}, check("list12", List.class)); + assertEquals(String.class, check("list12").resolve(List.class, 0)); } Object list13 = new ArrayList() {}; /** Tests, if we take the "best" type, if there are different possibilities. */ @Test public void testList13() throws Exception { - assertArrayEquals(new Type[] {String.class}, check("list13", List.class)); + assertEquals(String.class, check("list13").resolve(List.class, 0)); } List list14 = new ArrayList() {}; /** Tests, if we take the "best" type, if there are different possibilities. */ @Test public void testList14() throws Exception { - assertArrayEquals(new Type[] {Integer.class}, check("list14", List.class)); + assertEquals(Integer.class, check("list14").resolve(List.class, 0)); } List list15 = new ArrayList() {}; /** Tests, if we take the "best" type, if there are different possibilities. */ @Test public void testList15() throws Exception { - assertArrayEquals(new Type[] {Integer.class}, check("list15", List.class)); + assertEquals(Integer.class, check("list15").resolve(List.class, 0)); } abstract static class TwoInterfaces implements Iterator, Map {} @@ -178,10 +177,10 @@ abstract static class TwoInterfaces implements Iterator, Map implements Iterator, Map {} @@ -190,35 +189,35 @@ abstract static class TwoGenericInterfaces implements Iterator, Map< /** Tests, if we can read the type info of each generic interface. */ @Test public void testTwoInterfaces2() throws Exception { - assertArrayEquals(new Type[] {Integer.class}, check("twoInterfaces2", Iterator.class)); - assertArrayEquals( - new Type[] {String.class, Double.class}, check("twoInterfaces2", Map.class)); - assertArrayEquals( - new Type[] {Double.class, Integer.class, String.class}, - check("twoInterfaces2", TwoGenericInterfaces.class)); + assertEquals(Integer.class, check("twoInterfaces2").resolve(Iterator.class, 0)); + assertEquals(String.class, check("twoInterfaces2").resolve(Map.class, 0)); + assertEquals(Double.class, check("twoInterfaces2").resolve(Map.class, 1)); + assertEquals(Double.class, check("twoInterfaces2").resolve(TwoGenericInterfaces.class, 0)); + assertEquals(Integer.class, check("twoInterfaces2").resolve(TwoGenericInterfaces.class, 1)); + assertEquals(String.class, check("twoInterfaces2").resolve(TwoGenericInterfaces.class, 2)); } TwoGenericInterfaces twoInterfaces3; /** Tests, if we can read the type info of each generic interface. */ @Test public void testTwoInterfaces3() throws Exception { - assertArrayEquals(new Type[] {Integer.class}, check("twoInterfaces3", Iterator.class)); - assertArrayEquals( - new Type[] {String.class, Number.class}, check("twoInterfaces3", Map.class)); - assertArrayEquals( - new Type[] {Number.class, Integer.class, String.class}, - check("twoInterfaces3", TwoGenericInterfaces.class)); + assertEquals(Integer.class, check("twoInterfaces3").resolve(Iterator.class, 0)); + assertEquals(String.class, check("twoInterfaces3").resolve(Map.class, 0)); + assertEquals(Number.class, check("twoInterfaces3").resolve(Map.class, 1)); + assertEquals(Number.class, check("twoInterfaces3").resolve(TwoGenericInterfaces.class, 0)); + assertEquals(Integer.class, check("twoInterfaces3").resolve(TwoGenericInterfaces.class, 1)); + assertEquals(String.class, check("twoInterfaces3").resolve(TwoGenericInterfaces.class, 2)); } TwoGenericInterfaces twoInterfaces4; /** Tests, if we can read the type info of each generic interface. */ @Test public void testTwoInterfaces4() throws Exception { - assertArrayEquals(new Type[] {Object.class}, check("twoInterfaces4", Iterator.class)); - assertArrayEquals( - new Type[] {String.class, Number.class}, check("twoInterfaces4", Map.class)); - assertArrayEquals( - new Type[] {Number.class, Object.class, String.class}, - check("twoInterfaces4", TwoGenericInterfaces.class)); + assertEquals(Object.class, check("twoInterfaces4").resolve(Iterator.class, 0)); + assertEquals(String.class, check("twoInterfaces4").resolve(Map.class, 0)); + assertEquals(Number.class, check("twoInterfaces4").resolve(Map.class, 1)); + assertEquals(Number.class, check("twoInterfaces4").resolve(TwoGenericInterfaces.class, 0)); + assertEquals(Object.class, check("twoInterfaces4").resolve(TwoGenericInterfaces.class, 1)); + assertEquals(String.class, check("twoInterfaces4").resolve(TwoGenericInterfaces.class, 2)); } } From 9fe289014a491a65810a7454991abd40bb941337 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Mon, 8 Nov 2021 16:19:20 +0100 Subject: [PATCH 12/14] reviewed code & javadoc --- src/org/mozilla/javascript/JavaMembers.java | 4 + src/org/mozilla/javascript/JavaTypeInfo.java | 56 +++++--- .../mozilla/javascript/JavaTypeResolver.java | 33 +++-- .../mozilla/javascript/NativeJavaClass.java | 131 +++++++++++------- .../mozilla/javascript/NativeJavaObject.java | 4 +- 5 files changed, 142 insertions(+), 86 deletions(-) diff --git a/src/org/mozilla/javascript/JavaMembers.java b/src/org/mozilla/javascript/JavaMembers.java index c7c3d2fe67..400b41b6bd 100644 --- a/src/org/mozilla/javascript/JavaMembers.java +++ b/src/org/mozilla/javascript/JavaMembers.java @@ -108,6 +108,10 @@ Object get(Scriptable scope, String name, Object javaObject, boolean isStatic) { return cx.getWrapFactory().wrap(cx, scope, rval, type); } + void put(Scriptable scope, String name, Object javaObject, Object value, boolean isStatic) { + put(scope, name, javaObject, value, isStatic, null); + } + void put( Scriptable scope, String name, diff --git a/src/org/mozilla/javascript/JavaTypeInfo.java b/src/org/mozilla/javascript/JavaTypeInfo.java index 1b270468a1..37945832ae 100644 --- a/src/org/mozilla/javascript/JavaTypeInfo.java +++ b/src/org/mozilla/javascript/JavaTypeInfo.java @@ -12,45 +12,59 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; -import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; /** * Carries the reflection info per JavaObject. * + *

    For example, if we take ArrayList, we have the following inheritance + * + *

      + *
    • ArrayList<E> extends AbstractList<E> implements + * List<E> + *
    • AbstractList<E> extends AbstractCollection<E> + * implements List<E> + *
    • AbstractCollection<E> implements Collection<E> + *
    • Collection<E> extends Iterable<E> + *
    • Iterable<T> + *
    + * + * we have to walk trough the inheritance tree and build a graph, how the genericTypes (here E + * and T) are linked together. + * + *

    For the generic type declaration ArrayList>String< we can query which + * resolved type arguments each superClass or superInterface has. In this example + * resolve(Iterator.class, 0) will return String.class. + * + *

    When determining method parameters (for example the generic parameter of List.add(M) + * is M) {@link #resolve(Type)} and {@link #reverseResolve(Type)} are used to + * determine the correct type. (in this case String.class) + * + *

    Some ideas are taken from + * https://www.javacodegeeks.com/2013/12/advanced-java-generics-retreiving-generic-type-arguments.html + * * @author Roland Praml, FOCONIS AG */ public class JavaTypeInfo { - public static final JavaTypeInfo xEMPTY = new JavaTypeInfo(); - private final Map, Type[]> typeCache; private final Map resolved; private final Map reverseResolved; - JavaTypeInfo() { - typeCache = Collections.emptyMap(); - resolved = Collections.emptyMap(); - reverseResolved = Collections.emptyMap(); - } - JavaTypeInfo(Type type) { typeCache = new HashMap<>(); resolved = new HashMap<>(); reverseResolved = new HashMap<>(); reflect(type, null); - // remove unnecessary info. - Iterator it = resolved.values().iterator(); - while (it.hasNext()) { - if (it.next() == Object.class) { - // it.remove(); - } - } } - /** Returns the resolved type argument for classOfInterest. */ + /** + * Returns the resolved type argument for classOfInterest. + * + * @param classOfInterest a superClass or interface of the representing type. + * @param index the generic index of classOfIntererest + */ public Class resolve(Class classOfInterest, int index) { Type[] entry = typeCache.get(classOfInterest); if (entry == null) { @@ -69,7 +83,7 @@ public Class resolve(Type type) { if (ret instanceof Class) { return (Class) ret; } else { - return Object.class; + return null; } } @@ -85,6 +99,8 @@ private void reflect(Type type, Type[] typeArgs) { if (typeArgs == null) { resolvedParams[i] = params[i]; } else { + // build a map how generic type parameters are linked over various + // subclasses. resolvedParams[i] = resolved.getOrDefault(typeArgs[i], typeArgs[i]); resolved.put(params[i], resolvedParams[i]); if (resolvedParams[i] instanceof TypeVariable) { @@ -92,9 +108,7 @@ private void reflect(Type type, Type[] typeArgs) { } } } - // if (isResolved(resolvedParams)) { typeCache.put(cls, resolvedParams); - // } } for (Type iface : cls.getGenericInterfaces()) { reflect(iface, null); diff --git a/src/org/mozilla/javascript/JavaTypeResolver.java b/src/org/mozilla/javascript/JavaTypeResolver.java index bbfd082517..bac7779e6d 100644 --- a/src/org/mozilla/javascript/JavaTypeResolver.java +++ b/src/org/mozilla/javascript/JavaTypeResolver.java @@ -9,7 +9,10 @@ import java.lang.reflect.Type; /** - * Carries the reflection info per JavaObject. + * Carries the reflection info for a {@link NativeJavaObject}. + * + *

    This class holds as well the staticTypeInfo (left side) and the dynamicTypeInfo (right side) + * for each java type. * * @author Roland Praml, FOCONIS AG */ @@ -17,12 +20,8 @@ public class JavaTypeResolver { private final JavaTypeInfo staticTypeInfo; private final JavaTypeInfo dynamicTypeInfo; - private Type st; - private Type dt; public JavaTypeResolver(Scriptable scope, Type staticType, Type dynamicType) { - st = staticType; - dt = dynamicType; staticTypeInfo = JavaTypeInfo.get(scope, staticType); dynamicTypeInfo = JavaTypeInfo.get(scope, dynamicType); } @@ -44,7 +43,21 @@ public Class resolve(Class classOfInterest, int index) { return narrowType(staticType, dynamicType); } + /** resolves multiple types (e.g. method arguments) */ + public Class[] resolve(Type[] types) { + Class[] ret = new Class[types.length]; + for (int i = 0; i < types.length; i++) { + ret[i] = resolve(types[i]); + } + return ret; + } + /** + * Resolves given type and returns a concrete class. + * + * @param type type is normally a generic type + * @return the 'best' type. + */ public Class resolve(Type type) { if (type instanceof Class) { return (Class) type; @@ -62,7 +75,7 @@ public Class resolve(Type type) { if (resolved == null) { return JavaTypeInfo.getRawType(type); } else { - return resolved; + return narrowType(resolved, JavaTypeInfo.getRawType(type)); } } @@ -77,12 +90,4 @@ private Class narrowType(Class staticType, Class dynamicType) { return staticType; } } - - public Class[] resolve(Type[] types) { - Class[] ret = new Class[types.length]; - for (int i = 0; i < types.length; i++) { - ret[i] = resolve(types[i]); - } - return ret; - } } diff --git a/src/org/mozilla/javascript/NativeJavaClass.java b/src/org/mozilla/javascript/NativeJavaClass.java index c880056cf4..e9601a93b8 100644 --- a/src/org/mozilla/javascript/NativeJavaClass.java +++ b/src/org/mozilla/javascript/NativeJavaClass.java @@ -11,24 +11,30 @@ import java.util.Map; /** - * This class reflects Java classes into the JavaScript environment, mainly for constructors and - * static members. We lazily reflect properties, and currently do not guarantee that a single - * j.l.Class is only reflected once into the JS environment, although we should. The only known case - * where multiple reflections are possible occurs when a j.l.Class is wrapped as part of a method - * return or property access, rather than by walking the Packages/java tree. + * This class reflects Java classes into the JavaScript environment, mainly + * for constructors and static members. We lazily reflect properties, + * and currently do not guarantee that a single j.l.Class is only + * reflected once into the JS environment, although we should. + * The only known case where multiple reflections + * are possible occurs when a j.l.Class is wrapped as part of a + * method return or property access, rather than by walking the + * Packages/java tree. * * @author Mike Shaver * @see NativeJavaArray * @see NativeJavaObject * @see NativeJavaPackage */ -public class NativeJavaClass extends NativeJavaObject implements Function { + +public class NativeJavaClass extends NativeJavaObject implements Function +{ private static final long serialVersionUID = -6460763940409461664L; // Special property for getting the underlying Java class object. static final String javaClassPropertyName = "__javaObject__"; - public NativeJavaClass() {} + public NativeJavaClass() { + } public NativeJavaClass(Scriptable scope, Class cl) { this(scope, cl, false); @@ -40,7 +46,7 @@ public NativeJavaClass(Scriptable scope, Class cl, boolean isAdapter) { @Override protected void initMembers() { - Class cl = (Class) javaObject; + Class cl = (Class)javaObject; members = JavaMembers.lookupClass(parent, cl, cl, isAdapter); staticFieldAndMethods = members.getFieldAndMethodsObjects(this, cl, true); } @@ -61,11 +67,13 @@ public Object get(String name, Scriptable start) { // for our prototype to create an object of the correct type. // We don't really care what the object is, since we're returning // one constructed out of whole cloth, so we return null. - if (name.equals("prototype")) return null; + if (name.equals("prototype")) + return null; - if (staticFieldAndMethods != null) { + if (staticFieldAndMethods != null) { Object result = staticFieldAndMethods.get(name); - if (result != null) return result; + if (result != null) + return result; } if (members.has(name, true)) { @@ -77,14 +85,16 @@ public Object get(String name, Scriptable start) { WrapFactory wrapFactory = cx.getWrapFactory(); if (javaClassPropertyName.equals(name)) { - return wrapFactory.wrap(cx, scope, javaObject, ScriptRuntime.ClassClass); + return wrapFactory.wrap(cx, scope, javaObject, + ScriptRuntime.ClassClass); } // experimental: look for nested classes by appending $name to // current class' name. Class nestedClass = findNestedClass(getClassObject(), name); if (nestedClass != null) { - Scriptable nestedValue = wrapFactory.wrapJavaClass(cx, scope, nestedClass); + Scriptable nestedValue = wrapFactory.wrapJavaClass(cx, scope, + nestedClass); nestedValue.setParentScope(this); return nestedValue; } @@ -94,7 +104,7 @@ public Object get(String name, Scriptable start) { @Override public void put(String name, Scriptable start, Object value) { - members.put(this, name, javaObject, value, true, null); + members.put(this, name, javaObject, value, true); } @Override @@ -108,14 +118,19 @@ public Class getClassObject() { @Override public Object getDefaultValue(Class hint) { - if (hint == null || hint == ScriptRuntime.StringClass) return this.toString(); - if (hint == ScriptRuntime.BooleanClass) return Boolean.TRUE; - if (hint == ScriptRuntime.NumberClass) return ScriptRuntime.NaNobj; + if (hint == null || hint == ScriptRuntime.StringClass) + return this.toString(); + if (hint == ScriptRuntime.BooleanClass) + return Boolean.TRUE; + if (hint == ScriptRuntime.NumberClass) + return ScriptRuntime.NaNobj; return this; } @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + { // If it looks like a "cast" of an object to this class type, // walk the prototype chain to see if there's a wrapper of a // object that's an instanceof this class. @@ -125,7 +140,8 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] ar do { if (p instanceof Wrapper) { Object o = ((Wrapper) p).unwrap(); - if (c.isInstance(o)) return p; + if (c.isInstance(o)) + return p; } p = p.getPrototype(); } while (p != null); @@ -134,16 +150,19 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] ar } @Override - public Scriptable construct(Context cx, Scriptable scope, Object[] args) { + public Scriptable construct(Context cx, Scriptable scope, Object[] args) + { Class classObject = getClassObject(); int modifiers = classObject.getModifiers(); - if (!(Modifier.isInterface(modifiers) || Modifier.isAbstract(modifiers))) { + if (! (Modifier.isInterface(modifiers) || + Modifier.isAbstract(modifiers))) + { NativeJavaMethod ctors = members.ctors; int index = ctors.findCachedFunction(cx, args); if (index < 0) { String sig = NativeJavaMethod.scriptSignature(args); throw Context.reportRuntimeErrorById( - "msg.no.java.ctor", classObject.getName(), sig); + "msg.no.java.ctor", classObject.getName(), sig); } // Found the constructor, so try invoking it. @@ -157,10 +176,10 @@ public Scriptable construct(Context cx, Scriptable scope, Object[] args) { try { // When running on Android create an InterfaceAdapter since our // bytecode generation won't work on Dalvik VM. - if ("Dalvik".equals(System.getProperty("java.vm.name")) && classObject.isInterface()) { - Object obj = - createInterfaceAdapter( - classObject, ScriptableObject.ensureScriptableObject(args[0])); + if ("Dalvik".equals(System.getProperty("java.vm.name")) + && classObject.isInterface()) { + Object obj = createInterfaceAdapter(classObject, + ScriptableObject.ensureScriptableObject(args[0])); return cx.getWrapFactory().wrapAsJavaObject(cx, scope, obj, null); } // use JavaAdapter to construct a new class on the fly that @@ -169,19 +188,22 @@ public Scriptable construct(Context cx, Scriptable scope, Object[] args) { if (v != NOT_FOUND) { Function f = (Function) v; // Args are (interface, js object) - Object[] adapterArgs = {this, args[0]}; + Object[] adapterArgs = { this, args[0] }; return f.construct(cx, topLevel, adapterArgs); } } catch (Exception ex) { // fall through to error String m = ex.getMessage(); - if (m != null) msg = m; + if (m != null) + msg = m; } - throw Context.reportRuntimeErrorById("msg.cant.instantiate", msg, classObject.getName()); + throw Context.reportRuntimeErrorById( + "msg.cant.instantiate", msg, classObject.getName()); } - static Scriptable constructSpecific( - Context cx, Scriptable scope, Object[] args, MemberBox ctor) { + static Scriptable constructSpecific(Context cx, Scriptable scope, + Object[] args, MemberBox ctor) + { Object instance = constructInternal(args, ctor); // we need to force this to be wrapped, because construct _has_ // to return a scriptable @@ -189,13 +211,14 @@ static Scriptable constructSpecific( return cx.getWrapFactory().wrapNewObject(cx, topLevel, instance); } - static Object constructInternal(Object[] args, MemberBox ctor) { + static Object constructInternal(Object[] args, MemberBox ctor) + { Class[] argTypes = ctor.argTypes; if (ctor.vararg) { // marshall the explicit parameter Object[] newArgs = new Object[argTypes.length]; - for (int i = 0; i < argTypes.length - 1; i++) { + for (int i = 0; i < argTypes.length-1; i++) { newArgs[i] = Context.jsToJava(args[i], argTypes[i]); } @@ -203,24 +226,29 @@ static Object constructInternal(Object[] args, MemberBox ctor) { // Handle special situation where a single variable parameter // is given and it is a Java or ECMA array. - if (args.length == argTypes.length - && (args[args.length - 1] == null - || args[args.length - 1] instanceof NativeArray - || args[args.length - 1] instanceof NativeJavaArray)) { + if (args.length == argTypes.length && + (args[args.length-1] == null || + args[args.length-1] instanceof NativeArray || + args[args.length-1] instanceof NativeJavaArray)) + { // convert the ECMA array into a native array - varArgs = Context.jsToJava(args[args.length - 1], argTypes[argTypes.length - 1]); + varArgs = Context.jsToJava(args[args.length-1], + argTypes[argTypes.length - 1]); } else { // marshall the variable parameter - Class componentType = argTypes[argTypes.length - 1].getComponentType(); - varArgs = Array.newInstance(componentType, args.length - argTypes.length + 1); - for (int i = 0; i < Array.getLength(varArgs); i++) { - Object value = Context.jsToJava(args[argTypes.length - 1 + i], componentType); + Class componentType = argTypes[argTypes.length - 1]. + getComponentType(); + varArgs = Array.newInstance(componentType, + args.length - argTypes.length + 1); + for (int i=0; i < Array.getLength(varArgs); i++) { + Object value = Context.jsToJava(args[argTypes.length-1 + i], + componentType); Array.set(varArgs, i, value); } } // add varargs - newArgs[argTypes.length - 1] = varArgs; + newArgs[argTypes.length-1] = varArgs; // replace the original args with the new one args = newArgs; } else { @@ -246,16 +274,19 @@ public String toString() { } /** - * Determines if prototype is a wrapped Java object and performs a Java "instanceof". Exception: - * if value is an instance of NativeJavaClass, it isn't considered an instance of the Java - * class; this forestalls any name conflicts between java.lang.Class's methods and the static - * methods exposed by a JavaNativeClass. + * Determines if prototype is a wrapped Java object and performs + * a Java "instanceof". + * Exception: if value is an instance of NativeJavaClass, it isn't + * considered an instance of the Java class; this forestalls any + * name conflicts between java.lang.Class's methods and the + * static methods exposed by a JavaNativeClass. */ @Override public boolean hasInstance(Scriptable value) { - if (value instanceof Wrapper && !(value instanceof NativeJavaClass)) { - Object instance = ((Wrapper) value).unwrap(); + if (value instanceof Wrapper && + !(value instanceof NativeJavaClass)) { + Object instance = ((Wrapper)value).unwrap(); return getClassObject().isInstance(instance); } @@ -277,5 +308,5 @@ private static Class findNestedClass(Class parentClass, String name) { return Kit.classOrNull(loader, nestedClassName); } - private Map staticFieldAndMethods; + private Map staticFieldAndMethods; } diff --git a/src/org/mozilla/javascript/NativeJavaObject.java b/src/org/mozilla/javascript/NativeJavaObject.java index f784b00ee9..37be9c43b9 100644 --- a/src/org/mozilla/javascript/NativeJavaObject.java +++ b/src/org/mozilla/javascript/NativeJavaObject.java @@ -50,7 +50,9 @@ public NativeJavaObject( this.javaObject = javaObject; this.staticRawType = JavaTypeInfo.getRawType(staticType); this.isAdapter = isAdapter; - this.typeResolver = new JavaTypeResolver(scope, staticType, javaObject.getClass()); + this.typeResolver = + new JavaTypeResolver( + scope, staticType, javaObject == null ? null : javaObject.getClass()); initMembers(); } From bf76e9afe866989ace8415c0d1a3828e6aad9839 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 9 Nov 2021 12:03:34 +0100 Subject: [PATCH 13/14] Fix problems with serialization --- .../mozilla/javascript/NativeJavaList.java | 8 +- src/org/mozilla/javascript/NativeJavaMap.java | 13 +- .../mozilla/javascript/NativeJavaObject.java | 92 ++++++++-- .../javascript/tests/GenericAccessTest.java | 157 ++++++++++++++++-- 4 files changed, 232 insertions(+), 38 deletions(-) diff --git a/src/org/mozilla/javascript/NativeJavaList.java b/src/org/mozilla/javascript/NativeJavaList.java index d24a3a4f9a..4c87929a25 100644 --- a/src/org/mozilla/javascript/NativeJavaList.java +++ b/src/org/mozilla/javascript/NativeJavaList.java @@ -46,18 +46,22 @@ */ public class NativeJavaList extends NativeJavaObject { - private static final long serialVersionUID = 6403865639690547921L; + private static final long serialVersionUID = 660285467829047519L; protected final List list; - protected final Class valueType; + protected transient Class valueType; @SuppressWarnings("unchecked") public NativeJavaList(Scriptable scope, Object list, Type staticType) { super(scope, list, staticType); assert list instanceof List; this.list = (List) list; + } + @Override + protected void initMembers() { + super.initMembers(); this.valueType = typeResolver.resolve(List.class, 0); } diff --git a/src/org/mozilla/javascript/NativeJavaMap.java b/src/org/mozilla/javascript/NativeJavaMap.java index 7e07feaec2..a5a7d93246 100644 --- a/src/org/mozilla/javascript/NativeJavaMap.java +++ b/src/org/mozilla/javascript/NativeJavaMap.java @@ -20,17 +20,17 @@ * for .. of. * *

    Limitations: The wrapped map should have String or Integer as - * key. Otherwise, property based access may not work. + * key. Otherwise, property based access may not work properly. */ public class NativeJavaMap extends NativeJavaObject { - private static final long serialVersionUID = 46513864372878618L; + private static final long serialVersionUID = -3786257752907047381L; private final Map map; - private final Class keyType; + private transient Class keyType; - private final Class valueType; + private transient Class valueType; static void init(ScriptableObject scope, boolean sealed) { NativeJavaMapIterator.init(scope, sealed); @@ -41,6 +41,11 @@ public NativeJavaMap(Scriptable scope, Object map, Type staticType) { super(scope, map, staticType); assert map instanceof Map; this.map = (Map) map; + } + + @Override + protected void initMembers() { + super.initMembers(); this.keyType = typeResolver.resolve(Map.class, 0); this.valueType = typeResolver.resolve(Map.class, 1); } diff --git a/src/org/mozilla/javascript/NativeJavaObject.java b/src/org/mozilla/javascript/NativeJavaObject.java index 37be9c43b9..4c88dd6c60 100644 --- a/src/org/mozilla/javascript/NativeJavaObject.java +++ b/src/org/mozilla/javascript/NativeJavaObject.java @@ -13,6 +13,7 @@ import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Date; import java.util.Iterator; @@ -48,23 +49,23 @@ public NativeJavaObject( Scriptable scope, Object javaObject, Type staticType, boolean isAdapter) { this.parent = scope; this.javaObject = javaObject; - this.staticRawType = JavaTypeInfo.getRawType(staticType); + this.staticType = staticType; this.isAdapter = isAdapter; - this.typeResolver = - new JavaTypeResolver( - scope, staticType, javaObject == null ? null : javaObject.getClass()); initMembers(); } protected void initMembers() { - Class dynamicType; + Type dynamicType; if (javaObject != null) { dynamicType = javaObject.getClass(); } else { - dynamicType = staticRawType; + dynamicType = staticType; } - members = JavaMembers.lookupClass(parent, dynamicType, staticRawType, isAdapter); + this.typeResolver = new JavaTypeResolver(parent, staticType, dynamicType); + Class dynamicRawType = JavaTypeInfo.getRawType(dynamicType); + Class staticRawType = JavaTypeInfo.getRawType(staticType); + members = JavaMembers.lookupClass(parent, dynamicRawType, staticRawType, isAdapter); fieldAndMethods = members.getFieldAndMethodsObjects(this, javaObject, false); } @@ -851,8 +852,24 @@ private void writeObject(ObjectOutputStream out) throws IOException { out.writeObject(javaObject); } - if (staticRawType != null) { - out.writeObject(staticRawType.getName()); + if (staticType != null) { + StringBuilder sb = new StringBuilder(JavaTypeInfo.getRawType(staticType).getName()); + if (staticType instanceof ParameterizedType) { + // unfortunately, ParameterizedTypes are not serializable. in this case we serialize + // the first level of arguments and append it to the class name. + // This is sufficient for most use cases. See SimpleParameterizedTypeImpl for + // details + sb.append('<'); + Type[] args = ((ParameterizedType) staticType).getActualTypeArguments(); + for (int i = 0; i < args.length; i++) { + if (i > 0) { + sb.append(','); + } + sb.append(JavaTypeInfo.getRawType(args[i]).getName()); + } + sb.append('>'); + } + out.writeObject(sb.toString()); } else { out.writeObject(null); } @@ -873,17 +890,64 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE } else { javaObject = in.readObject(); } - String className = (String) in.readObject(); if (className != null) { - staticRawType = Class.forName(className); + int pos = className.indexOf('<'); + if (pos == -1) { + staticType = Class.forName(className); + } else { + staticType = new SimpleParameterizedTypeImpl(className); + } } else { - staticRawType = null; + staticType = null; } initMembers(); } + /** + * This is a very simple representation that enables deserialization of simple generic types. + * + *

    it supports only the first level, so java.util.List<java.util.Map> is + * supported, but + * java.util.List<java.util.Map<java.lang.String, java.lang.String>> not. + * + * @author Roland Praml, FOCONIS AG + */ + private static class SimpleParameterizedTypeImpl implements ParameterizedType { + + private final Class rawType; + private final Type[] actualTypeArguments; + + private SimpleParameterizedTypeImpl(String typeDef) throws ClassNotFoundException { + // This is a very simple parser. It expects a typeDef in the format + // "ClassName" + int open = typeDef.indexOf('<'); + int close = typeDef.lastIndexOf('>'); + rawType = Class.forName(typeDef.substring(0, open)); + String[] args = typeDef.substring(open + 1, close).split(","); + actualTypeArguments = new Type[args.length]; + for (int i = 0; i < args.length; i++) { + actualTypeArguments[i] = Class.forName(args[i]); + } + } + + @Override + public Type[] getActualTypeArguments() { + return actualTypeArguments.clone(); + } + + @Override + public Type getRawType() { + return rawType; + } + + @Override + public Type getOwnerType() { + return null; // not supported + } + } + private static Callable symbol_iterator = (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> { if (!(thisObj instanceof NativeJavaObject)) { @@ -948,12 +1012,10 @@ protected String getTag() { protected transient Object javaObject; - protected transient Class staticRawType; + protected transient Type staticType; protected transient JavaMembers members; private transient Map fieldAndMethods; protected transient boolean isAdapter; - // TODO: There is currently no serialization/deserialization support - // as this may break existing serialized content protected transient JavaTypeResolver typeResolver; private static final Object COERCED_INTERFACE_KEY = "Coerced Interface"; diff --git a/testsrc/org/mozilla/javascript/tests/GenericAccessTest.java b/testsrc/org/mozilla/javascript/tests/GenericAccessTest.java index 7be5a60126..d0ece65080 100644 --- a/testsrc/org/mozilla/javascript/tests/GenericAccessTest.java +++ b/testsrc/org/mozilla/javascript/tests/GenericAccessTest.java @@ -4,6 +4,12 @@ package org.mozilla.javascript.tests; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -12,7 +18,11 @@ import org.junit.Test; import org.mozilla.javascript.Context; import org.mozilla.javascript.ContextFactory; +import org.mozilla.javascript.NativeJavaObject; +import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; +import org.mozilla.javascript.serialize.ScriptableInputStream; +import org.mozilla.javascript.serialize.ScriptableOutputStream; /* * This testcase tests the basic access to java List and Map with [] @@ -184,7 +194,9 @@ public void testIntLongMap() { testIt(js, "Integer 4 Long 2 2"); } - public static class Bean { + public static class Bean implements Serializable { + private static final long serialVersionUID = 1L; + public List integers = new ArrayList<>(); private List doubles = new ArrayList<>(); @@ -198,13 +210,17 @@ public List getDoubles() { public Map intStringMap = new HashMap<>(); public Map intIntMap = new HashMap<>(); public Map intLongMap = new HashMap<>(); + // beans with typeInfo in the static type public GenericBean intBean1 = new GenericBean<>(); public GenericBean dblBean1 = new GenericBean<>(); - public GenericBean intBean2 = new GenericBean() {}; - public GenericBean dblBean2 = new GenericBean() {}; + // beans with typeInfo in the dynamic type + public GenericBean intBean2 = new IntegerGenericBean(); + public GenericBean dblBean2 = new DoubleGenericBean(); } - public static class GenericBean { + public static class GenericBean implements Serializable { + private static final long serialVersionUID = 1L; + private M value; public M publicValue; @@ -232,8 +248,28 @@ public void setValueMultipleSetters(String s) { } } + static class IntegerArrayList extends ArrayList { + private static final long serialVersionUID = 1L; + } + + static class DoubleArrayList extends ArrayList { + private static final long serialVersionUID = 1L; + } + + static class NumberArrayList extends ArrayList { + private static final long serialVersionUID = 1L; + } + + static class IntegerGenericBean extends GenericBean { + private static final long serialVersionUID = 1L; + } + + static class DoubleGenericBean extends GenericBean { + private static final long serialVersionUID = 1L; + } + private List createIntegerList() { - List list = new ArrayList() {}; + List list = new IntegerArrayList(); list.add(42); list.add(7); @@ -241,7 +277,7 @@ private List createIntegerList() { } private List createDoubleList() { - List list = new ArrayList() {}; + List list = new DoubleArrayList(); list.add(42.5); list.add(7.5); @@ -249,25 +285,29 @@ private List createDoubleList() { } private List createNumberList() { - List list = new ArrayList() {}; + List list = new NumberArrayList(); list.add(42); list.add(7.5); return list; } + private ContextFactory getContextFactory() { + return new ContextFactory() { + @Override + protected boolean hasFeature(Context cx, int featureIndex) { + switch (featureIndex) { + case Context.FEATURE_ENABLE_JAVA_MAP_ACCESS: + return true; + } + return super.hasFeature(cx, featureIndex); + } + }; + } + private void testIt(String script, String expected) { Utils.runWithAllOptimizationLevels( - new ContextFactory() { - @Override - protected boolean hasFeature(Context cx, int featureIndex) { - switch (featureIndex) { - case Context.FEATURE_ENABLE_JAVA_MAP_ACCESS: - return true; - } - return super.hasFeature(cx, featureIndex); - } - }, + getContextFactory(), cx -> { final ScriptableObject scope = cx.initStandardObjects(); scope.put("intList", scope, createIntegerList()); @@ -280,4 +320,87 @@ protected boolean hasFeature(Context cx, int featureIndex) { return null; }); } + + @Test + public void testSerDeser() { + Utils.runWithAllOptimizationLevels( + getContextFactory(), + cx -> { + + // do some tests when we do a deserialization -> serialization roundtrip + // while dynamic type info is stored in the wrapped object itself, + // we have to do some extra effort to store also static type information + ScriptableObject scope = cx.initStandardObjects(); + scope.put("bean", scope, new Bean()); + + // store 3 into value. We expect that 3 is converted to the generic type. + String testCode = "value = 3; value.getClass().getSimpleName()"; + doTest(cx, scope, testCode, "bean.intBean1", Integer.class); + doTest(cx, scope, testCode, "bean.intBean2", Integer.class); + doTest(cx, scope, testCode, "bean.dblBean1", Double.class); + doTest(cx, scope, testCode, "bean.dblBean2", Double.class); + + // perform test also for index based list access + testCode = "this[0] = 3; this[0].getClass().getSimpleName()"; + doTest(cx, scope, testCode, "bean.integers", Integer.class); + doTest(cx, scope, testCode, "bean.doubles", Double.class); + doTest(cx, scope, testCode, "bean.numbers", Double.class); + + // and for method access + testCode = "this.add(0, 3); this[0].getClass().getSimpleName()"; + doTest(cx, scope, testCode, "bean.integers", Integer.class); + doTest(cx, scope, testCode, "bean.doubles", Double.class); + doTest(cx, scope, testCode, "bean.numbers", Double.class); + + // and for maps + testCode = "this[0] = 3; this[0].getClass().getSimpleName()"; + doTest(cx, scope, testCode, "bean.intStringMap", String.class); + doTest(cx, scope, testCode, "bean.intIntMap", Integer.class); + doTest(cx, scope, testCode, "bean.intLongMap", Long.class); + return null; + }); + } + + private void doTest( + Context cx, + ScriptableObject scope, + String testCode, + String beanPath, + Class expectedType) { + + // first step, extract a NativeJavaObject (with type information) + Object o = cx.evaluateString(scope, beanPath, "testSerDeser", 1, null); + NativeJavaObject wrappedBean = (NativeJavaObject) o; + + o = cx.evaluateString(wrappedBean, testCode, "testSerDeser", 1, null); + o = Context.jsToJava(o, String.class); + assertEquals(expectedType.getSimpleName(), o); + + wrappedBean = serDeser(wrappedBean, scope); + + // try again after deserialization + o = cx.evaluateString(wrappedBean, testCode, "testSerDeser", 1, null); + o = Context.jsToJava(o, String.class); + + assertEquals(expectedType.getSimpleName(), o); + } + + @SuppressWarnings("unchecked") + private T serDeser(T element, Scriptable scope) { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ScriptableOutputStream(baos, scope)) { + oos.writeObject(element); + } catch (IOException e) { + throw new RuntimeException(e); + } + + // deserialize + try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ScriptableInputStream(bais, scope); ) { + return (T) ois.readObject(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } From 104dc078de4721721eaf7f4532a3eda0839cfaf7 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Thu, 6 Jul 2023 08:59:31 +0200 Subject: [PATCH 14/14] reverted changes --- testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java b/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java index cb4202f939..d181e9fd5c 100644 --- a/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java +++ b/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java @@ -140,8 +140,7 @@ public void updatingMapInMap() { @Test public void keys() { Map map = new HashMap<>(); - NativeArray resEmpty = - (NativeArray) runScript("Object.keys(value)", map, Function.identity(), true); + NativeArray resEmpty = (NativeArray) runScript("Object.keys(value)", map, true); assertEquals(0, resEmpty.size()); map.put("a", "a"); @@ -156,8 +155,7 @@ public void keys() { Map mapInt = new HashMap<>(); mapInt.put(42, "test"); - NativeArray resInt = - (NativeArray) runScript("Object.keys(value)", mapInt, Function.identity(), true); + NativeArray resInt = (NativeArray) runScript("Object.keys(value)", mapInt, true); assertTrue(resInt.contains("42")); // Object.keys always return Strings as key }