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;
  27 
  28 import static jdk.nashorn.internal.lookup.Lookup.MH;
  29 
  30 import java.lang.invoke.MethodHandle;
  31 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
  32 
  33 /**
  34  * This class represents the result from a find property search.
  35  */
  36 public final class FindProperty {
  37     /** Object where search began. */
  38     private final ScriptObject self;
  39 
  40     /** Object where search finish. */
  41     private final ScriptObject prototype;
  42 
  43     /** Found property. */
  44     private final Property     property;
  45 
  46     /**
  47      * Constructor
  48      *
  49      * @param self      script object where search began
  50      * @param prototype prototype where property was found, may be {@code self} if not inherited
  51      * @param property  property that was search result
  52      */
  53     public FindProperty(final ScriptObject self, final ScriptObject prototype, final Property property) {
  54         this.self      = self;
  55         this.prototype = prototype;
  56         this.property  = property;
  57     }
  58 
  59     /**
  60      * Ask for a getter that returns the given type. The type has nothing to do with the
  61      * internal representation of the property. It may be an Object (boxing primitives) or
  62      * a primitive (primitive fields with -Dnashorn.fields.dual=true)
  63      * @see ObjectClassGenerator
  64      *
  65      * @param type type of getter, e.g. int.class if we want a function with {@code get()I} signature
  66      * @return method handle for the getter
  67      */
  68     public MethodHandle getGetter(final Class<?> type) {
  69         MethodHandle getter = property.getGetter(type);
  70         if (property instanceof UserAccessorProperty) {
  71             final UserAccessorProperty uc = (UserAccessorProperty)property;
  72             getter = MH.insertArguments(getter, 0, isInherited() ? getOwner() : null, uc.getGetterSlot());
  73         }
  74         return getter;
  75     }
  76 
  77     /**
  78      * Ask for a setter that sets the given type. The type has nothing to do with the
  79      * internal representation of the property. It may be an Object (boxing primitives) or
  80      * a primitive (primitive fields with -Dnashorn.fields.dual=true)
  81      * @see ObjectClassGenerator
  82      *
  83      * @param type type of setter, e.g. int.class if we want a function with {@code set(I)V} signature
  84      * @param strict are we in strict mode
  85      *
  86      * @return method handle for the getter
  87      */
  88     public MethodHandle getSetter(final Class<?> type, final boolean strict) {
  89         MethodHandle setter = property.getSetter(type, getOwner().getMap());
  90         if (property instanceof UserAccessorProperty) {
  91             final UserAccessorProperty uc = (UserAccessorProperty) property;
  92             setter = MH.insertArguments(setter, 0, isInherited() ? getOwner() : null,
  93                     uc.getSetterSlot(), strict? property.getKey() : null);
  94         }
  95 
  96         return setter;
  97     }
  98 
  99     /**
 100      * Return the {@code ScriptObject} owning of the property:  this means the prototype.
 101      * @return owner of property
 102      */
 103     public ScriptObject getOwner() {
 104         return prototype;
 105     }
 106 
 107     /**
 108      * Return the appropriate receiver for a getter.
 109      * @return appropriate receiver
 110      */
 111     public ScriptObject getGetterReceiver() {
 112         return property != null && property.hasGetterFunction(prototype) ? self : prototype;
 113     }
 114 
 115     /**
 116      * Return the appropriate receiver for a setter.
 117      * @return appropriate receiver
 118      */
 119     public ScriptObject getSetterReceiver() {
 120         return property != null && property.hasSetterFunction(prototype) ? self : prototype;
 121     }
 122 
 123     /**
 124      * Return the property that was found
 125      * @return property
 126      */
 127     public Property getProperty() {
 128         return property;
 129     }
 130 
 131     /**
 132      * Check if the property found was inherited, i.e. not directly in the self
 133      * @return true if inherited property
 134      */
 135     public boolean isInherited() {
 136         return self != prototype;
 137     }
 138 
 139     /**
 140      * Check if the property found was NOT inherited, i.e. defined in the script
 141      * object, rather than in the prototype
 142      * @return true if not inherited
 143      */
 144     public boolean isSelf() {
 145         return self == prototype;
 146     }
 147 
 148     /**
 149      * Check if the property is in the scope
 150      * @return true if on scope
 151      */
 152     public boolean isScope() {
 153         return prototype.isScope();
 154     }
 155 
 156     /**
 157      * Get the property value from self as object.
 158      *
 159      * @return the property value
 160      */
 161     public Object getObjectValue() {
 162         return property.getObjectValue(getGetterReceiver(), getOwner());
 163     }
 164 
 165     /**
 166      * Set the property value in self.
 167      *
 168      * @param value the new value
 169      * @param strict strict flag
 170      */
 171     public void setObjectValue(final Object value, final boolean strict) {
 172         property.setObjectValue(getSetterReceiver(), getOwner(), value, strict);
 173     }
 174 
 175     /**
 176      * Get the number of objects in the prototype chain between the {@code self} and the
 177      * {@code owner} objects.
 178      * @return the prototype chain length
 179      */
 180     int getProtoChainLength() {
 181         assert self != null;
 182         int length = 0;
 183         for (ScriptObject obj = self; obj != prototype; obj = obj.getProto()) {
 184             assert !(obj instanceof WithObject);
 185             ++length;
 186         }
 187         return length;
 188     }
 189 
 190 }
 191