src/jdk/nashorn/internal/runtime/WithObject.java

Wed, 12 Mar 2014 11:26:00 +0100

author
hannesw
date
Wed, 12 Mar 2014 11:26:00 +0100
changeset 769
5a1ae83c295f
parent 766
06ee95f094b4
child 952
6d5471a497fb
child 963
e2497b11a021
permissions
-rw-r--r--

8021350: Share script classes between threads/globals within context
Reviewed-by: lagergren, sundar

jlaskey@3 1 /*
jlaskey@7 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
jlaskey@3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jlaskey@3 4 *
jlaskey@3 5 * This code is free software; you can redistribute it and/or modify it
jlaskey@3 6 * under the terms of the GNU General Public License version 2 only, as
jlaskey@3 7 * published by the Free Software Foundation. Oracle designates this
jlaskey@3 8 * particular file as subject to the "Classpath" exception as provided
jlaskey@3 9 * by Oracle in the LICENSE file that accompanied this code.
jlaskey@3 10 *
jlaskey@3 11 * This code is distributed in the hope that it will be useful, but WITHOUT
jlaskey@3 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jlaskey@3 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jlaskey@3 14 * version 2 for more details (a copy is included in the LICENSE file that
jlaskey@3 15 * accompanied this code).
jlaskey@3 16 *
jlaskey@3 17 * You should have received a copy of the GNU General Public License version
jlaskey@3 18 * 2 along with this work; if not, write to the Free Software Foundation,
jlaskey@3 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jlaskey@3 20 *
jlaskey@3 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jlaskey@3 22 * or visit www.oracle.com if you need additional information or have any
jlaskey@3 23 * questions.
jlaskey@3 24 */
jlaskey@3 25
jlaskey@3 26 package jdk.nashorn.internal.runtime;
jlaskey@3 27
sundar@133 28 import static jdk.nashorn.internal.lookup.Lookup.MH;
jlaskey@3 29
jlaskey@3 30 import java.lang.invoke.MethodHandle;
jlaskey@3 31 import java.lang.invoke.MethodHandles;
attila@178 32 import java.lang.invoke.MethodType;
sundar@541 33 import java.lang.invoke.SwitchPoint;
attila@90 34 import jdk.internal.dynalink.CallSiteDescriptor;
attila@90 35 import jdk.internal.dynalink.linker.GuardedInvocation;
attila@90 36 import jdk.internal.dynalink.linker.LinkRequest;
attila@90 37 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
jlaskey@3 38 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
hannesw@766 39 import jdk.nashorn.internal.runtime.linker.NashornGuards;
jlaskey@3 40
jlaskey@3 41 /**
jlaskey@3 42 * This class supports the handling of scope in a with body.
jlaskey@3 43 *
jlaskey@3 44 */
sundar@82 45 public final class WithObject extends ScriptObject implements Scope {
sundar@541 46 private static final MethodHandle WITHEXPRESSIONGUARD = findOwnMH("withExpressionGuard", boolean.class, Object.class, PropertyMap.class, SwitchPoint.class);
attila@178 47 private static final MethodHandle WITHEXPRESSIONFILTER = findOwnMH("withFilterExpression", Object.class, Object.class);
attila@178 48 private static final MethodHandle WITHSCOPEFILTER = findOwnMH("withFilterScope", Object.class, Object.class);
attila@178 49 private static final MethodHandle BIND_TO_EXPRESSION_OBJ = findOwnMH("bindToExpression", Object.class, Object.class, Object.class);
attila@178 50 private static final MethodHandle BIND_TO_EXPRESSION_FN = findOwnMH("bindToExpression", Object.class, ScriptFunction.class, Object.class);
jlaskey@3 51
jlaskey@3 52 /** With expression object. */
sundar@541 53 private final ScriptObject expression;
jlaskey@3 54
jlaskey@3 55 /**
jlaskey@3 56 * Constructor
jlaskey@3 57 *
jlaskey@3 58 * @param scope scope object
jlaskey@3 59 * @param expression with expression
jlaskey@3 60 */
sundar@541 61 WithObject(final ScriptObject scope, final ScriptObject expression) {
sundar@456 62 super(scope, null);
jlaskey@3 63 setIsScope();
jlaskey@3 64 this.expression = expression;
jlaskey@3 65 }
jlaskey@3 66
sundar@541 67
jlaskey@3 68 /**
jlaskey@3 69 * Delete a property based on a key.
jlaskey@3 70 * @param key Any valid JavaScript value.
jlaskey@3 71 * @param strict strict mode execution.
jlaskey@3 72 * @return True if deleted.
jlaskey@3 73 */
jlaskey@3 74 @Override
jlaskey@3 75 public boolean delete(final Object key, final boolean strict) {
sundar@541 76 final ScriptObject self = expression;
sundar@541 77 final String propName = JSType.toString(key);
jlaskey@3 78
sundar@541 79 final FindProperty find = self.findProperty(propName, true);
jlaskey@3 80
sundar@541 81 if (find != null) {
sundar@541 82 return self.delete(propName, strict);
jlaskey@3 83 }
jlaskey@3 84
jlaskey@3 85 return false;
jlaskey@3 86 }
jlaskey@3 87
jlaskey@3 88
jlaskey@3 89 @Override
hannesw@51 90 public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
hannesw@769 91 if (request.isCallSiteUnstable()) {
hannesw@769 92 // Fall back to megamorphic invocation which performs a complete lookup each time without further relinking.
hannesw@769 93 return super.lookup(desc, request);
hannesw@769 94 }
hannesw@769 95
jlaskey@3 96 // With scopes can never be observed outside of Nashorn code, so all call sites that can address it will of
jlaskey@3 97 // necessity have a Nashorn descriptor - it is safe to cast.
jlaskey@3 98 final NashornCallSiteDescriptor ndesc = (NashornCallSiteDescriptor)desc;
jlaskey@3 99 FindProperty find = null;
jlaskey@3 100 GuardedInvocation link = null;
jlaskey@3 101 ScriptObject self = null;
jlaskey@3 102
jlaskey@3 103 final boolean isNamedOperation;
jlaskey@3 104 final String name;
jlaskey@3 105 if(desc.getNameTokenCount() > 2) {
jlaskey@3 106 isNamedOperation = true;
jlaskey@3 107 name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
jlaskey@3 108 } else {
jlaskey@3 109 isNamedOperation = false;
jlaskey@3 110 name = null;
jlaskey@3 111 }
jlaskey@3 112
sundar@541 113 self = expression;
sundar@541 114 if (isNamedOperation) {
sundar@541 115 find = self.findProperty(name, true);
sundar@541 116 }
jlaskey@3 117
sundar@541 118 if (find != null) {
sundar@541 119 link = self.lookup(desc, request);
jlaskey@3 120
sundar@541 121 if (link != null) {
sundar@541 122 return fixExpressionCallSite(ndesc, link);
jlaskey@3 123 }
jlaskey@3 124 }
jlaskey@3 125
jlaskey@3 126 final ScriptObject scope = getProto();
jlaskey@3 127 if (isNamedOperation) {
jlaskey@3 128 find = scope.findProperty(name, true);
jlaskey@3 129 }
jlaskey@3 130
jlaskey@3 131 if (find != null) {
hannesw@766 132 return fixScopeCallSite(scope.lookup(desc, request), name, find.getOwner());
jlaskey@3 133 }
jlaskey@3 134
jlaskey@3 135 // the property is not found - now check for
jlaskey@3 136 // __noSuchProperty__ and __noSuchMethod__ in expression
jlaskey@3 137 if (self != null) {
attila@29 138 final String fallBack;
jlaskey@3 139
jlaskey@3 140 final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
jlaskey@3 141
jlaskey@3 142 switch (operator) {
jlaskey@3 143 case "callMethod":
attila@29 144 throw new AssertionError(); // Nashorn never emits callMethod
jlaskey@3 145 case "getMethod":
jlaskey@3 146 fallBack = NO_SUCH_METHOD_NAME;
jlaskey@3 147 break;
jlaskey@3 148 case "getProp":
jlaskey@3 149 case "getElem":
jlaskey@3 150 fallBack = NO_SUCH_PROPERTY_NAME;
jlaskey@3 151 break;
jlaskey@3 152 default:
jlaskey@3 153 fallBack = null;
jlaskey@3 154 break;
jlaskey@3 155 }
jlaskey@3 156
jlaskey@3 157 if (fallBack != null) {
jlaskey@3 158 find = self.findProperty(fallBack, true);
jlaskey@3 159 if (find != null) {
jlaskey@3 160 switch (operator) {
jlaskey@3 161 case "getMethod":
hannesw@51 162 link = self.noSuchMethod(desc, request);
jlaskey@3 163 break;
jlaskey@3 164 case "getProp":
jlaskey@3 165 case "getElem":
hannesw@51 166 link = self.noSuchProperty(desc, request);
jlaskey@3 167 break;
jlaskey@3 168 default:
jlaskey@3 169 break;
jlaskey@3 170 }
jlaskey@3 171 }
jlaskey@3 172 }
jlaskey@3 173
jlaskey@3 174 if (link != null) {
jlaskey@3 175 return fixExpressionCallSite(ndesc, link);
jlaskey@3 176 }
jlaskey@3 177 }
jlaskey@3 178
jlaskey@3 179 // still not found, may be scope can handle with it's own
jlaskey@3 180 // __noSuchProperty__, __noSuchMethod__ etc.
hannesw@51 181 link = scope.lookup(desc, request);
jlaskey@3 182
jlaskey@3 183 if (link != null) {
hannesw@766 184 return fixScopeCallSite(link, name, null);
jlaskey@3 185 }
jlaskey@3 186
jlaskey@3 187 return null;
jlaskey@3 188 }
jlaskey@3 189
attila@120 190 /**
attila@120 191 * Overridden to try to find the property first in the expression object (and its prototypes), and only then in this
attila@120 192 * object (and its prototypes).
attila@120 193 *
attila@120 194 * @param key Property key.
attila@120 195 * @param deep Whether the search should look up proto chain.
attila@120 196 * @param stopOnNonScope should a deep search stop on the first non-scope object?
attila@120 197 * @param start the object on which the lookup was originally initiated
attila@120 198 *
attila@120 199 * @return FindPropertyData or null if not found.
attila@120 200 */
attila@120 201 @Override
attila@120 202 FindProperty findProperty(final String key, final boolean deep, final boolean stopOnNonScope, final ScriptObject start) {
sundar@541 203 final FindProperty exprProperty = expression.findProperty(key, deep, stopOnNonScope, start);
sundar@541 204 if (exprProperty != null) {
sundar@541 205 return exprProperty;
attila@120 206 }
attila@120 207 return super.findProperty(key, deep, stopOnNonScope, start);
attila@120 208 }
attila@120 209
jlaskey@3 210 @Override
jlaskey@3 211 public void setSplitState(final int state) {
jlaskey@3 212 getNonWithParent().setSplitState(state);
jlaskey@3 213 }
jlaskey@3 214
jlaskey@3 215 @Override
jlaskey@3 216 public int getSplitState() {
jlaskey@3 217 return getNonWithParent().getSplitState();
jlaskey@3 218 }
jlaskey@3 219
jlaskey@3 220 /**
jlaskey@3 221 * Get first parent scope that is not an instance of WithObject.
jlaskey@3 222 */
jlaskey@3 223 private Scope getNonWithParent() {
sundar@541 224 ScriptObject proto = getParentScope();
jlaskey@3 225
jlaskey@3 226 while (proto != null && proto instanceof WithObject) {
sundar@541 227 proto = ((WithObject)proto).getParentScope();
jlaskey@3 228 }
jlaskey@3 229
jlaskey@3 230 assert proto instanceof Scope : "with scope without parent scope";
jlaskey@3 231 return (Scope) proto;
jlaskey@3 232 }
jlaskey@3 233
sundar@541 234
jlaskey@214 235 private static GuardedInvocation fixReceiverType(final GuardedInvocation link, final MethodHandle filter) {
jlaskey@214 236 // The receiver may be an Object or a ScriptObject.
jlaskey@214 237 final MethodType invType = link.getInvocation().type();
jlaskey@214 238 final MethodType newInvType = invType.changeParameterType(0, filter.type().returnType());
jlaskey@214 239 return link.asType(newInvType);
jlaskey@214 240 }
jlaskey@214 241
jlaskey@3 242 private static GuardedInvocation fixExpressionCallSite(final NashornCallSiteDescriptor desc, final GuardedInvocation link) {
jlaskey@3 243 // If it's not a getMethod, just add an expression filter that converts WithObject in "this" position to its
jlaskey@3 244 // expression.
jlaskey@3 245 if(!"getMethod".equals(desc.getFirstOperator())) {
jlaskey@214 246 return fixReceiverType(link, WITHEXPRESSIONFILTER).filterArguments(0, WITHEXPRESSIONFILTER);
jlaskey@3 247 }
jlaskey@3 248
attila@178 249 final MethodHandle linkInvocation = link.getInvocation();
attila@178 250 final MethodType linkType = linkInvocation.type();
attila@178 251 final boolean linkReturnsFunction = ScriptFunction.class.isAssignableFrom(linkType.returnType());
jlaskey@3 252 return link.replaceMethods(
jlaskey@3 253 // Make sure getMethod will bind the script functions it receives to WithObject.expression
attila@178 254 MH.foldArguments(linkReturnsFunction ? BIND_TO_EXPRESSION_FN : BIND_TO_EXPRESSION_OBJ,
attila@178 255 filter(linkInvocation.asType(linkType.changeReturnType(
attila@178 256 linkReturnsFunction ? ScriptFunction.class : Object.class)), WITHEXPRESSIONFILTER)),
jlaskey@3 257 // No clever things for the guard -- it is still identically filtered.
jlaskey@3 258 filterGuard(link, WITHEXPRESSIONFILTER));
jlaskey@3 259 }
jlaskey@3 260
hannesw@766 261 private GuardedInvocation fixScopeCallSite(final GuardedInvocation link, final String name, final ScriptObject owner) {
jlaskey@214 262 final GuardedInvocation newLink = fixReceiverType(link, WITHSCOPEFILTER);
sundar@541 263 return link.replaceMethods(filter(newLink.getInvocation(), WITHSCOPEFILTER),
hannesw@766 264 NashornGuards.combineGuards(expressionGuard(name, owner), filterGuard(newLink, WITHSCOPEFILTER)));
jlaskey@3 265 }
jlaskey@3 266
jlaskey@3 267 private static MethodHandle filterGuard(final GuardedInvocation link, final MethodHandle filter) {
jlaskey@3 268 final MethodHandle test = link.getGuard();
jlaskey@3 269 return test == null ? null : filter(test, filter);
jlaskey@3 270 }
jlaskey@3 271
jlaskey@3 272 private static MethodHandle filter(final MethodHandle mh, final MethodHandle filter) {
hannesw@769 273 return MH.filterArguments(mh, 0, filter.asType(filter.type().changeReturnType(mh.type().parameterType(0))));
jlaskey@3 274 }
jlaskey@3 275
jlaskey@3 276 /**
jlaskey@3 277 * Drops the WithObject wrapper from the expression.
jlaskey@3 278 * @param receiver WithObject wrapper.
jlaskey@3 279 * @return The with expression.
jlaskey@3 280 */
jlaskey@3 281 public static Object withFilterExpression(final Object receiver) {
jlaskey@3 282 return ((WithObject)receiver).expression;
jlaskey@3 283 }
jlaskey@3 284
jlaskey@3 285 @SuppressWarnings("unused")
jlaskey@3 286 private static Object bindToExpression(final Object fn, final Object receiver) {
attila@178 287 return fn instanceof ScriptFunction ? bindToExpression((ScriptFunction) fn, receiver) : fn;
attila@178 288 }
attila@178 289
attila@178 290 private static Object bindToExpression(final ScriptFunction fn, final Object receiver) {
attila@178 291 return fn.makeBoundFunction(withFilterExpression(receiver), new Object[0]);
jlaskey@3 292 }
jlaskey@3 293
hannesw@766 294 private MethodHandle expressionGuard(final String name, final ScriptObject owner) {
sundar@541 295 final PropertyMap map = expression.getMap();
hannesw@766 296 final SwitchPoint sp = expression.getProtoSwitchPoint(name, owner);
sundar@541 297 return MH.insertArguments(WITHEXPRESSIONGUARD, 1, map, sp);
sundar@541 298 }
sundar@541 299
sundar@541 300 @SuppressWarnings("unused")
sundar@541 301 private static boolean withExpressionGuard(final Object receiver, final PropertyMap map, final SwitchPoint sp) {
sundar@541 302 return ((WithObject)receiver).expression.getMap() == map && (sp == null || !sp.hasBeenInvalidated());
sundar@541 303 }
sundar@541 304
jlaskey@3 305 /**
jlaskey@3 306 * Drops the WithObject wrapper from the scope.
jlaskey@3 307 * @param receiver WithObject wrapper.
jlaskey@3 308 * @return The with scope.
jlaskey@3 309 */
jlaskey@3 310 public static Object withFilterScope(final Object receiver) {
jlaskey@3 311 return ((WithObject)receiver).getProto();
jlaskey@3 312 }
jlaskey@3 313
jlaskey@3 314 /**
jlaskey@3 315 * Get the with expression for this {@code WithObject}
jlaskey@3 316 * @return the with expression
jlaskey@3 317 */
sundar@541 318 public ScriptObject getExpression() {
jlaskey@3 319 return expression;
jlaskey@3 320 }
jlaskey@3 321
lagergren@610 322 /**
lagergren@610 323 * Get the parent scope for this {@code WithObject}
lagergren@610 324 * @return the parent scope
lagergren@610 325 */
sundar@541 326 public ScriptObject getParentScope() {
sundar@541 327 return getProto();
sundar@541 328 }
sundar@541 329
jlaskey@3 330 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
jlaskey@3 331 return MH.findStatic(MethodHandles.lookup(), WithObject.class, name, MH.type(rtype, types));
jlaskey@3 332 }
jlaskey@3 333 }

mercurial