# HG changeset patch # User attila # Date 1445448838 -7200 # Node ID 23abd10384a505c9f7aa595f868614da87d05581 # Parent 86b4260bb17ad1f3a3dad6ea16af4cca14149410 8139931: Introduce Operation objects in Dynalink instead of string encoding Reviewed-by: hannesw, sundar diff -r 86b4260bb17a -r 23abd10384a5 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/CallSiteDescriptor.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/CallSiteDescriptor.java Wed Oct 21 10:42:20 2015 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/CallSiteDescriptor.java Wed Oct 21 19:33:58 2015 +0200 @@ -86,19 +86,13 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.NoSuchElementException; import java.util.Objects; -import java.util.StringTokenizer; -import jdk.internal.dynalink.support.NameCodec; /** * Call site descriptors contain all the information necessary for linking a * call site. This information is normally passed as parameters to bootstrap * methods and consists of the {@code MethodHandles.Lookup} object on the caller - * class in which the call site occurs, the method name mentioned in the call + * class in which the call site occurs, the dynamic operation at the call * site, and the method type of the call site. {@code CallSiteDescriptor} * objects are used in Dynalink to capture and store these parameters for * subsequent use by the {@link DynamicLinker}. @@ -109,13 +103,12 @@ * Call site descriptors must be immutable. You can use this class as-is or you * can subclass it, especially if you need to add further information to the * descriptors (typically, values passed in additional parameters to the - * bootstrap method) or want to cache results of name tokenization. Since the - * descriptors must be immutable, you can set up a cache for equivalent - * descriptors to have the call sites share them. + * bootstrap method. Since the descriptors must be immutable, you can set up a + * cache for equivalent descriptors to have the call sites share them. */ public class CallSiteDescriptor { private final MethodHandles.Lookup lookup; - private final String name; + private final Operation operation; private final MethodType methodType; /** @@ -127,98 +120,23 @@ private static final RuntimePermission GET_LOOKUP_PERMISSION = new RuntimePermission(GET_LOOKUP_PERMISSION_NAME); /** - * The index of the name token that will carry the operation scheme prefix, - * e.g. {@code "dyn"} for operations specified by Dynalink itself. - */ - public static final int SCHEME = 0; - - /** - * The index of the name token that carries the operation name, at least - * when using the {@code "dyn"} scheme. - */ - - public static final int OPERATOR = 1; - - /** - * The index of the name token that carries the name of an operand (e.g. a - * property or a method), at least when using the {@code "dyn"} scheme. - */ - public static final int NAME_OPERAND = 2; - - /** - * String used to delimit tokens in a call site name; its value is - * {@code ":"}, that is the colon character. - */ - public static final String TOKEN_DELIMITER = ":"; - - /** - * String used to delimit operation names in a composite operation name; - * its value is {@code "|"}, that is the pipe character. - */ - public static final String OPERATOR_DELIMITER = "|"; - - /** * Creates a new call site descriptor. * @param lookup the lookup object describing the class the call site belongs to. - * @param name the name of the method at the call site. + * @param operation the dynamic operation at the call site. * @param methodType the method type of the call site. */ - public CallSiteDescriptor(final Lookup lookup, final String name, final MethodType methodType) { + public CallSiteDescriptor(final Lookup lookup, final Operation operation, final MethodType methodType) { this.lookup = Objects.requireNonNull(lookup, "lookup"); - this.name = Objects.requireNonNull(name, "name"); + this.operation = Objects.requireNonNull(operation, "name"); this.methodType = Objects.requireNonNull(methodType, "methodType"); } /** - * Returns the number of tokens in the name of the method at the call site. - * Method names are tokenized with the {@link #TOKEN_DELIMITER} character - * character, e.g. {@code "dyn:getProp:color"} would be the name used to - * describe a method that retrieves the property named "color" on the object - * it is invoked on. This method will count the tokens in the name on every - * invocation. Subclasses can override this method with a more efficient - * implementation that caches the tokens. - * @return the number of tokens in the name of the method at the call site. + * Returns the operation at the call site. + * @return the operation at the call site. */ - public int getNameTokenCount() { - return getNameTokenizer().countTokens(); - } - - /** - * Returns the ith token in the method name at the call - * site. Method names are tokenized with the {@link #TOKEN_DELIMITER} - * character. This method will tokenize the name on every invocation. - * Subclasses can override this method with a more efficient implementation - * that caches the tokens. - * @param i the index of the token. Must be between 0 (inclusive) and - * {@link #getNameTokenCount()} (exclusive). - * @throws NoSuchElementException if the index is outside the allowed - * range. - * @return the ith token in the method name at the call - * site. - */ - public String getNameToken(final int i) { - final StringTokenizer tok = getNameTokenizer(); - for (int j = 0; j < i; ++j) { - tok.nextToken(); - } - final String token = tok.nextToken(); - return (i > 1 ? NameCodec.decode(token) : token).intern(); - } - - private StringTokenizer getNameTokenizer() { - return getNameTokenizer(name); - } - - private static StringTokenizer getNameTokenizer(final String name) { - return new StringTokenizer(name, CallSiteDescriptor.TOKEN_DELIMITER); - } - - /** - * Returns the full (untokenized) name of the method at the call site. - * @return the full (untokenized) name of the method at the call site. - */ - public final String getName() { - return name; + public final Operation getOperation() { + return operation; } /** @@ -260,11 +178,16 @@ /** * Creates a new call site descriptor from this descriptor, which is - * identical to this, except it changes the method type. Subclasses must - * override the + * identical to this, except it changes the method type. Invokes + * {@link #changeMethodTypeInternal(MethodType)} and checks that it returns + * a descriptor of the same class as this descriptor. * * @param newMethodType the new method type * @return a new call site descriptor, with the method type changed. + * @throws RuntimeException if {@link #changeMethodTypeInternal(MethodType)} + * returned a descriptor of different class than this object. + * @throws NullPointerException if {@link #changeMethodTypeInternal(MethodType)} + * returned null. */ public final CallSiteDescriptor changeMethodType(final MethodType newMethodType) { final CallSiteDescriptor changed = Objects.requireNonNull( @@ -288,53 +211,16 @@ * @return a new call site descriptor, with the method type changed. */ protected CallSiteDescriptor changeMethodTypeInternal(final MethodType newMethodType) { - return new CallSiteDescriptor(lookup, name, newMethodType); + return new CallSiteDescriptor(lookup, operation, newMethodType); } /** - * Tokenizes a composite operation name of this descriptor along - * {@link #OPERATOR_DELIMITER} characters. E.g. if this descriptor's name is - * {@code "dyn:getElem|getProp|getMethod"}, then it returns a list of - * {@code ["getElem", "getProp", "getMethod"]}. - * @return a list of operator tokens. + * Returns true if this call site descriptor is equal to the passed object. + * It is considered equal if the other object is of the exact same class, + * their operations and method types are equal, and their lookups have the + * same {@link java.lang.invoke.MethodHandles.Lookup#lookupClass()} and + * {@link java.lang.invoke.MethodHandles.Lookup#lookupModes()}. */ - public final List tokenizeOperators() { - final String ops = getNameToken(CallSiteDescriptor.OPERATOR); - final StringTokenizer tok = new StringTokenizer(ops, CallSiteDescriptor.OPERATOR_DELIMITER); - final int count = tok.countTokens(); - if(count == 1) { - return Collections.singletonList(ops); - } - final String[] tokens = new String[count]; - for(int i = 0; i < count; ++i) { - tokens[i] = tok.nextToken(); - } - return Arrays.asList(tokens); - } - - /** - * Tokenizes the composite name along {@link #TOKEN_DELIMITER} characters, - * as well as {@link NameCodec#decode(String) demangles} and interns the - * tokens. The first two tokens are not demangled as they are supposed to - * be the naming scheme and the name of the operation which can be expected - * to consist of just alphabetical characters. - * @param name the composite name consisting of - * {@link #TOKEN_DELIMITER}-separated, possibly mangled tokens. - * @return an array of unmangled, interned tokens. - */ - public static String[] tokenizeName(final String name) { - final StringTokenizer tok = getNameTokenizer(name); - final String[] tokens = new String[tok.countTokens()]; - for(int i = 0; i < tokens.length; ++i) { - String token = tok.nextToken(); - if(i > 1) { - token = NameCodec.decode(token); - } - tokens[i] = token.intern(); - } - return tokens; - } - @Override public boolean equals(final Object obj) { if (obj == this) { @@ -345,8 +231,9 @@ return false; } final CallSiteDescriptor other = (CallSiteDescriptor)obj; - return name.equals(other.name) && methodType.equals(other.methodType) && - lookupsEqual(lookup, other.lookup); + return operation.equals(other.operation) && + methodType.equals(other.methodType) && + lookupsEqual(lookup, other.lookup); } /** @@ -362,9 +249,15 @@ return l1.lookupClass() == l2.lookupClass() && l1.lookupModes() == l2.lookupModes(); } + /** + * Returns a value-based hash code of this call site descriptor computed + * from its operation, method type, and lookup object's lookup class and + * lookup modes. + * @return value-based hash code for this call site descriptor. + */ @Override public int hashCode() { - return name.hashCode() + 31 * methodType.hashCode() + 31 * 31 * lookupHashCode(lookup); + return operation.hashCode() + 31 * methodType.hashCode() + 31 * 31 * lookupHashCode(lookup); } /** @@ -375,7 +268,7 @@ * @param lookup the lookup object. * @return a hash code for the object.. */ - protected static int lookupHashCode(final Lookup lookup) { + private static int lookupHashCode(final Lookup lookup) { return lookup.lookupClass().hashCode() + 31 * lookup.lookupModes(); } @@ -387,7 +280,8 @@ public String toString() { final String mt = methodType.toString(); final String l = lookup.toString(); - final StringBuilder b = new StringBuilder(name.length() + mt.length() + 1 + l.length()); - return b.append(name).append(mt).append("@").append(l).toString(); + final String o = operation.toString(); + final StringBuilder b = new StringBuilder(o.length() + mt.length() + 1 + l.length()); + return b.append(o).append(mt).append('@').append(l).toString(); } } diff -r 86b4260bb17a -r 23abd10384a5 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/CompositeOperation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/CompositeOperation.java Wed Oct 21 19:33:58 2015 +0200 @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file, and Oracle licenses the original version of this file under the BSD + * license: + */ +/* + Copyright 2015 Attila Szegedi + + Licensed under both the Apache License, Version 2.0 (the "Apache License") + and the BSD License (the "BSD License"), with licensee being free to + choose either of the two at their discretion. + + You may not use this file except in compliance with either the Apache + License or the BSD License. + + If you choose to use this file in compliance with the Apache License, the + following notice applies to you: + + You may obtain a copy of the Apache License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + If you choose to use this file in compliance with the BSD License, the + following notice applies to you: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package jdk.internal.dynalink; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Describes an operation that is composed of at least two other operations. The + * component operations are treated as alternatives to each other in order of + * preference. The semantics of the composite operation is "first successful". + * That is, a composite of {@code GET_PROPERTY|GET_ELEMENT:color} should be + * interpreted as get the property named "color" on the object, but if the + * property does not exist, then get the collection element named "color" + * instead. + *

+ * Composite operations are helpful in implementation of languages that + * don't distinguish between one or more of the property, method, and element + * namespaces, or when expressing operations against objects that can be + * considered both ordinary objects and collections, e.g. Java + * {@link java.util.Map} objects. A composite operation + * {@code GET_PROPERTY|GET_ELEMENT:empty} against a Java map will always match + * the {@link java.util.Map#isEmpty()} property, but + * {@code GET_ELEMENT|GET_PROPERTY:empty} will actually match a map element with + * key {@code "empty"} if the map contains that key, and only fall back to the + * {@code isEmpty()} property getter if the map does not contain the key. If + * the source language mandates this semantics, it can be easily achieved using + * composite operations. + *

+ * Even if the language itself doesn't distinguish between some of the + * namespaces, it can be helpful to map different syntaxes to different + * compositions. E.g. the source expression {@code obj.color} could map to + * {@code GET_PROPERTY|GET_ELEMENT|GET_METHOD:color}, but a different source + * expression that looks like collection element access {@code obj[key]} could + * be expressed instead as {@code GET_ELEMENT|GET_PROPERTY|GET_METHOD}. + * Finally, if the retrieved value is subsequently called, then it makes sense + * to bring {@code GET_METHOD} to the front of the list: the getter part of the + * source expression {@code obj.color()} should be + * {@code GET_METHOD|GET_PROPERTY|GET_ELEMENT:color} and the one for + * {@code obj[key]()} should be {@code GET_METHOD|GET_ELEMENT|GET_PROPERTY}. + *

+ * The elements of a composite operation can not be composites or named + * operations, but rather simple operations such are elements of + * {@link StandardOperation}. A composite operation itself can serve as the base + * operation of a named operation, though; a typical way to construct e.g. the + * {@code GET_ELEMENT|GET_PROPERTY:empty} from above would be: + *

+ * Operation getElementOrPropertyEmpty = new NamedOperation(
+ *     new CompositeOperation(
+ *         StandardOperation.GET_ELEMENT,
+ *         StandardOperation.GET_PROPERTY),
+ *     "empty");
+ * 
+ *

+ * Not all compositions make sense. Typically, any combination in any order of + * standard getter operations {@code GET_PROPERTY}, {@code GET_ELEMENT}, and + * {@code GET_METHOD} make sense, as do combinations of {@code SET_PROPERTY} and + * {@code SET_ELEMENT}; other standard operations should not be combined. The + * constructor will allow any combination of operations, though. + */ +public class CompositeOperation implements Operation { + private final Operation[] operations; + + /** + * Constructs a new composite operation. + * @param operations the components for this composite operation. The passed + * array will be cloned. + * @throws IllegalArgumentException if less than two components are + * specified, or any component is itself a {@link CompositeOperation} or a + * {@link NamedOperation}. + * @throws NullPointerException if either the operations array or any of its + * elements are {@code null}. + */ + public CompositeOperation(final Operation... operations) { + Objects.requireNonNull(operations, "operations array is null"); + if (operations.length < 2) { + throw new IllegalArgumentException("Must have at least two operations"); + } + final Operation[] clonedOps = operations.clone(); + for(int i = 0; i < clonedOps.length; ++i) { + final Operation op = clonedOps[i]; + if (op == null) { + throw new NullPointerException("operations[" + i + "] is null"); + } else if (op instanceof NamedOperation) { + throw new IllegalArgumentException("operations[" + i + "] is a NamedOperation"); + } else if (op instanceof CompositeOperation) { + throw new IllegalArgumentException("operations[" + i + "] is a CompositeOperation"); + } + } + this.operations = clonedOps; + } + + /** + * Returns the component operations in this composite operation. The + * returned array is a copy and changes to it don't have effect on this + * object. + * @return the component operations in this composite operation. + */ + public Operation[] getOperations() { + return operations.clone(); + } + + /** + * Returns the number of component operations in this composite operation. + * @return the number of component operations in this composite operation. + */ + public int getOperationCount() { + return operations.length; + } + + /** + * Returns the i-th component operation in this composite operation. + * @param i the operation index + * @return the i-th component operation in this composite operation. + * @throws IndexOutOfBoundsException if the index is out of range. + */ + public Operation getOperation(final int i) { + try { + return operations[i]; + } catch (final ArrayIndexOutOfBoundsException e) { + throw new IndexOutOfBoundsException(Integer.toString(i)); + } + } + + /** + * Returns true if the other object is also a composite operation and their + * component operations are equal. + * @param obj the object to compare to + * @return true if this object is equal to the other one, false otherwise. + */ + @Override + public boolean equals(final Object obj) { + if (obj == null || obj.getClass() != CompositeOperation.class) { + return false; + } + return Arrays.equals(operations, ((CompositeOperation)obj).operations); + } + + /** + * Returns the hash code of this composite operation. Defined to be equal + * to {@code java.util.Arrays.hashCode(operations)}. + */ + @Override + public int hashCode() { + return Arrays.hashCode(operations); + }; + + /** + * Returns the string representation of this composite operation. Defined to + * be the {@code toString} of its component operations, each separated by + * the vertical line character (e.g. {@code "GET_PROPERTY|GET_ELEMENT"}). + * @return the string representation of this composite operation. + */ + @Override + public String toString() { + final StringBuilder b = new StringBuilder(); + b.append(operations[0]); + for(int i = 1; i < operations.length; ++i) { + b.append('|').append(operations[i]); + } + return b.toString(); + } + + /** + * Returns the components of the passed operation if it is a composite + * operation, otherwise returns an array containing the operation itself. + * This allows for returning an array of component even if it is not known + * whether the operation is itself a composite (treating a non-composite + * operation as if it were a single-element composite of itself). + * @param op the operation whose components are retrieved. + * @return if the passed operation is a composite operation, returns its + * {@link #getOperations()}, otherwise returns the operation itself. + */ + public static Operation[] getOperations(final Operation op) { + return op instanceof CompositeOperation + ? ((CompositeOperation)op).operations.clone() + : new Operation[] { op }; + } +} diff -r 86b4260bb17a -r 23abd10384a5 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/NamedOperation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/NamedOperation.java Wed Oct 21 19:33:58 2015 +0200 @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file, and Oracle licenses the original version of this file under the BSD + * license: + */ +/* + Copyright 2015 Attila Szegedi + + Licensed under both the Apache License, Version 2.0 (the "Apache License") + and the BSD License (the "BSD License"), with licensee being free to + choose either of the two at their discretion. + + You may not use this file except in compliance with either the Apache + License or the BSD License. + + If you choose to use this file in compliance with the Apache License, the + following notice applies to you: + + You may obtain a copy of the Apache License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + If you choose to use this file in compliance with the BSD License, the + following notice applies to you: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package jdk.internal.dynalink; + +import java.util.Objects; + +/** + * Operation that associates a name with another operation. Typically used with + * operations that normally take a name or an index to bind them to a fixed + * name. E.g. {@code new NamedOperation(StandardOperation.GET_PROPERTY, "color")} + * will be a named operation for getting the property named "color" on the + * object it is applied to, and + * {@code new NamedOperation(StandardOperation.GET_ELEMENT, 3)} will be a named + * operation for getting the element at index 3 from the collection it is + * applied to. In these cases, the expected signature of the call site for the + * operation will change to no longer include the name parameter. Specifically, + * the documentation for all {@link StandardOperation} members describes how + * they are affected by being incorporated into a named operation. + */ +public class NamedOperation implements Operation { + private final Operation baseOperation; + private final Object name; + + /** + * Creates a new named operation. + * @param baseOperation the base operation that is associated with a name. + * @param name the name associated with the base operation. Note that the + * name is not necessarily a string, but can be an arbitrary object. As the + * name is used for addressing, it can be an {@link Integer} when meant + * to be used as an index into an array or list etc. + * @throws NullPointerException if either {@code baseOperation} or + * {@code name} is null. + * @throws IllegalArgumentException if {@code baseOperation} is itself a + * {@code NamedOperation}. + */ + public NamedOperation(final Operation baseOperation, final Object name) { + if (baseOperation instanceof NamedOperation) { + throw new IllegalArgumentException("baseOperation is a named operation"); + } + this.baseOperation = Objects.requireNonNull(baseOperation, "baseOperation is null"); + this.name = Objects.requireNonNull(name, "name is null"); + } + + /** + * Returns the base operation of this named operation. + * @return the base operation of this named operation. + */ + public Operation getBaseOperation() { + return baseOperation; + } + + /** + * Returns the name of this named operation. + * @return the name of this named operation. + */ + public Object getName() { + return name; + } + + /** + * Compares this named operation to another object. Returns true if the + * other object is also a named operation, and both their base operations + * and name are equal. + */ + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } else if(obj.getClass() != NamedOperation.class) { + return false; + } + final NamedOperation other = (NamedOperation)obj; + return baseOperation.equals(other.baseOperation) && name.equals(other.name); + } + + /** + * Returns the hash code of this named operation. It is defined to be equal + * to {@code baseOperation.hashCode() + 31 * name.hashCode()}. + */ + @Override + public int hashCode() { + return baseOperation.hashCode() + 31 * name.hashCode(); + } + + /** + * Returns the string representation of this named operation. It is defined + * to be equal to {@code baseOperation.toString() + ":" + name.toString()}. + */ + @Override + public String toString() { + return baseOperation.toString() + ":" + name.toString(); + } + + /** + * If the passed operation is a named operation, returns its + * {@link #getBaseOperation()}, otherwise returns the operation as is. + * @param op the operation + * @return the base operation of the passed operation. + */ + public static Operation getBaseOperation(final Operation op) { + return op instanceof NamedOperation ? ((NamedOperation)op).baseOperation : op; + } + + /** + * If the passed operation is a named operation, returns its + * {@link #getName()}, otherwise returns null. Note that a named operation + * object can never have a null name, therefore returning null is indicative + * that the passed operation is not, in fact, a named operation. + * @param op the operation + * @return the name in the passed operation, or null if it is not a named + * operation. + */ + public static Object getName(final Operation op) { + return op instanceof NamedOperation ? ((NamedOperation)op).name : null; + } +} diff -r 86b4260bb17a -r 23abd10384a5 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/Operation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/Operation.java Wed Oct 21 19:33:58 2015 +0200 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file, and Oracle licenses the original version of this file under the BSD + * license: + */ +/* + Copyright 2015 Attila Szegedi + + Licensed under both the Apache License, Version 2.0 (the "Apache License") + and the BSD License (the "BSD License"), with licensee being free to + choose either of the two at their discretion. + + You may not use this file except in compliance with either the Apache + License or the BSD License. + + If you choose to use this file in compliance with the Apache License, the + following notice applies to you: + + You may obtain a copy of the Apache License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + If you choose to use this file in compliance with the BSD License, the + following notice applies to you: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package jdk.internal.dynalink; + +/** + * An object that describes a dynamic operation. Dynalink defines a set of + * standard operations with the {@link StandardOperation} class, as well as a + * way to attach a fixed name to an operation using {@link NamedOperation} and + * to express a set of alternative operations using {@link CompositeOperation}. + * When presenting examples in this documentation, we will refer to standard + * operations using their name (e.g. {@code GET_PROPERTY}), to composite + * operations by separating their components with the vertical line character + * (e.g. {@code GET_PROPERTY|GET_ELEMENT}), and finally to named operations by + * separating the base operation and the name with the colon character (e.g. + * {@code GET_PROPERTY|GET_ELEMENT:color}). + */ +public interface Operation { +} diff -r 86b4260bb17a -r 23abd10384a5 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/StandardOperation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/StandardOperation.java Wed Oct 21 19:33:58 2015 +0200 @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file, and Oracle licenses the original version of this file under the BSD + * license: + */ +/* + Copyright 2015 Attila Szegedi + + Licensed under both the Apache License, Version 2.0 (the "Apache License") + and the BSD License (the "BSD License"), with licensee being free to + choose either of the two at their discretion. + + You may not use this file except in compliance with either the Apache + License or the BSD License. + + If you choose to use this file in compliance with the Apache License, the + following notice applies to you: + + You may obtain a copy of the Apache License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + If you choose to use this file in compliance with the BSD License, the + following notice applies to you: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package jdk.internal.dynalink; + +/** + * Defines the standard dynamic operations. Getter and setter operations defined + * in this enumeration can be composed into a {@link CompositeOperation}, and + * {@link NamedOperation} can be used to bind the name parameter of operations + * that take one, in which case it disappears from the type signature. + */ +public enum StandardOperation implements Operation { + /** + * Get the value of a property defined on an object. Call sites with this + * operation should have a signature of + * (receiver, propertyName)→value or + * (receiver)→value when used with {@link NamedOperation}, with + * all parameters and return type being of any type (either primitive or + * reference). + */ + GET_PROPERTY, + /** + * Set the value of a property defined on an object. Call sites with this + * operation should have a signature of + * (receiver, propertyName, value)→void or + * (receiver, value)→void when used with {@link NamedOperation}, + * with all parameters and return type being of any type (either primitive + * or reference). + */ + SET_PROPERTY, + /** + * Get the value of an element of a collection. Call sites with this + * operation should have a signature of + * (receiver, index)→value or + * (receiver)→value when used with {@link NamedOperation}, with + * all parameters and return type being of any type (either primitive or + * reference). + */ + GET_ELEMENT, + /** + * Set the value of an element of a collection. Call sites with this + * operation should have a signature of + * (receiver, index, value)→void or + * (receiver, value)→void when used with {@link NamedOperation}, + * with all parameters and return type being of any type (either primitive + * or reference). + */ + SET_ELEMENT, + /** + * Get the length of an array of size of a collection. Call sites with + * this operation should have a signature of (receiver)→value, + * with all parameters and return type being of any type (either primitive + * or reference). + */ + GET_LENGTH, + /** + * Gets an object representing a method defined on an object. Call sites + * with this operation should have a signature of + * (receiver, methodName)→value, or + * (receiver)→value when used with {@link NamedOperation} + * with all parameters and return type being of any type (either primitive + * or reference). + */ + GET_METHOD, + /** + * Calls a method defined on an object. Call sites with this + * operation should have a signature of + * (receiver, methodName, arguments...)→value or + * (receiver, arguments...)→value when used with {@link NamedOperation}, + * with all parameters and return type being of any type (either primitive + * or reference). + */ + CALL_METHOD, + /** + * Calls a callable object. Call sites with this operation should have a + * signature of (receiver, arguments...)→value, with all + * parameters and return type being of any type (either primitive or + * reference). Typically, if the callable is a method of an object, the + * first argument will act as the "this" value passed to the called method. + * The CALL operation is allowed to be used with a + * {@link NamedOperation} even though it does not take a name. Using it with + * a named operation won't affect its signature; the name is solely meant to + * be used as a diagnostic description for error messages. + */ + CALL, + /** + * Calls a constructor object. Call sites with this operation should have a + * signature of (receiver, arguments...)→value, with all + * parameters and return type being of any type (either primitive or + * reference). The NEW operation is allowed to be used with a + * {@link NamedOperation} even though it does not take a name. Using it with + * a named operation won't affect its signature; the name is solely meant to + * be used as a diagnostic description for error messages. + */ + NEW +} diff -r 86b4260bb17a -r 23abd10384a5 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java Wed Oct 21 10:42:20 2015 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java Wed Oct 21 19:33:58 2015 +0200 @@ -92,12 +92,17 @@ import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import jdk.internal.dynalink.CallSiteDescriptor; +import jdk.internal.dynalink.CompositeOperation; +import jdk.internal.dynalink.NamedOperation; +import jdk.internal.dynalink.Operation; +import jdk.internal.dynalink.StandardOperation; import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType; import jdk.internal.dynalink.internal.InternalTypeUtilities; import jdk.internal.dynalink.linker.GuardedInvocation; @@ -342,17 +347,27 @@ @Override public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices) throws Exception { - // BeansLinker already checked that the name is at least 2 elements long and the first element is "dyn". final CallSiteDescriptor callSiteDescriptor = request.getCallSiteDescriptor(); - final String op = callSiteDescriptor.getNameToken(CallSiteDescriptor.OPERATOR); - // Either dyn:callMethod:name(this[,args]) or dyn:callMethod(this,name[,args]). - if("callMethod" == op) { - return getCallPropWithThis(callSiteDescriptor, linkerServices); + + // Handle NamedOperation(CALL_METHOD, name) separately + final Operation operation = callSiteDescriptor.getOperation(); + if (operation instanceof NamedOperation) { + final NamedOperation namedOperation = (NamedOperation)operation; + if (namedOperation.getBaseOperation() == StandardOperation.CALL_METHOD) { + return createGuardedDynamicMethodInvocation(callSiteDescriptor, + linkerServices, namedOperation.getName().toString(), methods); + } } - List operations = callSiteDescriptor.tokenizeOperators(); + + List operations = Arrays.asList( + CompositeOperation.getOperations( + NamedOperation.getBaseOperation(operation))); + final Object name = NamedOperation.getName(operation); + while(!operations.isEmpty()) { - final GuardedInvocationComponent gic = getGuardedInvocationComponent(callSiteDescriptor, linkerServices, - operations); + final GuardedInvocationComponent gic = + getGuardedInvocationComponent(callSiteDescriptor, + linkerServices, operations, name); if(gic != null) { return gic.getGuardedInvocation(); } @@ -361,23 +376,26 @@ return null; } - protected GuardedInvocationComponent getGuardedInvocationComponent(final CallSiteDescriptor callSiteDescriptor, - final LinkerServices linkerServices, final List operations) throws Exception { + protected GuardedInvocationComponent getGuardedInvocationComponent( + final CallSiteDescriptor callSiteDescriptor, + final LinkerServices linkerServices, + final List operations, final Object name) + throws Exception { if(operations.isEmpty()) { return null; } - final String op = operations.get(0); - // Either dyn:getProp:name(this) or dyn:getProp(this, name) - if("getProp".equals(op)) { - return getPropertyGetter(callSiteDescriptor, linkerServices, pop(operations)); + final Operation op = operations.get(0); + // Either GET_PROPERTY:name(this) or GET_PROPERTY(this, name) + if(op == StandardOperation.GET_PROPERTY) { + return getPropertyGetter(callSiteDescriptor, linkerServices, pop(operations), name); } - // Either dyn:setProp:name(this, value) or dyn:setProp(this, name, value) - if("setProp".equals(op)) { - return getPropertySetter(callSiteDescriptor, linkerServices, pop(operations)); + // Either SET_PROPERTY:name(this, value) or SET_PROPERTY(this, name, value) + if(op == StandardOperation.SET_PROPERTY) { + return getPropertySetter(callSiteDescriptor, linkerServices, pop(operations), name); } - // Either dyn:getMethod:name(this), or dyn:getMethod(this, name) - if("getMethod".equals(op)) { - return getMethodGetter(callSiteDescriptor, linkerServices, pop(operations)); + // Either GET_METHOD:name(this), or GET_METHOD(this, name) + if(op == StandardOperation.GET_METHOD) { + return getMethodGetter(callSiteDescriptor, linkerServices, pop(operations), name); } return null; } @@ -406,18 +424,6 @@ return Guards.asType(assignableGuard, type); } - private GuardedInvocation getCallPropWithThis(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) { - switch(callSiteDescriptor.getNameTokenCount()) { - case 3: { - return createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices, - callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), methods); - } - default: { - return null; - } - } - } - private GuardedInvocation createGuardedDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices, final String methodName, final Map methodMap){ final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap); @@ -480,84 +486,86 @@ MethodHandles.constant(Object.class, null), 0, MethodHandle.class); private GuardedInvocationComponent getPropertySetter(final CallSiteDescriptor callSiteDescriptor, - final LinkerServices linkerServices, final List operations) throws Exception { - switch(callSiteDescriptor.getNameTokenCount()) { - case 2: { - // Must have three arguments: target object, property name, and property value. - assertParameterCount(callSiteDescriptor, 3); + final LinkerServices linkerServices, final List operations, final Object name) throws Exception { + if (name == null) { + return getUnnamedPropertySetter(callSiteDescriptor, linkerServices, operations); + } + return getNamedPropertySetter(callSiteDescriptor, linkerServices, operations, name); + } - // We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be - // valid for us to convert return values proactively. Also, since we don't know what setters will be - // invoked, we'll conservatively presume Object return type. The one exception is void return. - final MethodType origType = callSiteDescriptor.getMethodType(); - final MethodType type = origType.returnType() == void.class ? origType : origType.changeReturnType(Object.class); + private GuardedInvocationComponent getUnnamedPropertySetter(final CallSiteDescriptor callSiteDescriptor, + final LinkerServices linkerServices, final List operations) throws Exception { + // Must have three arguments: target object, property name, and property value. + assertParameterCount(callSiteDescriptor, 3); + + // We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be + // valid for us to convert return values proactively. Also, since we don't know what setters will be + // invoked, we'll conservatively presume Object return type. The one exception is void return. + final MethodType origType = callSiteDescriptor.getMethodType(); + final MethodType type = origType.returnType() == void.class ? origType : origType.changeReturnType(Object.class); - // What's below is basically: - // foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation), - // get_setter_handle(type, linkerServices)) - // only with a bunch of method signature adjustments. Basically, retrieve method setter - // MethodHandle; if it is non-null, invoke it, otherwise either return null, or delegate to next - // component's invocation. + // What's below is basically: + // foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation), + // get_setter_handle(type, linkerServices)) + // only with a bunch of method signature adjustments. Basically, retrieve method setter + // MethodHandle; if it is non-null, invoke it, otherwise either return null, or delegate to next + // component's invocation. - // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll - // abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using - // Object return type). - final MethodType setterType = type.dropParameterTypes(1, 2); - // Bind property setter handle to the expected setter type and linker services. Type is - // MethodHandle(Object, String, Object) - final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0, - callSiteDescriptor.changeMethodType(setterType), linkerServices); + // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll + // abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using + // Object return type). + final MethodType setterType = type.dropParameterTypes(1, 2); + // Bind property setter handle to the expected setter type and linker services. Type is + // MethodHandle(Object, String, Object) + final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0, + callSiteDescriptor.changeMethodType(setterType), linkerServices); - // Cast getter to MethodHandle(O, N, V) - final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType( - MethodHandle.class)); + // Cast getter to MethodHandle(O, N, V) + final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType( + MethodHandle.class)); - // Handle to invoke the setter R(MethodHandle, O, V) - final MethodHandle invokeHandle = MethodHandles.exactInvoker(setterType); - // Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V) - final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType( - 1)); - final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor, - linkerServices, operations); + // Handle to invoke the setter R(MethodHandle, O, V) + final MethodHandle invokeHandle = MethodHandles.exactInvoker(setterType); + // Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V) + final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType( + 1)); + final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor, + linkerServices, operations, null); - final MethodHandle fallbackFolded; - if(nextComponent == null) { - // Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null - fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1, - type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class)); - } else { - // Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the - // extra argument resulting from fold - fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(), - 0, MethodHandle.class); - } + final MethodHandle fallbackFolded; + if(nextComponent == null) { + // Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null + fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1, + type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class)); + } else { + // Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the + // extra argument resulting from fold + fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(), + 0, MethodHandle.class); + } - // fold(R(MethodHandle, O, N, V), MethodHandle(O, N, V)) - final MethodHandle compositeSetter = MethodHandles.foldArguments(MethodHandles.guardWithTest( - IS_METHOD_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter); - if(nextComponent == null) { - return getClassGuardedInvocationComponent(compositeSetter, type); - } - return nextComponent.compose(compositeSetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); - } - case 3: { - // Must have two arguments: target object and property value - assertParameterCount(callSiteDescriptor, 2); - final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices, - callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), propertySetters); - // If we have a property setter with this name, this composite operation will always stop here - if(gi != null) { - return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS); - } - // If we don't have a property setter with this name, always fall back to the next operation in the - // composite (if any) - return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, operations); - } - default: { - // More than two name components; don't know what to do with it. - return null; - } + // fold(R(MethodHandle, O, N, V), MethodHandle(O, N, V)) + final MethodHandle compositeSetter = MethodHandles.foldArguments(MethodHandles.guardWithTest( + IS_METHOD_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter); + if(nextComponent == null) { + return getClassGuardedInvocationComponent(compositeSetter, type); } + return nextComponent.compose(compositeSetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); + } + + private GuardedInvocationComponent getNamedPropertySetter(final CallSiteDescriptor callSiteDescriptor, + final LinkerServices linkerServices, final List operations, final Object name) throws Exception { + // Must have two arguments: target object and property value + assertParameterCount(callSiteDescriptor, 2); + final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices, + name.toString(), propertySetters); + // If we have a property setter with this name, this composite operation will always stop here + if(gi != null) { + return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS); + } + // If we don't have a property setter with this name, always fall back to the next operation in the + // composite (if any) + return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, operations, name); } private static final Lookup privateLookup = new Lookup(MethodHandles.lookup()); @@ -571,87 +579,89 @@ private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class)); private GuardedInvocationComponent getPropertyGetter(final CallSiteDescriptor callSiteDescriptor, - final LinkerServices linkerServices, final List ops) throws Exception { - switch(callSiteDescriptor.getNameTokenCount()) { - case 2: { - // Since we can't know what kind of a getter we'll get back on different invocations, we'll just - // conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking - // runtime might not allow coercing at that call site. - final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class); - // Must have exactly two arguments: receiver and name - assertParameterCount(callSiteDescriptor, 2); + final LinkerServices linkerServices, final List ops, final Object name) throws Exception { + if (name == null) { + return getUnnamedPropertyGetter(callSiteDescriptor, linkerServices, ops); + } + + return getNamedPropertyGetter(callSiteDescriptor, linkerServices, ops, name); + } - // What's below is basically: - // foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle) - // only with a bunch of method signature adjustments. Basically, retrieve method getter - // AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null, - // or delegate to next component's invocation. + private GuardedInvocationComponent getUnnamedPropertyGetter(final CallSiteDescriptor callSiteDescriptor, + final LinkerServices linkerServices, final List ops) throws Exception { + // Since we can't know what kind of a getter we'll get back on different invocations, we'll just + // conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking + // runtime might not allow coercing at that call site. + final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class); + // Must have exactly two arguments: receiver and name + assertParameterCount(callSiteDescriptor, 2); - final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType( - AnnotatedDynamicMethod.class)); - final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments( - GET_ANNOTATED_METHOD, 1, callSiteDescriptor, linkerServices); - final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0, - callSiteBoundMethodGetter); - // Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0) - final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker, - MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0))); - // Since it's in the target of a fold, drop the unnecessary second argument - // Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1) - final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2, - type.parameterType(1)); - final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor, - linkerServices, ops); + // What's below is basically: + // foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle) + // only with a bunch of method signature adjustments. Basically, retrieve method getter + // AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null, + // or delegate to next component's invocation. + + final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType( + AnnotatedDynamicMethod.class)); + final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments( + GET_ANNOTATED_METHOD, 1, callSiteDescriptor, linkerServices); + final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0, + callSiteBoundMethodGetter); + // Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0) + final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker, + MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0))); + // Since it's in the target of a fold, drop the unnecessary second argument + // Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1) + final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2, + type.parameterType(1)); + final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor, + linkerServices, ops, null); - final MethodHandle fallbackFolded; - if(nextComponent == null) { - // Object(AnnotatedDynamicMethod)->Object(AnnotatedDynamicMethod, T0, T1); returns constant null - fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1, - type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class)); - } else { - // Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to - // drop the extra argument resulting from fold and to change its return type to Object. - final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation(); - final MethodType nextType = nextInvocation.type(); - fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType( - nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class); - } + final MethodHandle fallbackFolded; + if(nextComponent == null) { + // Object(AnnotatedDynamicMethod)->Object(AnnotatedDynamicMethod, T0, T1); returns constant null + fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1, + type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class)); + } else { + // Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to + // drop the extra argument resulting from fold and to change its return type to Object. + final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation(); + final MethodType nextType = nextInvocation.type(); + fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType( + nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class); + } - // fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1)) - final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest( - IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter); - if(nextComponent == null) { - return getClassGuardedInvocationComponent(compositeGetter, type); - } - return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); - } - case 3: { - // Must have exactly one argument: receiver - assertParameterCount(callSiteDescriptor, 1); - // Fixed name - final AnnotatedDynamicMethod annGetter = propertyGetters.get(callSiteDescriptor.getNameToken( - CallSiteDescriptor.NAME_OPERAND)); - if(annGetter == null) { - // We have no such property, always delegate to the next component operation - return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops); - } - final MethodHandle getter = annGetter.getInvocation(callSiteDescriptor, linkerServices); - // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being - // overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the - // method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If - // we're linking against a field getter, don't make the assumption. - // NOTE: No delegation to the next component operation if we have a property with this name, even if its - // value is null. - final ValidationType validationType = annGetter.validationType; - // TODO: we aren't using the type that declares the most generic getter here! - return new GuardedInvocationComponent(getter, getGuard(validationType, - callSiteDescriptor.getMethodType()), clazz, validationType); - } - default: { - // Can't do anything with more than 3 name components - return null; - } + // fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1)) + final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest( + IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter); + if(nextComponent == null) { + return getClassGuardedInvocationComponent(compositeGetter, type); } + return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); + } + + private GuardedInvocationComponent getNamedPropertyGetter(final CallSiteDescriptor callSiteDescriptor, + final LinkerServices linkerServices, final List ops, final Object name) throws Exception { + // Must have exactly one argument: receiver + assertParameterCount(callSiteDescriptor, 1); + // Fixed name + final AnnotatedDynamicMethod annGetter = propertyGetters.get(name.toString()); + if(annGetter == null) { + // We have no such property, always delegate to the next component operation + return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops, name); + } + final MethodHandle getter = annGetter.getInvocation(callSiteDescriptor, linkerServices); + // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being + // overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the + // method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If + // we're linking against a field getter, don't make the assumption. + // NOTE: No delegation to the next component operation if we have a property with this name, even if its + // value is null. + final ValidationType validationType = annGetter.validationType; + // TODO: we aren't using the type that declares the most generic getter here! + return new GuardedInvocationComponent(getter, getGuard(validationType, + callSiteDescriptor.getMethodType()), clazz, validationType); } private MethodHandle getGuard(final ValidationType validationType, final MethodType methodType) { @@ -679,64 +689,67 @@ private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class); private GuardedInvocationComponent getMethodGetter(final CallSiteDescriptor callSiteDescriptor, - final LinkerServices linkerServices, final List ops) throws Exception { + final LinkerServices linkerServices, final List ops, final Object name) throws Exception { // The created method handle will always return a DynamicMethod (or null), but since we don't want that type to // be visible outside of this linker, declare it to return Object. final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class); - switch(callSiteDescriptor.getNameTokenCount()) { - case 2: { - // Must have exactly two arguments: receiver and name - assertParameterCount(callSiteDescriptor, 2); - final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor, - linkerServices, ops); - if(nextComponent == null || !InternalTypeUtilities.areAssignable(DynamicMethod.class, - nextComponent.getGuardedInvocation().getInvocation().type().returnType())) { - // No next component operation, or it can never produce a dynamic method; just return a component - // for this operation. - return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type); - } + if (name == null) { + return getUnnamedMethodGetter(callSiteDescriptor, linkerServices, ops, type); + } + + return getNamedMethodGetter(callSiteDescriptor, linkerServices, ops, name, type); + } - // What's below is basically: - // foldArguments(guardWithTest(isNotNull, identity, nextComponent.invocation), getter) only with a - // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null - // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation. + private GuardedInvocationComponent getUnnamedMethodGetter(final CallSiteDescriptor callSiteDescriptor, + final LinkerServices linkerServices, final List ops, final MethodType type) throws Exception { + // Must have exactly two arguments: receiver and name + assertParameterCount(callSiteDescriptor, 2); + final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor, + linkerServices, ops, null); + if(nextComponent == null || !InternalTypeUtilities.areAssignable(DynamicMethod.class, + nextComponent.getGuardedInvocation().getInvocation().type().returnType())) { + // No next component operation, or it can never produce a dynamic method; just return a component + // for this operation. + return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type); + } + + // What's below is basically: + // foldArguments(guardWithTest(isNotNull, identity, nextComponent.invocation), getter) only with a + // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null + // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation. - final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type); - // Since it is part of the foldArgument() target, it will have extra args that we need to drop. - final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments( - OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class)); - final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation(); - // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the - // return type. - assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type); - // Since it is part of the foldArgument() target, we have to drop an extra arg it receives. - final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0, - Object.class); - // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get) - final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest( - IS_DYNAMIC_METHOD, returnMethodHandle, nextCombinedInvocation), typedGetter); + final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type); + // Since it is part of the foldArgument() target, it will have extra args that we need to drop. + final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments( + OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class)); + final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation(); + // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the + // return type. + assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type); + // Since it is part of the foldArgument() target, we have to drop an extra arg it receives. + final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0, + Object.class); + // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get) + final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest( + IS_DYNAMIC_METHOD, returnMethodHandle, nextCombinedInvocation), typedGetter); - return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); - } - case 3: { - // Must have exactly one argument: receiver - assertParameterCount(callSiteDescriptor, 1); - final DynamicMethod method = getDynamicMethod(callSiteDescriptor.getNameToken( - CallSiteDescriptor.NAME_OPERAND)); - if(method == null) { - // We have no such method, always delegate to the next component - return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops); - } - // No delegation to the next component of the composite operation; if we have a method with that name, - // we'll always return it at this point. - return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments( - MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type); - } - default: { - // Can't do anything with more than 3 name components - return null; - } + return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); + } + + private GuardedInvocationComponent getNamedMethodGetter(final CallSiteDescriptor callSiteDescriptor, + final LinkerServices linkerServices, final List ops, final Object name, final MethodType type) + throws Exception { + // Must have exactly one argument: receiver + assertParameterCount(callSiteDescriptor, 1); + final DynamicMethod method = getDynamicMethod(name.toString()); + if(method == null) { + // We have no such method, always delegate to the next component + return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops, name); } + // No delegation to the next component of the composite operation; if we have a method with that name, + // we'll always return it at this point. + return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments( + MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type); } static class MethodPair { @@ -765,7 +778,7 @@ private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) { if(descriptor.getMethodType().parameterCount() != paramCount) { - throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters."); + throw new BootstrapMethodError(descriptor.getOperation() + " must have exactly " + paramCount + " parameters."); } } @@ -804,7 +817,7 @@ @SuppressWarnings("unused") // This method is marked to return Object instead of DynamicMethod as it's used as a linking component and we don't // want to make the DynamicMethod type observable externally (e.g. as the return type of a MethodHandle returned for - // "dyn:getMethod" linking). + // GET_METHOD linking). private Object getDynamicMethod(final Object name) { return getDynamicMethod(String.valueOf(name), methods); } diff -r 86b4260bb17a -r 23abd10384a5 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeanLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeanLinker.java Wed Oct 21 10:42:20 2015 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeanLinker.java Wed Oct 21 19:33:58 2015 +0200 @@ -91,6 +91,8 @@ import java.util.List; import java.util.Map; import jdk.internal.dynalink.CallSiteDescriptor; +import jdk.internal.dynalink.Operation; +import jdk.internal.dynalink.StandardOperation; import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkerServices; @@ -109,7 +111,7 @@ if(clazz.isArray()) { // Some languages won't have a notion of manipulating collections. Exposing "length" on arrays as an // explicit property is beneficial for them. - // REVISIT: is it maybe a code smell that "dyn:getLength" is not needed? + // REVISIT: is it maybe a code smell that StandardOperation.GET_LENGTH is not needed? setPropertyGetter("length", GET_ARRAY_LENGTH, ValidationType.IS_ARRAY); } else if(List.class.isAssignableFrom(clazz)) { setPropertyGetter("length", GET_COLLECTION_LENGTH, ValidationType.INSTANCE_OF); @@ -128,27 +130,23 @@ @Override protected GuardedInvocationComponent getGuardedInvocationComponent(final CallSiteDescriptor callSiteDescriptor, - final LinkerServices linkerServices, final List operations) throws Exception { + final LinkerServices linkerServices, final List operations, final Object name) throws Exception { final GuardedInvocationComponent superGic = super.getGuardedInvocationComponent(callSiteDescriptor, - linkerServices, operations); + linkerServices, operations, name); if(superGic != null) { return superGic; } if(operations.isEmpty()) { return null; } - final String op = operations.get(0); - // dyn:getElem(this, id) - // id is typically either an int (for arrays and lists) or an object (for maps). linkerServices can provide - // conversion from call site argument type though. - if("getElem".equals(op)) { - return getElementGetter(callSiteDescriptor, linkerServices, pop(operations)); + final Operation op = operations.get(0); + if(op == StandardOperation.GET_ELEMENT) { + return getElementGetter(callSiteDescriptor, linkerServices, pop(operations), name); } - if("setElem".equals(op)) { - return getElementSetter(callSiteDescriptor, linkerServices, pop(operations)); + if(op == StandardOperation.SET_ELEMENT) { + return getElementSetter(callSiteDescriptor, linkerServices, pop(operations), name); } - // dyn:getLength(this) (works on Java arrays, collections, and maps) - if("getLength".equals(op)) { + if(op == StandardOperation.GET_LENGTH) { return getLengthGetter(callSiteDescriptor); } return null; @@ -168,11 +166,11 @@ }; private GuardedInvocationComponent getElementGetter(final CallSiteDescriptor callSiteDescriptor, - final LinkerServices linkerServices, final List operations) throws Exception { + final LinkerServices linkerServices, final List operations, final Object name) throws Exception { final MethodType callSiteType = callSiteDescriptor.getMethodType(); final Class declaredType = callSiteType.parameterType(0); final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor, - linkerServices, operations); + linkerServices, operations, name); // If declared type of receiver at the call site is already an array, a list or map, bind without guard. Thing // is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance they're @@ -206,22 +204,20 @@ return nextComponent; } - // We can have "dyn:getElem:foo", especially in composites, i.e. "dyn:getElem|getProp|getMethod:foo" - final String fixedKey = getFixedKey(callSiteDescriptor); // Convert the key to a number if we're working with a list or array - final Object typedFixedKey; - if(collectionType != CollectionType.MAP && fixedKey != null) { - typedFixedKey = convertKeyToInteger(fixedKey, linkerServices); - if(typedFixedKey == null) { + final Object typedName; + if(collectionType != CollectionType.MAP && name != null) { + typedName = convertKeyToInteger(name, linkerServices); + if(typedName == null) { // key is not numeric, it can never succeed return nextComponent; } } else { - typedFixedKey = fixedKey; + typedName = name; } final GuardedInvocation gi = gic.getGuardedInvocation(); - final Binder binder = new Binder(linkerServices, callSiteType, typedFixedKey); + final Binder binder = new Binder(linkerServices, callSiteType, typedName); final MethodHandle invocation = gi.getInvocation(); if(nextComponent == null) { @@ -263,40 +259,50 @@ validatorClass, validationType); } - private static String getFixedKey(final CallSiteDescriptor callSiteDescriptor) { - return callSiteDescriptor.getNameTokenCount() == 2 ? null : callSiteDescriptor.getNameToken( - CallSiteDescriptor.NAME_OPERAND); - } + private static Integer convertKeyToInteger(final Object fixedKey, final LinkerServices linkerServices) throws Exception { + if (fixedKey instanceof Integer) { + return (Integer)fixedKey; + } - private static Object convertKeyToInteger(final String fixedKey, final LinkerServices linkerServices) throws Exception { - try { - if(linkerServices.canConvert(String.class, Number.class)) { + final Number n; + if (fixedKey instanceof Number) { + n = (Number)fixedKey; + } else { + final Class keyClass = fixedKey.getClass(); + if(linkerServices.canConvert(keyClass, Number.class)) { + final Object val; try { - final Object val = linkerServices.getTypeConverter(String.class, Number.class).invoke(fixedKey); - if(!(val instanceof Number)) { - return null; // not a number - } - final Number n = (Number)val; - if(n instanceof Integer) { - return n; - } - final int intIndex = n.intValue(); - final double doubleValue = n.doubleValue(); - if(intIndex != doubleValue && !Double.isInfinite(doubleValue)) { // let infinites trigger IOOBE - return null; // not an exact integer - } - return intIndex; + val = linkerServices.getTypeConverter(keyClass, Number.class).invoke(fixedKey); } catch(Exception|Error e) { throw e; } catch(final Throwable t) { throw new RuntimeException(t); } + if(!(val instanceof Number)) { + return null; // not a number + } + n = (Number)val; + } else if (fixedKey instanceof String){ + try { + return Integer.valueOf((String)fixedKey); + } catch(final NumberFormatException e) { + // key is not a number + return null; + } + } else { + return null; } - return Integer.valueOf(fixedKey); - } catch(final NumberFormatException e) { - // key is not a number - return null; + } + + if(n instanceof Integer) { + return (Integer)n; } + final int intIndex = n.intValue(); + final double doubleValue = n.doubleValue(); + if(intIndex != doubleValue && !Double.isInfinite(doubleValue)) { // let infinites trigger IOOBE + return null; // not an exact integer + } + return intIndex; } private static MethodHandle convertArgToInt(final MethodHandle mh, final LinkerServices ls, final CallSiteDescriptor desc) { @@ -389,7 +395,7 @@ MethodType.methodType(Object.class, Object.class, Object.class)); private GuardedInvocationComponent getElementSetter(final CallSiteDescriptor callSiteDescriptor, - final LinkerServices linkerServices, final List operations) throws Exception { + final LinkerServices linkerServices, final List operations, final Object name) throws Exception { final MethodType callSiteType = callSiteDescriptor.getMethodType(); final Class declaredType = callSiteType.parameterType(0); @@ -431,27 +437,25 @@ // as maps will always succeed in setting the element and will never need to fall back to the next component // operation. final GuardedInvocationComponent nextComponent = collectionType == CollectionType.MAP ? null : getGuardedInvocationComponent( - callSiteDescriptor, linkerServices, operations); + callSiteDescriptor, linkerServices, operations, name); if(gic == null) { return nextComponent; } - // We can have "dyn:setElem:foo", especially in composites, i.e. "dyn:setElem|setProp:foo" - final String fixedKey = getFixedKey(callSiteDescriptor); // Convert the key to a number if we're working with a list or array - final Object typedFixedKey; - if(collectionType != CollectionType.MAP && fixedKey != null) { - typedFixedKey = convertKeyToInteger(fixedKey, linkerServices); - if(typedFixedKey == null) { + final Object typedName; + if(collectionType != CollectionType.MAP && name != null) { + typedName = convertKeyToInteger(name, linkerServices); + if(typedName == null) { // key is not numeric, it can never succeed return nextComponent; } } else { - typedFixedKey = fixedKey; + typedName = name; } final GuardedInvocation gi = gic.getGuardedInvocation(); - final Binder binder = new Binder(linkerServices, callSiteType, typedFixedKey); + final Binder binder = new Binder(linkerServices, callSiteType, typedName); final MethodHandle invocation = gi.getInvocation(); if(nextComponent == null) { @@ -510,7 +514,7 @@ private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) { if(descriptor.getMethodType().parameterCount() != paramCount) { - throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters."); + throw new BootstrapMethodError(descriptor.getOperation() + " must have exactly " + paramCount + " parameters."); } } } diff -r 86b4260bb17a -r 23abd10384a5 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeansLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeansLinker.java Wed Oct 21 10:42:20 2015 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeansLinker.java Wed Oct 21 19:33:58 2015 +0200 @@ -86,8 +86,8 @@ import java.lang.invoke.MethodHandles.Lookup; import java.util.Collection; import java.util.Collections; -import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.DynamicLinkerFactory; +import jdk.internal.dynalink.StandardOperation; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.GuardingDynamicLinker; import jdk.internal.dynalink.linker.LinkRequest; @@ -101,24 +101,26 @@ * linker will: *

*

What can you already do with the above setup? {@code DynamicLinkerFactory} * by default creates a {@code DynamicLinker} that can link Java objects with the @@ -217,158 +228,21 @@ * them with representations of dynamic operations in the interpreted program * (e.g. a typical representation would be some node objects in a syntax tree). *

Available operations

- * The table below contains all operations defined by Dynalink. All of them have - * the prefix {@code "dyn:"} and this prefix is reserved for Dynalink use, with - * potential of future extensions. Elements of the name are separated with the - * COLON character. {@code $id} is used as a placeholder for an identifier for - * those operations that contain an identifier as part of their name. - * Identifiers in operation names need to be - * {@link jdk.internal.dynalink.support.NameCodec#encode(String) encoded}. Signatures are - * expressed in the usual JVM - * {@code (parameter-type1, parameter-type2, ...)return-type} format. The type - * "any" means that any type, either primitive or reference can be used (with - * obvious JVM limitation that {@code void} is disallowed as a parameter type - * but allowed as a return type. - *

  - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
NameSignatureSemantics
{@code dyn:getProp:$id}(any)anyRetrieve the value of a named property on the object, with the - * receiver being passed as the argument, and the property identifier - * encoded in the operation name as {@code $id}.
{@code dyn:getProp}(any, any)anyRetrieve the value of a named property on the object, with the - * receiver being passed as the first, and the property identifier - * being passed as the second argument.
{@code dyn:setProp:$id}(any, any)voidSet the value of a named property on the object, with the - * receiver being passed as the first, and the value to set being - * passed as the second argument, with the property identifier - * encoded in the operation name as {@code $id}.
{@code dyn:setProp}(any, any, any)voidSet the value of a named property on the object, with the - * receiver being passed as the first, the property identifier as the - * second, and the value to set as the third argument.
{@code dyn:getElem:$id}(any)anyRetrieve an element of a collection object (array, list, map, - * etc.) with a fixed key encoded in the operation name as {@code $id}. - * In this form, the key is necessarily a string as it is part of the - * operation name, but runtimes are allowed to parse it as a number - * literal when linking to a collection using numeric indices.
{@code dyn:getElem}(any, any)anyRetrieve an element of a collection object (array, list, map, - * etc.) with the receiver being passed as the first and the index - * passed as the second argument.
{@code dyn:setElem:$id}(any, any)voidSet an element of a collection object (array, list, map, - * etc.) with a fixed key encoded in the operation name as {@code $id}. - * The receiver is passed as the first, and the new element value as - * the second argument. In this form, the key is necessarily a string - * as it is part of the operation name, but runtimes are allowed to - * parse it as a number literal when linking to a collection using - * numeric indices.
{@code dyn:setElem}(any, any, any)voidSet an element of a collection object (array, list, map, - * etc.) with the receiver being passed as the first, the index - * passed as the second, and the new element value as the third argument.
{@code dyn:getMethod:$id}(any)anyRetrieve a named method on the object, identified by {@code $id}. - * It is expected that at least the {@code "dyn:call"} operation is - * applicable to the returned value.
{@code dyn:getMethod}(any, any)anyRetrieve a named method on the object, with the receiver passed as - * the first and the name of the method passed as the second argument. - * It is expected that {@code "dyn:call"} operation is applicable to - * the returned value.
{@code dyn:call}(any[, any, any,...])anyCall a callable (method, function, etc.). The first argument - * is the callable itself, and the rest are arguments passed to the - * call. If the callable is an instance method, the {@code this} - * argument should be the second argument, immediately following the - * callable.
{@code dyn:callMethod:$id}(any[, any, any,...])anyCall a named instance method on an object. The first argument - * is the object on which the method is invoked, and the rest are - * arguments passed to the call. Note that this method is not strictly - * necessary, as it can be implemented as a composition of - * {@code dyn:getMethod:$id} and {@code dyn:call}. It is a frequent - * enough object-oriented pattern so it is convenient to provide it as - * a separate operation.
{@code dyn:new}(any[, any, any,...])anyCall a constructor. The first argument is the constructor itself, - * and the rest are arguments passed to the constructor call.
+ * Dynalink defines several standard operations in its + * {@link jdk.internal.dynalink.StandardOperation} class. The linker for Java + * objects can link all of these operations, and you are encouraged to at + * minimum support and use these operations in your language too. To associate + * a fixed name with an operation, you can use + * {@link jdk.internal.dynalink.NamedOperation} as in the above example where + * {@code StandardOperation.GET_PROPERTY} was combined with the name + * {@code "color"} in a {@code NamedOperation} to form a property getter for the + * property named "color". *

Composite operations

* Some languages might not have separate namespaces on objects for - * properties, elements, and methods. Dynalink supports specifying composite - * operations for this purpose using the VERTICAL LINE character as the - * separator. Typical syntax would be e.g. - * {@code "dyn:getProp|getElem|getMethod:color"}. Any combination of - * {@code "getProp"}, {@code "getElem"}, and {@code "getMethod"} in any order can be - * specified. The semantics of this is "return the first matching member, trying - * them in the specified order". Similarly, {@code "setProp"} and {@code "setElem"} - * can be combined too in both orders. Only compositions consisting of getter - * operations only and setter operations only are allowed. They can either have - * an identifier encoded in the name or not. - *

- * Even if the language itself doesn't distinguish some of the namespaces, it - * can be helpful to map different syntaxes to different compositions. E.g. - * source expression {@code obj.color} could map to - * {@code "dyn:getProp|getElem|getMethod:color"}, but a different source - * expression that looks like collection element access {@code obj[key]} could - * be expressed instead as {@code "dyn:getElem|getProp|getMethod"}. Finally, if - * the retrieved value is subsequently called, then it makes sense to bring - * {@code getMethod} to the front of the list: the getter part of the source - * expression {@code obj.color()} should be - * {@code "dyn:getMethod|getProp|getElem:color"} and the one for - * {@code obj[key]()} should be {@code "dyn:getMethod|getElem|getProp"}. + * properties, elements, and methods, and a source language construct might + * address two or three of them. Dynalink supports specifying composite + * operations for this purpose using the + * {@link jdk.internal.dynalink.CompositeOperation} class. *

Language-specific linkers

* Languages that define their own object model different than the JVM * class-based model and/or use their own type conversions will need to create diff -r 86b4260bb17a -r 23abd10384a5 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/NameCodec.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/NameCodec.java Wed Oct 21 10:42:20 2015 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/NameCodec.java Wed Oct 21 19:33:58 2015 +0200 @@ -83,27 +83,13 @@ package jdk.internal.dynalink.support; -import jdk.internal.dynalink.CallSiteDescriptor; /** * Implements the name mangling and demangling as specified by John Rose's * "Symbolic Freedom in the VM" article. It is recommended - * that implementers of languages on the JVM uniformly adopt this for symbolic - * interoperability between languages. Normally, you would mangle the names as - * you're generating bytecode, and then demangle them when you're creating - * {@link CallSiteDescriptor} objects. Note that you are expected to mangle - * individual tokens, and not the whole name at the call site, i.e. the colon - * character normally separating the tokens is never mangled. I.e. you wouldn't - * mangle {@code dyn:getProp:color} into {@code dyn\!getProp\!color}, but you - * would mangle {@code dyn:getProp:color$} into {@code dyn:getProp:\=color\%} - * (only mangling the individual token containing the symbol {@code color$}). - * {@link CallSiteDescriptor#tokenizeName(String)} already uses - * {@link #decode(String)} to perform demangling on the passed names. If you use - * that method when creating call site descriptors, (it is recommended that you - * do), then you have demangling handled for you already, and only need to - * ensure that you mangle the names using {@link #encode(String)} when you're - * emitting them in the bytecode. + * target="_blank">"Symbolic Freedom in the VM" article. Normally, you would + * mangle the names in the call sites as you're generating bytecode, and then + * demangle them when you receive them in bootstrap methods. */ public final class NameCodec { private static final char ESCAPE_CHAR = '\\'; @@ -180,7 +166,7 @@ * @return the demangled form of the symbolic name. */ public static String decode(final String name) { - if(name.charAt(0) != ESCAPE_CHAR) { + if(name.isEmpty() || name.charAt(0) != ESCAPE_CHAR) { return name; } final int l = name.length(); diff -r 86b4260bb17a -r 23abd10384a5 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Oct 21 10:42:20 2015 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Oct 21 19:33:58 2015 +0200 @@ -683,9 +683,9 @@ // (in)equality operators need the specialized JSType.toNumberFor[Strict]Equals. E.g. in the code snippet // "i < obj.size" (where i is primitive and obj.size is statically an object), ".size" will thus be allowed // to compile as: - // invokedynamic dyn:getProp|getElem|getMethod:size(Object;)D + // invokedynamic GET_PROPERTY:size(Object;)D // instead of the more costly: - // invokedynamic dyn:getProp|getElem|getMethod:size(Object;)Object + // invokedynamic GET_PROPERTY:size(Object;)Object // invokestatic JSType.toNumber(Object)D // Note also that even if this is allowed, we're only using it on operands that are non-optimistic, as // otherwise the logic for determining effective optimistic-ness would turn an optimistic double return @@ -1494,11 +1494,11 @@ void loadStack() { /* * We want to load 'eval' to check if it is indeed global builtin eval. - * If this eval call is inside a 'with' statement, dyn:getMethod|getProp|getElem + * If this eval call is inside a 'with' statement, GET_METHOD_PROPERTY * would be generated if ident is a "isFunction". But, that would result in a * bound function from WithObject. We don't want that as bound function as that * won't be detected as builtin eval. So, we make ident as "not a function" which - * results in "dyn:getProp|getElem|getMethod" being generated and so WithObject + * results in GET_PROPERTY being generated and so WithObject * would return unbounded eval function. * * Example: @@ -1525,7 +1525,7 @@ method._goto(invoke_direct_eval); method.label(is_not_eval); - // load this time but with dyn:getMethod|getProp|getElem + // load this time but with GET_METHOD_PROPERTY loadExpressionAsObject(ident); // Type.OBJECT as foo() makes no sense if foo == 3 // This is some scope 'eval' or global eval replaced by user // but not the built-in ECMAScript 'eval' function call diff -r 86b4260bb17a -r 23abd10384a5 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java Wed Oct 21 10:42:20 2015 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java Wed Oct 21 19:33:58 2015 +0200 @@ -107,6 +107,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.UnwarrantedOptimismException; import jdk.nashorn.internal.runtime.linker.Bootstrap; +import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.options.Options; @@ -124,6 +125,8 @@ * including bytecode stack contents */ public class MethodEmitter { + private static final String EMPTY_NAME = NameCodec.encode(""); + /** The ASM MethodVisitor we are plugged into */ private final MethodVisitor method; @@ -2148,8 +2151,8 @@ debug("dynamic_new", "argcount=", argCount); final String signature = getDynamicSignature(Type.OBJECT, argCount); method.visitInvokeDynamicInsn( - msg != null && msg.length() < LARGE_STRING_THRESHOLD? "dyn:new:" + NameCodec.encode(msg) : "dyn:new", - signature, LINKERBOOTSTRAP, flags); + msg != null && msg.length() < LARGE_STRING_THRESHOLD? NameCodec.encode(msg) : EMPTY_NAME, + signature, LINKERBOOTSTRAP, flags | NashornCallSiteDescriptor.NEW); pushType(Type.OBJECT); //TODO fix result type return this; } @@ -2182,8 +2185,8 @@ final String signature = getDynamicSignature(returnType, argCount); // +1 because the function itself is the 1st parameter for dynamic calls (what you call - call target) debug(" signature", signature); method.visitInvokeDynamicInsn( - msg != null && msg.length() < LARGE_STRING_THRESHOLD? "dyn:call:" + NameCodec.encode(msg) : "dyn:call", - signature, LINKERBOOTSTRAP, flags); + msg != null && msg.length() < LARGE_STRING_THRESHOLD? NameCodec.encode(msg) : EMPTY_NAME, + signature, LINKERBOOTSTRAP, flags | NashornCallSiteDescriptor.CALL); pushType(returnType); return this; @@ -2220,8 +2223,8 @@ } popType(Type.SCOPE); - method.visitInvokeDynamicInsn(dynGetOperation(isMethod, isIndex) + ':' + NameCodec.encode(name), - Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags); + method.visitInvokeDynamicInsn(NameCodec.encode(name), + Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags | dynGetOperation(isMethod, isIndex)); pushType(type); convert(valueType); //most probably a nop @@ -2253,8 +2256,8 @@ popType(type); popType(Type.SCOPE); - method.visitInvokeDynamicInsn(dynSetOperation(isIndex) + ':' + NameCodec.encode(name), - methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags); + method.visitInvokeDynamicInsn(NameCodec.encode(name), + methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags | dynSetOperation(isIndex)); } /** @@ -2287,7 +2290,7 @@ final String signature = Type.getMethodDescriptor(resultType, Type.OBJECT /*e.g STRING->OBJECT*/, index); - method.visitInvokeDynamicInsn(dynGetOperation(isMethod, true), signature, LINKERBOOTSTRAP, flags); + method.visitInvokeDynamicInsn(EMPTY_NAME, signature, LINKERBOOTSTRAP, flags | dynGetOperation(isMethod, true)); pushType(resultType); if (result.isBoolean()) { @@ -2331,7 +2334,9 @@ final Type receiver = popType(Type.OBJECT); assert receiver.isObject(); - method.visitInvokeDynamicInsn("dyn:setElem|setProp", methodDescriptor(void.class, receiver.getTypeClass(), index.getTypeClass(), value.getTypeClass()), LINKERBOOTSTRAP, flags); + method.visitInvokeDynamicInsn(EMPTY_NAME, + methodDescriptor(void.class, receiver.getTypeClass(), index.getTypeClass(), value.getTypeClass()), + LINKERBOOTSTRAP, flags | NashornCallSiteDescriptor.SET_ELEMENT); } /** @@ -2501,16 +2506,16 @@ } } - private static String dynGetOperation(final boolean isMethod, final boolean isIndex) { + private static int dynGetOperation(final boolean isMethod, final boolean isIndex) { if (isMethod) { - return isIndex ? "dyn:getMethod|getElem|getProp" : "dyn:getMethod|getProp|getElem"; + return isIndex ? NashornCallSiteDescriptor.GET_METHOD_ELEMENT : NashornCallSiteDescriptor.GET_METHOD_PROPERTY; } else { - return isIndex ? "dyn:getElem|getProp|getMethod" : "dyn:getProp|getElem|getMethod"; + return isIndex ? NashornCallSiteDescriptor.GET_ELEMENT : NashornCallSiteDescriptor.GET_PROPERTY; } } - private static String dynSetOperation(final boolean isIndex) { - return isIndex ? "dyn:setElem|setProp" : "dyn:setProp|setElem"; + private static int dynSetOperation(final boolean isIndex) { + return isIndex ? NashornCallSiteDescriptor.SET_ELEMENT : NashornCallSiteDescriptor.SET_PROPERTY; } private Type emitLocalVariableConversion(final LocalVariableConversion conversion, final boolean onlySymbolLiveValue) { diff -r 86b4260bb17a -r 23abd10384a5 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionCall.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionCall.java Wed Oct 21 10:42:20 2015 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionCall.java Wed Oct 21 19:33:58 2015 +0200 @@ -25,14 +25,16 @@ package jdk.nashorn.internal.ir; +import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; + /** * Interface used by AccessNodes, IndexNodes and IdentNodes to signal that when evaluated, their value will be treated * as a function and immediately invoked, e.g. {@code foo()}, {@code foo.bar()} or {@code foo[bar]()}. Used to customize * the priority of composite dynamic operations when emitting {@code INVOKEDYNAMIC} instructions that implement them, * namely prioritize {@code getMethod} over {@code getElem} or {@code getProp}. An access or ident node with isFunction - * set to true will be emitted as {@code dyn:getMethod|getProp|getElem} while one with it set to false will be emitted - * as {@code dyn:getProp|getElem|getMethod}. Similarly, an index node with isFunction set to true will be emitted as - * {@code dyn:getMethod|getElem|getProp} while the one set to false will be emitted as {@code dyn:getElem|getProp|getMethod}. + * set to true will be emitted as {@link NashornCallSiteDescriptor#GET_METHOD_PROPERTY} while one with it set to false will be emitted + * as {@link NashornCallSiteDescriptor#GET_PROPERTY}. Similarly, an index node with isFunction set to true will be emitted as + * {@link NashornCallSiteDescriptor#GET_METHOD_ELEMENT} while the one set to false will be emitted as {@link NashornCallSiteDescriptor#GET_ELEMENT}. */ public interface FunctionCall { /** diff -r 86b4260bb17a -r 23abd10384a5 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/NashornTextifier.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/NashornTextifier.java Wed Oct 21 10:42:20 2015 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/NashornTextifier.java Wed Oct 21 19:33:58 2015 +0200 @@ -39,6 +39,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import jdk.internal.dynalink.support.NameCodec; import jdk.internal.org.objectweb.asm.Attribute; import jdk.internal.org.objectweb.asm.Handle; import jdk.internal.org.objectweb.asm.Label; @@ -48,6 +49,7 @@ import jdk.internal.org.objectweb.asm.util.Printer; import jdk.internal.org.objectweb.asm.util.TraceSignatureVisitor; import jdk.nashorn.internal.runtime.ScriptEnvironment; +import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; /** @@ -55,6 +57,7 @@ * Also supports dot formats if --print-code has arguments */ public final class NashornTextifier extends Printer { + private static final String BOOTSTRAP_CLASS_NAME = Bootstrap.class.getName().replace('.', '/'); private String currentClassName; private Iterator