src/jdk/nashorn/internal/objects/NativeArguments.java

Fri, 16 Aug 2013 18:51:53 +0200

author
lagergren
date
Fri, 16 Aug 2013 18:51:53 +0200
changeset 505
36fb36217e1d
parent 496
03ba1cd734c0
child 678
a165c0fb5be6
permissions
-rw-r--r--

8023017: SUB missing for widest op == number for BinaryNode
Reviewed-by: sundar, jlaskey

     1 /*
     2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package jdk.nashorn.internal.objects;
    28 import static jdk.nashorn.internal.lookup.Lookup.MH;
    29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
    30 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
    32 import java.lang.invoke.MethodHandle;
    33 import java.lang.invoke.MethodHandles;
    34 import java.util.ArrayList;
    35 import java.util.Arrays;
    36 import java.util.BitSet;
    37 import jdk.nashorn.internal.runtime.AccessorProperty;
    38 import jdk.nashorn.internal.runtime.Property;
    39 import jdk.nashorn.internal.runtime.PropertyDescriptor;
    40 import jdk.nashorn.internal.runtime.PropertyMap;
    41 import jdk.nashorn.internal.runtime.ScriptFunction;
    42 import jdk.nashorn.internal.runtime.ScriptObject;
    43 import jdk.nashorn.internal.runtime.ScriptRuntime;
    44 import jdk.nashorn.internal.runtime.arrays.ArrayData;
    45 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
    47 /**
    48  * ECMA 10.6 Arguments Object.
    49  *
    50  * Arguments object used for non-strict mode functions. For strict mode, we use
    51  * a different implementation (@see NativeStrictArguments). In non-strict mode,
    52  * named argument access and index argument access (arguments[i]) are linked.
    53  * Modifications reflect on each other access -- till arguments indexed element
    54  * is deleted. After delete, there is no link between named access and indexed
    55  * access for that deleted index alone.
    56  */
    57 public final class NativeArguments extends ScriptObject {
    59     private static final MethodHandle G$LENGTH = findOwnMH("G$length", Object.class, Object.class);
    60     private static final MethodHandle S$LENGTH = findOwnMH("S$length", void.class, Object.class, Object.class);
    61     private static final MethodHandle G$CALLEE = findOwnMH("G$callee", Object.class, Object.class);
    62     private static final MethodHandle S$CALLEE = findOwnMH("S$callee", void.class, Object.class, Object.class);
    64     private static final PropertyMap map$;
    66     static {
    67         final ArrayList<Property> properties = new ArrayList<>(2);
    68         properties.add(AccessorProperty.create("length", Property.NOT_ENUMERABLE, G$LENGTH, S$LENGTH));
    69         properties.add(AccessorProperty.create("callee", Property.NOT_ENUMERABLE, G$CALLEE, S$CALLEE));
    70         map$ = PropertyMap.newMap(properties).setIsShared();
    71     }
    73     static PropertyMap getInitialMap() {
    74         return map$;
    75     }
    77     private Object length;
    78     private Object callee;
    79     private final int numMapped;
    80     private final int numParams;
    82     // These are lazily initialized when delete is invoked on a mapped arg or an unmapped argument is set.
    83     private ArrayData unmappedArgs;
    84     private BitSet deleted;
    86     NativeArguments(final Object[] arguments, final Object callee, final int numParams, final ScriptObject proto, final PropertyMap map) {
    87         super(proto, map);
    88         setIsArguments();
    89         setArray(ArrayData.allocate(arguments));
    90         this.length = arguments.length;
    91         this.callee = callee;
    92         this.numMapped = Math.min(numParams, arguments.length);
    93         this.numParams = numParams;
    94     }
    96     @Override
    97     public String getClassName() {
    98         return "Arguments";
    99     }
   101     /**
   102      * getArgument is used for named argument access.
   103      */
   104     @Override
   105     public Object getArgument(final int key) {
   106         assert key >= 0 && key < numParams : "invalid argument index";
   107         return isMapped(key) ? getArray().getObject(key) : getUnmappedArg(key);
   108     }
   110     /**
   111      * setArgument is used for named argument set.
   112      */
   113     @Override
   114     public void setArgument(final int key, final Object value) {
   115         assert key >= 0 && key < numParams : "invalid argument index";
   116         if (isMapped(key)) {
   117             setArray(getArray().set(key, value, false));
   118         } else {
   119             setUnmappedArg(key, value);
   120         }
   121     }
   123     @Override
   124     public boolean delete(final int key, final boolean strict) {
   125         final int index = ArrayIndex.getArrayIndex(key);
   126         return isMapped(index) ? deleteMapped(index, strict) : super.delete(key, strict);
   127     }
   129     @Override
   130     public boolean delete(final long key, final boolean strict) {
   131         final int index = ArrayIndex.getArrayIndex(key);
   132         return isMapped(index) ? deleteMapped(index, strict) : super.delete(key, strict);
   133     }
   135     @Override
   136     public boolean delete(final double key, final boolean strict) {
   137         final int index = ArrayIndex.getArrayIndex(key);
   138         return isMapped(index) ? deleteMapped(index, strict) : super.delete(key, strict);
   139     }
   141     @Override
   142     public boolean delete(final Object key, final boolean strict) {
   143         final int index = ArrayIndex.getArrayIndex(key);
   144         return isMapped(index) ? deleteMapped(index, strict) : super.delete(key, strict);
   145     }
   147     /**
   148      * ECMA 15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw ) as specialized in
   149      * ECMA 10.6 for Arguments object.
   150      */
   151     @Override
   152     public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
   153         final int index = ArrayIndex.getArrayIndex(key);
   154         if (index >= 0) {
   155             final boolean isMapped = isMapped(index);
   156             final Object oldValue = isMapped ? getArray().getObject(index) : null;
   158             if (!super.defineOwnProperty(key, propertyDesc, false)) {
   159                 if (reject) {
   160                     throw typeError("cant.redefine.property",  key, ScriptRuntime.safeToString(this));
   161                 }
   162                 return false;
   163             }
   165             if (isMapped) {
   166                 // When mapped argument is redefined, if new descriptor is accessor property
   167                 // or data-non-writable property, we have to "unmap" (unlink).
   168                 final PropertyDescriptor desc = toPropertyDescriptor(Global.instance(), propertyDesc);
   169                 if (desc.type() == PropertyDescriptor.ACCESSOR) {
   170                     setDeleted(index, oldValue);
   171                 } else if (desc.has(PropertyDescriptor.WRITABLE) && !desc.isWritable()) {
   172                     // delete and set value from new descriptor if it has one, otherwise use old value
   173                     setDeleted(index, desc.has(PropertyDescriptor.VALUE) ? desc.getValue() : oldValue);
   174                 } else if (desc.has(PropertyDescriptor.VALUE)) {
   175                     setArray(getArray().set(index, desc.getValue(), false));
   176                 }
   177             }
   179             return true;
   180         }
   182         return super.defineOwnProperty(key, propertyDesc, reject);
   183     }
   185     // Internals below this point
   187     // We track deletions using a bit set (delete arguments[index])
   188     private boolean isDeleted(final int index) {
   189         return deleted != null && deleted.get(index);
   190     }
   192     private void setDeleted(final int index, final Object unmappedValue) {
   193         if (deleted == null) {
   194             deleted = new BitSet(numMapped);
   195         }
   196         deleted.set(index, true);
   197         setUnmappedArg(index, unmappedValue);
   198     }
   200     private boolean deleteMapped(final int index, final boolean strict) {
   201         final Object value = getArray().getObject(index);
   202         final boolean success = super.delete(index, strict);
   203         if (success) {
   204             setDeleted(index, value);
   205         }
   206         return success;
   207     }
   209     private Object getUnmappedArg(final int key) {
   210         assert key >= 0 && key < numParams;
   211         return unmappedArgs == null ? UNDEFINED : unmappedArgs.getObject(key);
   212     }
   214     private void setUnmappedArg(final int key, final Object value) {
   215         assert key >= 0 && key < numParams;
   216         if (unmappedArgs == null) {
   217             /*
   218              * Declared number of parameters may be more or less than the actual passed
   219              * runtime arguments count. We need to truncate or extend with undefined values.
   220              *
   221              * Example:
   222              *
   223              * // less declared params
   224              * (function (x) { print(arguments); })(20, 44);
   225              *
   226              * // more declared params
   227              * (function (x, y) { print(arguments); })(3);
   228              */
   229             final Object[] newValues = new Object[numParams];
   230             System.arraycopy(getArray().asObjectArray(), 0, newValues, 0, numMapped);
   231             if (numMapped < numParams) {
   232                 Arrays.fill(newValues, numMapped, numParams, UNDEFINED);
   233             }
   234             this.unmappedArgs = ArrayData.allocate(newValues);
   235         }
   236         // Set value of argument
   237         unmappedArgs = unmappedArgs.set(key, value, false);
   238     }
   240     /**
   241      * Are arguments[index] and corresponding named parameter linked?
   242      *
   243      * In non-strict mode, arguments[index] and corresponding named param are "linked" or "mapped"
   244      * if the argument is provided by the caller. Modifications are tacked b/w each other - until
   245      * (delete arguments[index]) is used. Once deleted, the corresponding arg is no longer 'mapped'.
   246      * Please note that delete can happen only through the arguments array - named param can not
   247      * be deleted. (delete is one-way).
   248      */
   249     private boolean isMapped(final int index) {
   250         // in mapped named args and not marked as "deleted"
   251         return index >= 0 && index < numMapped && !isDeleted(index);
   252     }
   254     /**
   255      * Factory to create correct Arguments object based on strict mode.
   256      *
   257      * @param arguments the actual arguments array passed
   258      * @param callee the callee function that uses arguments object
   259      * @param numParams the number of declared (named) function parameters
   260      * @return Arguments Object
   261      */
   262     public static ScriptObject allocate(final Object[] arguments, final ScriptFunction callee, final int numParams) {
   263         // Strict functions won't always have a callee for arguments, and will pass null instead.
   264         final boolean isStrict = callee == null || callee.isStrict();
   265         final Global global = Global.instance();
   266         final ScriptObject proto = global.getObjectPrototype();
   267         if (isStrict) {
   268             return new NativeStrictArguments(arguments, numParams, proto, global.getStrictArgumentsMap());
   269         }
   270         return new NativeArguments(arguments, callee, numParams, proto, global.getArgumentsMap());
   271     }
   273     /**
   274      * Length getter
   275      * @param self self reference
   276      * @return length property value
   277      */
   278     public static Object G$length(final Object self) {
   279         if (self instanceof NativeArguments) {
   280             return ((NativeArguments)self).getArgumentsLength();
   281         }
   283         return 0;
   284     }
   286     /**
   287      * Length setter
   288      * @param self self reference
   289      * @param value value for length property
   290      */
   291     public static void S$length(final Object self, final Object value) {
   292         if (self instanceof NativeArguments) {
   293             ((NativeArguments)self).setArgumentsLength(value);
   294         }
   295     }
   297     /**
   298      * Callee getter
   299      * @param self self reference
   300      * @return value for callee property
   301      */
   302     public static Object G$callee(final Object self) {
   303         if (self instanceof NativeArguments) {
   304             return ((NativeArguments)self).getCallee();
   305         }
   306         return UNDEFINED;
   307     }
   309     /**
   310      * Callee setter
   311      * @param self self reference
   312      * @param value value for callee property
   313      */
   314     public static void S$callee(final Object self, final Object value) {
   315         if (self instanceof NativeArguments) {
   316             ((NativeArguments)self).setCallee(value);
   317         }
   318     }
   320     @Override
   321     public Object getLength() {
   322         return length;
   323     }
   325     private Object getArgumentsLength() {
   326         return length;
   327     }
   329     private void setArgumentsLength(final Object length) {
   330         this.length = length;
   331     }
   333     private Object getCallee() {
   334         return callee;
   335     }
   337     private void setCallee(final Object callee) {
   338         this.callee = callee;
   339     }
   341     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
   342         return MH.findStatic(MethodHandles.lookup(), NativeArguments.class, name, MH.type(rtype, types));
   343     }
   344 }

mercurial