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  */
  25 
  26 package jdk.nashorn.internal.runtime.linker;
  27 
  28 import static jdk.nashorn.internal.lookup.Lookup.MH;
  29 
  30 import java.lang.invoke.MethodHandle;
  31 import java.lang.invoke.MethodHandles;
  32 import java.lang.ref.WeakReference;
  33 import jdk.dynalink.CallSiteDescriptor;
  34 import jdk.dynalink.linker.LinkRequest;
  35 import jdk.nashorn.api.scripting.JSObject;
  36 import jdk.nashorn.internal.objects.Global;
  37 import jdk.nashorn.internal.runtime.JSType;
  38 import jdk.nashorn.internal.runtime.Property;
  39 import jdk.nashorn.internal.runtime.PropertyMap;
  40 import jdk.nashorn.internal.runtime.ScriptFunction;
  41 import jdk.nashorn.internal.runtime.ScriptObject;
  42 import jdk.nashorn.internal.runtime.options.Options;
  43 
  44 /**
  45  * Constructor of method handles used to guard call sites.
  46  */
  47 public final class NashornGuards {
  48     private static final MethodHandle IS_MAP              = findOwnMH("isMap", boolean.class, ScriptObject.class, PropertyMap.class);
  49     private static final MethodHandle IS_MAP_SCRIPTOBJECT = findOwnMH("isMap", boolean.class, Object.class, PropertyMap.class);
  50     private static final MethodHandle IS_SCRIPTOBJECT     = findOwnMH("isScriptObject", boolean.class, Object.class);
  51     private static final MethodHandle IS_NOT_JSOBJECT     = findOwnMH("isNotJSObject", boolean.class, Object.class);
  52     private static final MethodHandle SAME_OBJECT         = findOwnMH("sameObject", boolean.class, Object.class, WeakReference.class);
  53     //TODO - maybe put this back in ScriptFunction instead of the ClassCastException.class relinkage
  54     //private static final MethodHandle IS_SCRIPTFUNCTION = findOwnMH("isScriptFunction", boolean.class, Object.class);
  55 
  56     private static final boolean CCE_ONLY = Options.getBooleanProperty("nashorn.cce");
  57 
  58     // don't create me!
  59     private NashornGuards() {
  60     }
  61 
  62     /**
  63      * Given a callsite descriptor and a link request, determine whether we should use an instanceof
  64      * check explicitly for the guard if needed, or if we should link it with a try/catch ClassCastException
  65      * combinator as its relink criteria - i.e. relink when CCE is thrown.
  66      *
  67      * @param desc     callsite descriptor
  68      * @param request  link request
  69      * @return true of explicit instanceof check is needed
  70      */
  71     public static boolean explicitInstanceOfCheck(final CallSiteDescriptor desc, final LinkRequest request) {
  72         //THIS is currently true, as the inliner encounters several problems with sun.misc.ValueConversions.castReference
  73         //otherwise. We should only use the exception based relink where we have no choice, and the result is faster code,
  74         //for example in the NativeArray, TypedArray, ContinuousArray getters. For the standard callsite, it appears that
  75         //we lose performance rather than gain it, due to JVM issues. :-(
  76         return !CCE_ONLY;
  77     }
  78 
  79     /**
  80      * Returns a guard that does an instanceof ScriptObject check on the receiver
  81      * @return guard
  82      */
  83     public static MethodHandle getScriptObjectGuard() {
  84         return IS_SCRIPTOBJECT;
  85     }
  86 
  87    /**
  88     * Get the guard that checks if an item is not a {@code JSObject}
  89     * @return method handle for guard
  90     */
  91    public static MethodHandle getNotJSObjectGuard() {
  92        return IS_NOT_JSOBJECT;
  93    }
  94 
  95     /**
  96      * Returns a guard that does an instanceof ScriptObject check on the receiver
  97      * @param explicitInstanceOfCheck - if false, then this is a nop, because it's all the guard does
  98      * @return guard
  99      */
 100     public static MethodHandle getScriptObjectGuard(final boolean explicitInstanceOfCheck) {
 101         return explicitInstanceOfCheck ? IS_SCRIPTOBJECT : null;
 102     }
 103 
 104     /**
 105      * Get the guard that checks if a {@link PropertyMap} is equal to
 106      * a known map, using reference comparison
 107      *
 108      * @param explicitInstanceOfCheck true if we should do an explicit script object instanceof check instead of just casting
 109      * @param map The map to check against. This will be bound to the guard method handle
 110      *
 111      * @return method handle for guard
 112      */
 113     public static MethodHandle getMapGuard(final PropertyMap map, final boolean explicitInstanceOfCheck) {
 114         return MH.insertArguments(explicitInstanceOfCheck ? IS_MAP_SCRIPTOBJECT : IS_MAP, 1, map);
 115     }
 116 
 117     /**
 118      * Determine whether the given callsite needs a guard.
 119      * @param property the property, or null
 120      * @param desc the callsite descriptor
 121      * @return true if a guard should be used for this callsite
 122      */
 123     static boolean needsGuard(final Property property, final CallSiteDescriptor desc) {
 124         return property == null || property.isConfigurable()
 125                 || property.isBound() || property.hasDualFields()
 126                 || !NashornCallSiteDescriptor.isFastScope(desc) || property.canChangeType();
 127     }
 128 
 129     /**
 130      * Get the guard for a property access. This returns an identity guard for non-configurable global properties
 131      * and a map guard for everything else.
 132      *
 133      * @param sobj the first object in the prototype chain
 134      * @param property the property
 135      * @param desc the callsite descriptor
 136      * @param explicitInstanceOfCheck true if we should do an explicit script object instanceof check instead of just casting
 137      * @return method handle for guard
 138      */
 139     public static MethodHandle getGuard(final ScriptObject sobj, final Property property, final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck) {
 140         if (!needsGuard(property, desc)) {
 141             return null;
 142         }
 143         if (NashornCallSiteDescriptor.isScope(desc) && sobj.isScope()) {
 144             if (property != null && property.isBound() && !property.canChangeType()) {
 145                 // This is a declared top level variables in main script or eval, use identity guard.
 146                 return getIdentityGuard(sobj);
 147             }
 148             if (!(sobj instanceof Global) && (property == null || property.isConfigurable())) {
 149                 // Undeclared variables in nested evals need stronger guards
 150                 return combineGuards(getIdentityGuard(sobj), getMapGuard(sobj.getMap(), explicitInstanceOfCheck));
 151             }
 152         }
 153         return getMapGuard(sobj.getMap(), explicitInstanceOfCheck);
 154     }
 155 
 156 
 157     /**
 158      * Get a guard that checks referential identity of the current object.
 159      *
 160      * @param sobj the self object
 161      * @return true if same self object instance
 162      */
 163     public static MethodHandle getIdentityGuard(final ScriptObject sobj) {
 164         return MH.insertArguments(SAME_OBJECT, 1, new WeakReference<>(sobj));
 165     }
 166 
 167     /**
 168      * Get a guard that checks if in item is a JS string.
 169      *
 170      * @return method handle for guard
 171      */
 172     public static MethodHandle getStringGuard() {
 173         return JSType.IS_STRING.methodHandle();
 174     }
 175 
 176     /**
 177      * Get a guard that checks if in item is a JS number.
 178      *
 179      * @return method handle for guard
 180      */
 181     public static MethodHandle getNumberGuard() {
 182         return JSType.IS_NUMBER.methodHandle();
 183     }
 184 
 185     /**
 186      * Combine two method handles of type {@code (Object)boolean} using logical AND.
 187      *
 188      * @param guard1 the first guard
 189      * @param guard2 the second guard, only invoked if guard1 returns true
 190      * @return true if both guard1 and guard2 returned true
 191      */
 192     public static MethodHandle combineGuards(final MethodHandle guard1, final MethodHandle guard2) {
 193         if (guard1 == null) {
 194             return guard2;
 195         } else if (guard2 == null) {
 196             return guard1;
 197         } else {
 198             return MH.guardWithTest(guard1, guard2, MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class));
 199         }
 200     }
 201 
 202     @SuppressWarnings("unused")
 203     private static boolean isScriptObject(final Object self) {
 204         return self instanceof ScriptObject;
 205     }
 206 
 207     @SuppressWarnings("unused")
 208     private static boolean isScriptObject(final Class<? extends ScriptObject> clazz, final Object self) {
 209         return clazz.isInstance(self);
 210     }
 211 
 212     @SuppressWarnings("unused")
 213     private static boolean isMap(final ScriptObject self, final PropertyMap map) {
 214         return self.getMap() == map;
 215     }
 216 
 217     @SuppressWarnings("unused")
 218     private static boolean isNotJSObject(final Object self) {
 219         return !(self instanceof JSObject);
 220     }
 221 
 222     @SuppressWarnings("unused")
 223     private static boolean isMap(final Object self, final PropertyMap map) {
 224         return self instanceof ScriptObject && ((ScriptObject)self).getMap() == map;
 225     }
 226 
 227 
 228     @SuppressWarnings("unused")
 229     private static boolean sameObject(final Object self, final WeakReference<ScriptObject> ref) {
 230         return self == ref.get();
 231     }
 232 
 233     @SuppressWarnings("unused")
 234     private static boolean isScriptFunction(final Object self) {
 235         return self instanceof ScriptFunction;
 236     }
 237 
 238     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
 239         return MH.findStatic(MethodHandles.lookup(), NashornGuards.class, name, MH.type(rtype, types));
 240     }
 241 }