changeset 349:3d947baa33cc

8016618: script mirror object access should be improved Reviewed-by: jlaskey, lagergren
author sundar
date Fri, 14 Jun 2013 21:16:14 +0530
parents 3efa56767847
children a2fa56222fa2
files src/jdk/nashorn/api/scripting/ScriptObjectMirror.java src/jdk/nashorn/internal/ir/BreakableNode.java src/jdk/nashorn/internal/objects/NativeArray.java src/jdk/nashorn/internal/objects/NativeFunction.java src/jdk/nashorn/internal/objects/NativeObject.java src/jdk/nashorn/internal/runtime/Context.java src/jdk/nashorn/internal/runtime/ScriptObject.java src/jdk/nashorn/internal/runtime/ScriptRuntime.java src/jdk/nashorn/internal/runtime/arrays/ArrayLikeIterator.java src/jdk/nashorn/internal/runtime/arrays/IteratorAction.java src/jdk/nashorn/internal/runtime/arrays/ReverseScriptObjectMirrorIterator.java src/jdk/nashorn/internal/runtime/arrays/ScriptObjectMirrorIterator.java test/script/basic/JDK-8016618.js test/script/basic/JDK-8016618.js.EXPECTED
diffstat 14 files changed, 615 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java	Fri Jun 14 13:53:40 2013 +0200
+++ b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java	Fri Jun 14 21:16:14 2013 +0530
@@ -342,6 +342,184 @@
         });
     }
 
+    // Support for ECMAScript Object API on mirrors
+
+    /**
+     * Return the __proto__ of this object.
+     * @return __proto__ object.
+     */
+    public Object getProto() {
+        return inGlobal(new Callable<Object>() {
+            @Override public Object call() {
+                return wrap(getScriptObject().getProto(), global);
+            }
+        });
+    }
+
+    /**
+     * ECMA 8.12.1 [[GetOwnProperty]] (P)
+     *
+     * @param key property key
+     *
+     * @return Returns the Property Descriptor of the named own property of this
+     * object, or undefined if absent.
+     */
+    public Object getOwnPropertyDescriptor(final String key) {
+        return inGlobal(new Callable<Object>() {
+            @Override public Object call() {
+                return wrap(getScriptObject().getOwnPropertyDescriptor(key), global);
+            }
+        });
+    }
+
+    /**
+     * return an array of own property keys associated with the object.
+     *
+     * @param all True if to include non-enumerable keys.
+     * @return Array of keys.
+     */
+    public String[] getOwnKeys(final boolean all) {
+        return inGlobal(new Callable<String[]>() {
+            @Override public String[] call() {
+                return getScriptObject().getOwnKeys(all);
+            }
+        });
+    }
+
+    /**
+     * Flag this script object as non extensible
+     *
+     * @return the object after being made non extensible
+     */
+    public ScriptObjectMirror preventExtensions() {
+        return inGlobal(new Callable<ScriptObjectMirror>() {
+            @Override public ScriptObjectMirror call() {
+                getScriptObject().preventExtensions();
+                return ScriptObjectMirror.this;
+            }
+        });
+    }
+
+    /**
+     * Check if this script object is extensible
+     * @return true if extensible
+     */
+    public boolean isExtensible() {
+        return inGlobal(new Callable<Boolean>() {
+            @Override public Boolean call() {
+                return getScriptObject().isExtensible();
+            }
+        });
+    }
+
+    /**
+     * ECMAScript 15.2.3.8 - seal implementation
+     * @return the sealed script object
+     */
+    public ScriptObjectMirror seal() {
+        return inGlobal(new Callable<ScriptObjectMirror>() {
+            @Override public ScriptObjectMirror call() {
+                getScriptObject().seal();
+                return ScriptObjectMirror.this;
+            }
+        });
+    }
+
+    /**
+     * Check whether this script object is sealed
+     * @return true if sealed
+     */
+    public boolean isSealed() {
+        return inGlobal(new Callable<Boolean>() {
+            @Override public Boolean call() {
+                return getScriptObject().isSealed();
+            }
+        });
+    }
+
+    /**
+     * ECMA 15.2.39 - freeze implementation. Freeze this script object
+     * @return the frozen script object
+     */
+    public ScriptObjectMirror freeze() {
+        return inGlobal(new Callable<ScriptObjectMirror>() {
+            @Override public ScriptObjectMirror call() {
+                getScriptObject().freeze();
+                return ScriptObjectMirror.this;
+            }
+        });
+    }
+
+    /**
+     * Check whether this script object is frozen
+     * @return true if frozen
+     */
+    public boolean isFrozen() {
+        return inGlobal(new Callable<Boolean>() {
+            @Override public Boolean call() {
+                return getScriptObject().isFrozen();
+            }
+        });
+    }
+
+    // ECMAScript instanceof check
+
+    /**
+     * Checking whether a script object is an instance of another by
+     * walking the proto chain
+     *
+     * @param instance instace to check
+     * @return true if 'instance' is an instance of this object
+     */
+    public boolean isInstance(final ScriptObjectMirror instance) {
+        // if not belongs to my global scope, return false
+        if (instance == null || global != instance.global) {
+            return false;
+        }
+
+        return inGlobal(new Callable<Boolean>() {
+            @Override public Boolean call() {
+                return getScriptObject().isInstance(instance.getScriptObject());
+            }
+        });
+    }
+
+    /**
+     * Utility to check if given object is ECMAScript undefined value
+     *
+     * @param obj object to check
+     * @return true if 'obj' is ECMAScript undefined value
+     */
+    public static boolean isUndefined(final Object obj) {
+        return obj == ScriptRuntime.UNDEFINED;
+    }
+
+    /**
+     * is this a function object?
+     *
+     * @return if this mirror wraps a ECMAScript function instance
+     */
+    public boolean isFunction() {
+        return getScriptObject() instanceof ScriptFunction;
+    }
+
+    /**
+     * is this a 'use strict' function object?
+     *
+     * @return true if this mirror represents a ECMAScript 'use strict' function
+     */
+    public boolean isStrictFunction() {
+        return isFunction() && ((ScriptFunction)getScriptObject()).isStrict();
+    }
+
+    /**
+     * is this an array object?
+     *
+     * @return if this mirror wraps a ECMAScript array object
+     */
+    public boolean isArray() {
+        return getScriptObject().isArray();
+    }
 
     // These are public only so that Context can access these.
 
--- a/src/jdk/nashorn/internal/ir/BreakableNode.java	Fri Jun 14 13:53:40 2013 +0200
+++ b/src/jdk/nashorn/internal/ir/BreakableNode.java	Fri Jun 14 21:16:14 2013 +0530
@@ -86,7 +86,7 @@
 
     /**
      * Return the labels associated with this node. Breakable nodes that
-     * aren't LoopNodes only have a break label -> the location immediately
+     * aren't LoopNodes only have a break label - the location immediately
      * afterwards the node in code
      * @return list of labels representing locations around this node
      */
--- a/src/jdk/nashorn/internal/objects/NativeArray.java	Fri Jun 14 13:53:40 2013 +0200
+++ b/src/jdk/nashorn/internal/objects/NativeArray.java	Fri Jun 14 21:16:14 2013 +0530
@@ -39,6 +39,7 @@
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
 import jdk.nashorn.internal.objects.annotations.Attribute;
 import jdk.nashorn.internal.objects.annotations.Constructor;
 import jdk.nashorn.internal.objects.annotations.Function;
@@ -291,7 +292,8 @@
     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
     public static Object isArray(final Object self, final Object arg) {
         return isArray(arg) || (arg == Global.instance().getArrayPrototype())
-                || (arg instanceof NativeRegExpExecResult);
+                || (arg instanceof NativeRegExpExecResult)
+                || (arg instanceof ScriptObjectMirror && ((ScriptObjectMirror)arg).isArray());
     }
 
     /**
--- a/src/jdk/nashorn/internal/objects/NativeFunction.java	Fri Jun 14 13:53:40 2013 +0200
+++ b/src/jdk/nashorn/internal/objects/NativeFunction.java	Fri Jun 14 21:16:14 2013 +0530
@@ -29,6 +29,7 @@
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
 
 import java.util.List;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
 import jdk.nashorn.internal.objects.annotations.Attribute;
 import jdk.nashorn.internal.objects.annotations.Constructor;
 import jdk.nashorn.internal.objects.annotations.Function;
@@ -102,6 +103,16 @@
             list.toArray(args = new Object[list.size()]);
         } else if (array == null || array == UNDEFINED) {
             args = ScriptRuntime.EMPTY_ARRAY;
+        } else if (array instanceof ScriptObjectMirror) {
+            // look for array-like ScriptObjectMirror object
+            final ScriptObjectMirror mirror = (ScriptObjectMirror)array;
+            final Object       len  = mirror.containsKey("length")? mirror.getMember("length") : Integer.valueOf(0);
+            final int n = (int)JSType.toUint32(len);
+
+            args = new Object[n];
+            for (int i = 0; i < args.length; i++) {
+                args[i] = mirror.containsKey(i)? mirror.getSlot(i) : UNDEFINED;
+            }
         } else {
             throw typeError("function.apply.expects.array");
         }
--- a/src/jdk/nashorn/internal/objects/NativeObject.java	Fri Jun 14 13:53:40 2013 +0200
+++ b/src/jdk/nashorn/internal/objects/NativeObject.java	Fri Jun 14 21:16:14 2013 +0530
@@ -28,11 +28,13 @@
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
 
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
 import jdk.nashorn.internal.objects.annotations.Attribute;
 import jdk.nashorn.internal.objects.annotations.Constructor;
 import jdk.nashorn.internal.objects.annotations.Function;
 import jdk.nashorn.internal.objects.annotations.ScriptClass;
 import jdk.nashorn.internal.objects.annotations.Where;
+import jdk.nashorn.internal.runtime.ECMAException;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
@@ -54,6 +56,10 @@
     private NativeObject() {
     }
 
+    private static ECMAException notAnObject(final Object obj) {
+        return typeError("not.an.object", ScriptRuntime.safeToString(obj));
+    }
+
     /**
      * ECMA 15.2.3.2 Object.getPrototypeOf ( O )
      *
@@ -63,9 +69,13 @@
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
     public static Object getPrototypeOf(final Object self, final Object obj) {
-        Global.checkObject(obj);
-
-        return ((ScriptObject)obj).getProto();
+        if (obj instanceof ScriptObject) {
+            return ((ScriptObject)obj).getProto();
+        } else if (obj instanceof ScriptObjectMirror) {
+            return ((ScriptObjectMirror)obj).getProto();
+        } else {
+            throw notAnObject(obj);
+        }
     }
 
     /**
@@ -78,12 +88,19 @@
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
     public static Object getOwnPropertyDescriptor(final Object self, final Object obj, final Object prop) {
-        Global.checkObject(obj);
+        if (obj instanceof ScriptObject) {
+            final String       key  = JSType.toString(prop);
+            final ScriptObject sobj = (ScriptObject)obj;
 
-        final String       key  = JSType.toString(prop);
-        final ScriptObject sobj = (ScriptObject)obj;
+            return sobj.getOwnPropertyDescriptor(key);
+        } else if (obj instanceof ScriptObjectMirror) {
+            final String       key  = JSType.toString(prop);
+            final ScriptObjectMirror sobjMirror = (ScriptObjectMirror)obj;
 
-        return sobj.getOwnPropertyDescriptor(key);
+            return sobjMirror.getOwnPropertyDescriptor(key);
+        } else {
+            throw notAnObject(obj);
+        }
     }
 
     /**
@@ -95,9 +112,13 @@
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
     public static Object getOwnPropertyNames(final Object self, final Object obj) {
-        Global.checkObject(obj);
-
-        return new NativeArray(((ScriptObject)obj).getOwnKeys(true));
+        if (obj instanceof ScriptObject) {
+            return new NativeArray(((ScriptObject)obj).getOwnKeys(true));
+        } else if (obj instanceof ScriptObjectMirror) {
+            return new NativeArray(((ScriptObjectMirror)obj).getOwnKeys(true));
+        } else {
+            throw notAnObject(obj);
+        }
     }
 
     /**
@@ -175,8 +196,13 @@
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
     public static Object seal(final Object self, final Object obj) {
-        Global.checkObject(obj);
-        return ((ScriptObject)obj).seal();
+        if (obj instanceof ScriptObject) {
+            return ((ScriptObject)obj).seal();
+        } else if (obj instanceof ScriptObjectMirror) {
+            return ((ScriptObjectMirror)obj).seal();
+        } else {
+            throw notAnObject(obj);
+        }
     }
 
 
@@ -189,8 +215,13 @@
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
     public static Object freeze(final Object self, final Object obj) {
-        Global.checkObject(obj);
-        return ((ScriptObject)obj).freeze();
+        if (obj instanceof ScriptObject) {
+            return ((ScriptObject)obj).freeze();
+        } else if (obj instanceof ScriptObjectMirror) {
+            return ((ScriptObjectMirror)obj).freeze();
+        } else {
+            throw notAnObject(obj);
+        }
     }
 
     /**
@@ -202,8 +233,13 @@
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
     public static Object preventExtensions(final Object self, final Object obj) {
-        Global.checkObject(obj);
-        return ((ScriptObject)obj).preventExtensions();
+        if (obj instanceof ScriptObject) {
+            return ((ScriptObject)obj).preventExtensions();
+        } else if (obj instanceof ScriptObjectMirror) {
+            return ((ScriptObjectMirror)obj).preventExtensions();
+        } else {
+            throw notAnObject(obj);
+        }
     }
 
     /**
@@ -215,8 +251,13 @@
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
     public static Object isSealed(final Object self, final Object obj) {
-        Global.checkObject(obj);
-        return ((ScriptObject)obj).isSealed();
+        if (obj instanceof ScriptObject) {
+            return ((ScriptObject)obj).isSealed();
+        } else if (obj instanceof ScriptObjectMirror) {
+            return ((ScriptObjectMirror)obj).isSealed();
+        } else {
+            throw notAnObject(obj);
+        }
     }
 
     /**
@@ -228,8 +269,13 @@
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
     public static Object isFrozen(final Object self, final Object obj) {
-        Global.checkObject(obj);
-        return ((ScriptObject)obj).isFrozen();
+        if (obj instanceof ScriptObject) {
+            return ((ScriptObject)obj).isFrozen();
+        } else if (obj instanceof ScriptObjectMirror) {
+            return ((ScriptObjectMirror)obj).isFrozen();
+        } else {
+            throw notAnObject(obj);
+        }
     }
 
     /**
@@ -241,8 +287,13 @@
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
     public static Object isExtensible(final Object self, final Object obj) {
-        Global.checkObject(obj);
-        return ((ScriptObject)obj).isExtensible();
+        if (obj instanceof ScriptObject) {
+            return ((ScriptObject)obj).isExtensible();
+        } else if (obj instanceof ScriptObjectMirror) {
+            return ((ScriptObjectMirror)obj).isExtensible();
+        } else {
+            throw notAnObject(obj);
+        }
     }
 
     /**
@@ -254,9 +305,15 @@
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
     public static Object keys(final Object self, final Object obj) {
-        Global.checkObject(obj);
-        final ScriptObject sobj = (ScriptObject)obj;
-        return new NativeArray(sobj.getOwnKeys(false));
+        if (obj instanceof ScriptObject) {
+            final ScriptObject sobj = (ScriptObject)obj;
+            return new NativeArray(sobj.getOwnKeys(false));
+        } else if (obj instanceof ScriptObjectMirror) {
+            final ScriptObjectMirror sobjMirror = (ScriptObjectMirror)obj;
+            return new NativeArray(sobjMirror.getOwnKeys(false));
+        } else {
+            throw notAnObject(obj);
+        }
     }
 
     /**
--- a/src/jdk/nashorn/internal/runtime/Context.java	Fri Jun 14 13:53:40 2013 +0200
+++ b/src/jdk/nashorn/internal/runtime/Context.java	Fri Jun 14 21:16:14 2013 +0530
@@ -46,6 +46,7 @@
 import java.security.Permissions;
 import java.security.PrivilegedAction;
 import java.security.ProtectionDomain;
+import java.util.Map;
 import jdk.internal.org.objectweb.asm.ClassReader;
 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
 import jdk.nashorn.api.scripting.ScriptObjectMirror;
@@ -482,6 +483,13 @@
                 final String name   = JSType.toString(sobj.get("name"));
                 source = new Source(name, script);
             }
+        } else if (src instanceof Map) {
+            final Map map = (Map)src;
+            if (map.containsKey("script") && map.containsKey("name")) {
+                final String script = JSType.toString(map.get("script"));
+                final String name   = JSType.toString(map.get("name"));
+                source = new Source(name, script);
+            }
         }
 
         if (source != null) {
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java	Fri Jun 14 13:53:40 2013 +0200
+++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java	Fri Jun 14 21:16:14 2013 +0530
@@ -1108,7 +1108,8 @@
     }
 
     /**
-     * return a List of own keys associated with the object.
+     * return an array of own property keys associated with the object.
+     *
      * @param all True if to include non-enumerable keys.
      * @return Array of keys.
      */
@@ -1209,7 +1210,7 @@
      * the proto chain
      *
      * @param instance instace to check
-     * @return true if instance of instance
+     * @return true if 'instance' is an instance of this object
      */
     public boolean isInstance(final ScriptObject instance) {
         return false;
@@ -1382,7 +1383,7 @@
 
     /**
      * Check whether this ScriptObject is frozen
-     * @return true if frozed
+     * @return true if frozen
      */
     public boolean isFrozen() {
         return getMap().isFrozen();
--- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Fri Jun 14 13:53:40 2013 +0200
+++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Fri Jun 14 21:16:14 2013 +0530
@@ -825,6 +825,13 @@
             return ((StaticClass)clazz).getRepresentedClass().isInstance(obj);
         }
 
+        if (clazz instanceof ScriptObjectMirror) {
+            if (obj instanceof ScriptObjectMirror) {
+                return ((ScriptObjectMirror)clazz).isInstance((ScriptObjectMirror)obj);
+            }
+            return false;
+        }
+
         throw typeError("instanceof.on.non.object");
     }
 
--- a/src/jdk/nashorn/internal/runtime/arrays/ArrayLikeIterator.java	Fri Jun 14 13:53:40 2013 +0200
+++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayLikeIterator.java	Fri Jun 14 21:16:14 2013 +0530
@@ -26,6 +26,7 @@
 package jdk.nashorn.internal.runtime.arrays;
 
 import java.util.Iterator;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ScriptObject;
 
@@ -125,6 +126,10 @@
             return new MapIterator((ScriptObject)obj, includeUndefined);
         }
 
+        if (obj instanceof ScriptObjectMirror) {
+            return new ScriptObjectMirrorIterator((ScriptObjectMirror)obj, includeUndefined);
+        }
+
         return new EmptyArrayLikeIterator();
     }
 
@@ -146,6 +151,10 @@
             return new ReverseMapIterator((ScriptObject)obj, includeUndefined);
         }
 
+        if (obj instanceof ScriptObjectMirror) {
+            return new ReverseScriptObjectMirrorIterator((ScriptObjectMirror)obj, includeUndefined);
+        }
+
         assert !obj.getClass().isArray();
 
         return new EmptyArrayLikeIterator();
--- a/src/jdk/nashorn/internal/runtime/arrays/IteratorAction.java	Fri Jun 14 13:53:40 2013 +0200
+++ b/src/jdk/nashorn/internal/runtime/arrays/IteratorAction.java	Fri Jun 14 21:16:14 2013 +0530
@@ -27,6 +27,7 @@
 
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
@@ -96,12 +97,18 @@
      * @return result of apply
      */
     public final T apply() {
-        if (!(callbackfn instanceof ScriptFunction)) {
+        final boolean strict;
+        if (callbackfn instanceof ScriptFunction) {
+            strict = ((ScriptFunction)callbackfn).isStrict();
+        } else if (callbackfn instanceof ScriptObjectMirror &&
+            ((ScriptObjectMirror)callbackfn).isFunction()) {
+            strict = ((ScriptObjectMirror)callbackfn).isStrictFunction();
+        } else {
             throw typeError("not.a.function", ScriptRuntime.safeToString(callbackfn));
         }
-        final ScriptFunction func = ((ScriptFunction)callbackfn);
+
         // for non-strict callback, need to translate undefined thisArg to be global object
-        thisArg = (thisArg == ScriptRuntime.UNDEFINED && !func.isStrict()) ? Context.getGlobal() : thisArg;
+        thisArg = (thisArg == ScriptRuntime.UNDEFINED && !strict)? Context.getGlobal() : thisArg;
 
         applyLoopBegin(iter);
         final boolean reverse = iter.isReverse();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/arrays/ReverseScriptObjectMirrorIterator.java	Fri Jun 14 21:16:14 2013 +0530
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime.arrays;
+
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import jdk.nashorn.internal.runtime.JSType;
+
+/**
+ * Reverse iterator over a ScriptObjectMirror
+ */
+final class ReverseScriptObjectMirrorIterator extends ScriptObjectMirrorIterator {
+
+    ReverseScriptObjectMirrorIterator(final ScriptObjectMirror obj, final boolean includeUndefined) {
+        super(obj, includeUndefined);
+        this.index = JSType.toUint32(obj.containsKey("length")? obj.getMember("length") : 0) - 1;
+    }
+
+    @Override
+    public boolean isReverse() {
+        return true;
+    }
+
+    @Override
+    protected boolean indexInArray() {
+        return index >= 0;
+    }
+
+    @Override
+    protected long bumpIndex() {
+        return index--;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/arrays/ScriptObjectMirrorIterator.java	Fri Jun 14 21:16:14 2013 +0530
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime.arrays;
+
+import java.util.NoSuchElementException;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import jdk.nashorn.internal.runtime.JSType;
+
+/**
+ * Iterator over a ScriptObjectMirror
+ */
+class ScriptObjectMirrorIterator extends ArrayLikeIterator<Object> {
+
+    protected final ScriptObjectMirror obj;
+    private final long length;
+
+    ScriptObjectMirrorIterator(final ScriptObjectMirror obj, final boolean includeUndefined) {
+        super(includeUndefined);
+        this.obj    = obj;
+        this.length = JSType.toUint32(obj.containsKey("length")? obj.getMember("length") : 0);
+        this.index  = 0;
+    }
+
+    protected boolean indexInArray() {
+        return index < length;
+    }
+
+    @Override
+    public long getLength() {
+        return length;
+    }
+
+    @Override
+    public boolean hasNext() {
+        if (length == 0L) {
+            return false; //return empty string if toUint32(length) == 0
+        }
+
+        while (indexInArray()) {
+            if (obj.containsKey(index) || includeUndefined) {
+                break;
+            }
+            bumpIndex();
+        }
+
+        return indexInArray();
+    }
+
+    @Override
+    public Object next() {
+        if (indexInArray()) {
+            return obj.get(bumpIndex());
+        }
+
+        throw new NoSuchElementException();
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8016618.js	Fri Jun 14 21:16:14 2013 +0530
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8016618: script mirror object access should be improved
+ *
+ * @test
+ * @option -scripting
+ * @option -strict
+ * @run
+ */
+
+var global = loadWithNewGlobal({
+    name: "code",
+    script: <<EOF
+var x = 33;
+
+function func(x, y) {
+    print('func.x = ' + x);
+    print('func.x = ' + y)
+}; 
+
+var obj = {
+    foo: 'hello',
+    bar: 42
+};
+
+Object.defineProperty(obj, "bar",
+    { enumerable: false, writable: false });
+
+// return global
+this;
+EOF
+});
+
+// load on mirror with local object as argument
+global.load({ 
+    name: "code",
+    script: "print('x = ' + x)"
+});
+
+function f() {
+    // mirror function called with local arguments
+    global.func.apply(obj, arguments);
+}
+
+f(23, "hello");
+
+var fObject = global.eval("Object");
+
+// instanceof on mirrors
+print("global instanceof fObject? " + (global instanceof fObject));
+
+// Object API on mirrors
+
+var desc = Object.getOwnPropertyDescriptor(global, "x");
+print("x is wriable ? " + desc.writable);
+print("x value = " + desc.value);
+
+var proto = Object.getPrototypeOf(global);
+print("global's __proto__ " + proto);
+
+var obj = global.obj;
+var keys = Object.keys(obj);
+print("Object.keys on obj");
+for (var i in keys) {
+    print(keys[i]);
+}
+
+print("Object.getOwnProperties on obj");
+var keys = Object.getOwnPropertyNames(obj);
+for (var i in keys) {
+  print(keys[i]);
+}
+
+// mirror array access
+var array = global.eval("[334, 55, 65]");
+Array.prototype.forEach.call(array, function(elem) {
+    print("forEach " + elem)
+});
+
+print("reduceRight " + Array.prototype.reduceRight.call(array,
+    function(previousValue, currentValue, index, array) {
+        print("reduceRight cur value " + currentValue);
+        return previousValue + currentValue;
+}, 0));
+
+print("reduce " + Array.prototype.reduce.call(array,
+    function(previousValue, currentValue, index, array) {
+        print("reduce cur value " + currentValue);
+        return previousValue + currentValue;
+}, 0));
+
+print("forEach");
+Array.prototype.forEach.call(array, function(o) {
+   print(o);
+});
+
+print("Array.isArray(array)? " + Array.isArray(array));
+
+// try to write to a non-writable property of mirror
+try {
+   obj.bar = 33;
+} catch (e) {
+   print(e);
+}
+
+// mirror function called with local callback
+print("forEach on mirror");
+array.forEach(function(toto) {
+    print(toto);
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8016618.js.EXPECTED	Fri Jun 14 21:16:14 2013 +0530
@@ -0,0 +1,33 @@
+x = 33
+func.x = 23
+func.x = hello
+global instanceof fObject? true
+x is wriable ? true
+x value = 33
+global's __proto__ [object Object]
+Object.keys on obj
+foo
+Object.getOwnProperties on obj
+foo
+bar
+forEach 334
+forEach 55
+forEach 65
+reduceRight cur value 65
+reduceRight cur value 55
+reduceRight cur value 334
+reduceRight 454
+reduce cur value 334
+reduce cur value 55
+reduce cur value 65
+reduce 454
+forEach
+334
+55
+65
+Array.isArray(array)? true
+TypeError: "bar" is not a writable property of [object Object]
+forEach on mirror
+334
+55
+65