8139931: Introduce Operation objects in Dynalink instead of string encoding

Reviewed-by: hannesw, sundar
This commit is contained in:
Attila Szegedi 2015-10-21 19:33:58 +02:00
parent df445c6578
commit fe13034c1d
52 changed files with 1779 additions and 1193 deletions

View File

@ -86,19 +86,13 @@ package jdk.internal.dynalink;
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 @@ import jdk.internal.dynalink.support.NameCodec;
* 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;
/**
@ -126,99 +119,24 @@ public class CallSiteDescriptor {
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 <i>i<sup>th</sup></i> 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 <i>i<sup>th</sup></i> 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 @@ public class CallSiteDescriptor {
/**
* 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 @@ public class CallSiteDescriptor {
* @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<String> 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 @@ public class CallSiteDescriptor {
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 @@ public class CallSiteDescriptor {
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 @@ public class CallSiteDescriptor {
* @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 class CallSiteDescriptor {
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();
}
}

View File

@ -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 <i>get the property named "color" on the object, but if the
* property does not exist, then get the collection element named "color"
* instead</i>.
* <p>
* 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.
* <p>
* 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}.
* <p>
* 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:
* <pre>
* Operation getElementOrPropertyEmpty = new NamedOperation(
* new CompositeOperation(
* StandardOperation.GET_ELEMENT,
* StandardOperation.GET_PROPERTY),
* "empty");
* </pre>
* <p>
* 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 };
}
}

View File

@ -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;
}
}

View File

@ -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 {
}

View File

@ -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
* <tt>(receiver,&nbsp;propertyName)&rarr;value</tt> or
* <tt>(receiver)&rarr;value</tt> 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
* <tt>(receiver,&nbsp;propertyName,&nbsp;value)&rarr;void</tt> or
* <tt>(receiver,&nbsp;value)&rarr;void</tt> 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
* <tt>(receiver,&nbsp;index)&rarr;value</tt> or
* <tt>(receiver)&rarr;value</tt> 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
* <tt>(receiver,&nbsp;index,&nbsp;value)&rarr;void</tt> or
* <tt>(receiver,&nbsp;value)&rarr;void</tt> 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 <tt>(receiver)&rarr;value</tt>,
* 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
* <tt>(receiver,&nbsp;methodName)&rarr;value</tt>, or
* <tt>(receiver)&rarr;value</tt> 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
* <tt>(receiver,&nbsp;methodName,&nbsp;arguments...)&rarr;value</tt> or
* <tt>(receiver,&nbsp;arguments...)&rarr;value</tt> 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 <tt>(receiver,&nbsp;arguments...)&rarr;value</tt>, 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 <tt>CALL</tt> 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 <tt>(receiver,&nbsp;arguments...)&rarr;value</tt>, with all
* parameters and return type being of any type (either primitive or
* reference). The <tt>NEW</tt> 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
}

View File

@ -92,12 +92,17 @@ import java.lang.reflect.Field;
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 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
@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<String> operations = callSiteDescriptor.tokenizeOperators();
List<Operation> 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 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
return null;
}
protected GuardedInvocationComponent getGuardedInvocationComponent(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<String> operations) throws Exception {
protected GuardedInvocationComponent getGuardedInvocationComponent(
final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices,
final List<Operation> 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 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
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<String, DynamicMethod> methodMap){
final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap);
@ -480,84 +486,86 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
MethodHandles.constant(Object.class, null), 0, MethodHandle.class);
private GuardedInvocationComponent getPropertySetter(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<String> operations) throws Exception {
switch(callSiteDescriptor.getNameTokenCount()) {
case 2: {
// 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.
// 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));
// 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);
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;
}
final LinkerServices linkerServices, final List<Operation> operations, final Object name) throws Exception {
if (name == null) {
return getUnnamedPropertySetter(callSiteDescriptor, linkerServices, operations);
}
return getNamedPropertySetter(callSiteDescriptor, linkerServices, operations, name);
}
private GuardedInvocationComponent getUnnamedPropertySetter(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<Operation> 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.
// 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));
// 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);
}
// 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<Operation> 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 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
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<String> 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);
// 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);
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;
}
final LinkerServices linkerServices, final List<Operation> ops, final Object name) throws Exception {
if (name == null) {
return getUnnamedPropertyGetter(callSiteDescriptor, linkerServices, ops);
}
return getNamedPropertyGetter(callSiteDescriptor, linkerServices, ops, name);
}
private GuardedInvocationComponent getUnnamedPropertyGetter(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<Operation> 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);
// 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);
}
// 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<Operation> 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 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class);
private GuardedInvocationComponent getMethodGetter(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<String> ops) throws Exception {
final LinkerServices linkerServices, final List<Operation> 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);
}
// 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);
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;
}
if (name == null) {
return getUnnamedMethodGetter(callSiteDescriptor, linkerServices, ops, type);
}
return getNamedMethodGetter(callSiteDescriptor, linkerServices, ops, name, type);
}
private GuardedInvocationComponent getUnnamedMethodGetter(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<Operation> 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);
return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
}
private GuardedInvocationComponent getNamedMethodGetter(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<Operation> 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 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
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 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
@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);
}

View File

@ -91,6 +91,8 @@ import java.util.Collection;
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 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
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 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
@Override
protected GuardedInvocationComponent getGuardedInvocationComponent(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<String> operations) throws Exception {
final LinkerServices linkerServices, final List<Operation> 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 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
};
private GuardedInvocationComponent getElementGetter(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<String> operations) throws Exception {
final LinkerServices linkerServices, final List<Operation> 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 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
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 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
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 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
MethodType.methodType(Object.class, Object.class, Object.class));
private GuardedInvocationComponent getElementSetter(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<String> operations) throws Exception {
final LinkerServices linkerServices, final List<Operation> operations, final Object name) throws Exception {
final MethodType callSiteType = callSiteDescriptor.getMethodType();
final Class<?> declaredType = callSiteType.parameterType(0);
@ -431,27 +437,25 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
// 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 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
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.");
}
}
}

View File

@ -86,8 +86,8 @@ package jdk.internal.dynalink.beans;
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 @@ import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
* linker will:
* <ul>
* <li>expose all public methods of form {@code setXxx()}, {@code getXxx()},
* and {@code isXxx()} as property setters and getters for {@code dyn:setProp}
* and {@code dyn:getProp} operations;</li>
* <li>expose all public methods for invocation through {@code dyn:callMethod}
* operation;</li>
* <li>expose all public methods for retrieval for {@code dyn:getMethod}
* operation; the methods thus retrieved can then be invoked using
* {@code dyn:call};</li>
* and {@code isXxx()} as property setters and getters for
* {@link StandardOperation#SET_PROPERTY} and {@link StandardOperation#GET_PROPERTY}
* operations;</li>
* <li>expose all public methods for invocation through
* {@link StandardOperation#CALL_METHOD} operation;</li>
* <li>expose all public methods for retrieval for
* {@link StandardOperation#GET_METHOD} operation; the methods thus retrieved
* can then be invoked using {@link StandardOperation#CALL}.</li>
* <li>expose all public fields as properties, unless there are getters or
* setters for the properties of the same name;</li>
* <li>expose {@code dyn:getLength}, {@code dyn:getElem} and
* {@code dyn:setElem} on native Java arrays, as well as {@link java.util.List}
* and {@link java.util.Map} objects; ({@code dyn:getLength} works on any
* {@link java.util.Collection});</li>
* <li>expose {@link StandardOperation#GET_LENGTH},
* {@link StandardOperation#GET_ELEMENT} and {@link StandardOperation#SET_ELEMENT}
* on native Java arrays, as well as {@link java.util.List} and
* {@link java.util.Map} objects; ({@link StandardOperation#GET_LENGTH} works on
* any {@link java.util.Collection});</li>
* <li>expose a virtual property named {@code length} on Java arrays;</li>
* <li>expose {@code dyn:new} on instances of {@link StaticClass} as calls to
* constructors, including those static class objects that represent Java arrays
* (their constructors take a single {@code int} parameter representing the
* length of the array to create);</li>
* <li>expose {@link StandardOperation#NEW} on instances of {@link StaticClass}
* as calls to constructors, including those static class objects that represent
* Java arrays (their constructors take a single {@code int} parameter
* representing the length of the array to create);</li>
* <li>expose static methods, fields, and properties of classes in a similar
* manner to how instance method, fields, and properties are exposed, on
* {@link StaticClass} objects.</li>
@ -129,15 +131,15 @@ import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
* for property setters, methods, and constructors. Additionally, manual
* overloaded method selection is supported by having a call site specify a name
* for a method that contains an explicit signature, i.e.
* {@code dyn:getMethod:parseInt(String,int)}. You can use non-qualified class
* names in such signatures regardless of those classes' packages, they will
* match any class with the same non-qualified name. You only have to use a
* fully qualified class name in case non-qualified class names would cause
* selection ambiguity (that is extremely rare). Overloaded resolution for
* constructors is not automatic as there is no logical place to attach that
* functionality to but if a language wishes to provide this functionality, it
* can use {@link #getConstructorMethod(Class, String)} as a useful building
* block for it.</p>
* {@code NamedMethod(GET_METHOD, "parseInt(String,int)")}. You can use
* non-qualified class names in such signatures regardless of those classes'
* packages, they will match any class with the same non-qualified name. You
* only have to use a fully qualified class name in case non-qualified class
* names would cause selection ambiguity (that is extremely rare). Overloaded
* resolution for constructors is not automatic as there is no logical place to
* attach that functionality to but if a language wishes to provide this
* functionality, it can use {@link #getConstructorMethod(Class, String)} as a
* useful building block for it.</p>
* <p><strong>Variable argument invocation</strong> is handled for both methods
* and constructors.</p>
* <p><strong>Caller sensitive methods</strong> can be linked as long as they
@ -182,7 +184,7 @@ public class BeansLinker implements GuardingDynamicLinker {
/**
* Returns true if the object is a Java dynamic method (e.g., one
* obtained through a {@code dyn:getMethod} call on a Java object or
* obtained through a {@code GET_METHOD} operation on a Java object or
* {@link StaticClass} or through
* {@link #getConstructorMethod(Class, String)}.
*
@ -295,13 +297,6 @@ public class BeansLinker implements GuardingDynamicLinker {
@Override
public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices)
throws Exception {
final CallSiteDescriptor callSiteDescriptor = request.getCallSiteDescriptor();
final int l = callSiteDescriptor.getNameTokenCount();
// All names conforming to the dynalang MOP should have at least two tokens, the first one being "dyn"
if(l < 2 || "dyn" != callSiteDescriptor.getNameToken(CallSiteDescriptor.SCHEME)) {
return null;
}
final Object receiver = request.getReceiver();
if(receiver == null) {
// Can't operate on null

View File

@ -86,6 +86,9 @@ package jdk.internal.dynalink.beans;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.NamedOperation;
import jdk.internal.dynalink.Operation;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
@ -93,8 +96,9 @@ import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
import jdk.internal.dynalink.linker.support.Guards;
/**
* Simple linker that implements the "dyn:call" operation for {@link DynamicMethod} objects - the objects returned by
* "dyn:getMethod" from {@link AbstractJavaLinker}.
* Simple linker that implements the {@link StandardOperation#CALL} operation
* for {@link DynamicMethod} objects - the objects returned by
* {@link StandardOperation#GET_METHOD} through {@link AbstractJavaLinker}.
*/
class DynamicMethodLinker implements TypeBasedGuardingDynamicLinker {
@Override
@ -108,19 +112,16 @@ class DynamicMethodLinker implements TypeBasedGuardingDynamicLinker {
if(!(receiver instanceof DynamicMethod)) {
return null;
}
final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor();
if(desc.getNameTokenCount() != 2 && desc.getNameToken(CallSiteDescriptor.SCHEME) != "dyn") {
return null;
}
final String operator = desc.getNameToken(CallSiteDescriptor.OPERATOR);
final DynamicMethod dynMethod = (DynamicMethod)receiver;
final boolean constructor = dynMethod.isConstructor();
final MethodHandle invocation;
if (operator == "call" && !constructor) {
final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor();
final Operation op = NamedOperation.getBaseOperation(desc.getOperation());
if (op == StandardOperation.CALL && !constructor) {
invocation = dynMethod.getInvocation(desc.changeMethodType(
desc.getMethodType().dropParameterTypes(0, 1)), linkerServices);
} else if (operator == "new" && constructor) {
} else if (op == StandardOperation.NEW && constructor) {
final MethodHandle ctorInvocation = dynMethod.getInvocation(desc, linkerServices);
if(ctorInvocation == null) {
return null;

View File

@ -85,16 +85,17 @@ package jdk.internal.dynalink.beans;
import java.io.Serializable;
import java.util.Objects;
import jdk.internal.dynalink.StandardOperation;
/**
* Object that allows access to the static members of a class (its static
* methods, properties, and fields), as well as construction of instances using
* {@code "dyn:new"} operation. In Dynalink, {@link Class} objects are not
* treated specially and act as ordinary Java objects; you can use e.g. {@code
* "dyn:getProp:superclass"} as a property getter to invoke
* {@code clazz.getSuperclass()}. On the other hand, you can not use
* {@link StandardOperation#NEW} operation. In Dynalink, {@link Class} objects
* are not treated specially and act as ordinary Java objects; you can use e.g.
* {@code NamedOperation(GET_PROPERTY, "superclass")} as a property getter to
* invoke {@code clazz.getSuperclass()}. On the other hand, you can not use
* {@code Class} objects to access static members of a class, nor to create new
* instances of the class using {@code "dyn:new"}. This is consistent with how
* instances of the class using {@code NEW}. This is consistent with how
* {@code Class} objects behave in Java: in Java, you write e.g.
* {@code new BitSet()} instead of {@code new BitSet.class()}. Similarly, you
* write {@code System.out} and not {@code System.class.out}. It is this aspect
@ -119,10 +120,10 @@ import java.util.Objects;
* constructors taking a single int argument and create an array of the
* specified size.
* <p>
* If the class has several constructors, {@code dyn:new} on {@code StaticClass}
* will try to select the most specific applicable constructor. You might want
* to expose a mechanism in your language for selecting a constructor with an
* explicit signature through
* If the class has several constructors, {@link StandardOperation#NEW} on
* {@code StaticClass} will try to select the most specific applicable
* constructor. You might want to expose a mechanism in your language for
* selecting a constructor with an explicit signature through
* {@link BeansLinker#getConstructorMethod(Class, String)}.
*/
public final class StaticClass implements Serializable {

View File

@ -90,6 +90,8 @@ import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.NamedOperation;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
@ -150,8 +152,7 @@ class StaticClassLinker implements TypeBasedGuardingDynamicLinker {
return gi;
}
final CallSiteDescriptor desc = request.getCallSiteDescriptor();
final String op = desc.getNameToken(CallSiteDescriptor.OPERATOR);
if("new" == op && constructor != null) {
if(NamedOperation.getBaseOperation(desc.getOperation()) == StandardOperation.NEW && constructor != null) {
final MethodHandle ctorInvocation = constructor.getInvocation(desc, linkerServices);
if(ctorInvocation != null) {
return new GuardedInvocation(ctorInvocation, getClassGuard(desc.getMethodType()));

View File

@ -129,7 +129,7 @@
* bytecode would look something like this:
* <pre>
* aload 2 // load "obj" on stack
* invokedynamic "dyn:getProp:color"(Object)Object // invoke property getter on object of unknown type
* invokedynamic "GET_PROPERTY:color"(Object)Object // invoke property getter on object of unknown type
* astore 3 // store the return value into local variable "color"
* </pre>
* In order to link the {@code invokedynamic} instruction, we need a bootstrap
@ -145,7 +145,11 @@
* public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type) {
* return dynamicLinker.link(
* new SimpleRelinkableCallSite(
* new CallSiteDescriptor(lookup, name, type)));
* new CallSiteDescriptor(lookup, parseOperation(name), type)));
* }
*
* private static Operation parseOperation(String name) {
* ...
* }
* }
* </pre>
@ -164,9 +168,16 @@
* type. {@link jdk.internal.dynalink.support.SimpleRelinkableCallSite} and
* {@link jdk.internal.dynalink.support.ChainedCallSite} (not used in the above example)
* are two implementations already provided by the library.</li>
* <li>Finally, Dynalink uses {@link jdk.internal.dynalink.CallSiteDescriptor} objects to
* preserve the parameters to the bootstrap method as it will need them whenever
* it needs to relink a call site.</li>
* <li>Dynalink uses {@link jdk.internal.dynalink.CallSiteDescriptor} objects to
* preserve the parameters to the bootstrap method: the lookup and the method type,
* as it will need them whenever it needs to relink a call site.</li>
* <li>Dynalink uses {@link jdk.internal.dynalink.Operation} objects to express
* dynamic operations. It does not prescribe how would you encode the operations
* in your call site, though. That is why in the above example the
* {@code parseOperation} function is left empty, and you would be expected to
* provide the code to parse the string {@code "GET_PROPERTY:color"}
* in the call site's name into a named property getter operation object as
* {@code new NamedOperation(StandardOperation.GET_PROPERTY), "color")}.
* </ul>
* <p>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).
* <h2>Available operations</h2>
* 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.
* <p>&nbsp;
* <table summary="Dynalink defined operations" border="1" frame="border" cellpadding="5">
* <thead>
* <tr>
* <th>Name</th>
* <th>Signature</th>
* <th>Semantics</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>{@code dyn:getProp:$id}</td>
* <td>(any)any</td>
* <td>Retrieve 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}.</td>
* </tr>
* <tr>
* <td>{@code dyn:getProp}</td>
* <td>(any,&nbsp;any)any</td>
* <td>Retrieve 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.</td>
* </tr>
* <tr>
* <td>{@code dyn:setProp:$id}</td>
* <td>(any,&nbsp;any)void</td>
* <td>Set 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}.</td>
* </tr>
* <tr>
* <td>{@code dyn:setProp}</td>
* <td>(any,&nbsp;any,&nbsp;any)void</td>
* <td>Set 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.</td>
* </tr>
* <tr>
* <td>{@code dyn:getElem:$id}</td>
* <td>(any)any</td>
* <td>Retrieve 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.</td>
* </tr>
* <tr>
* <td>{@code dyn:getElem}</td>
* <td>(any,&nbsp;any)any</td>
* <td>Retrieve 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.</td>
* </tr>
* <tr>
* <td>{@code dyn:setElem:$id}</td>
* <td>(any,&nbsp;any)void</td>
* <td>Set 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.</td>
* </tr>
* <tr>
* <td>{@code dyn:setElem}</td>
* <td>(any,&nbsp;any,&nbsp;any)void</td>
* <td>Set 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.</td>
* </tr>
* <tr>
* <td>{@code dyn:getMethod:$id}</td>
* <td>(any)any</td>
* <td>Retrieve 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.</td>
* </tr>
* <tr>
* <td>{@code dyn:getMethod}</td>
* <td>(any,&nbsp;any)any</td>
* <td>Retrieve 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.</td>
* </tr>
* <tr>
* <td>{@code dyn:call}</td>
* <td>(any[,&nbsp;any,&nbsp;any,...])any</td>
* <td>Call 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.</td>
* </tr>
* <tr>
* <td>{@code dyn:callMethod:$id}</td>
* <td>(any[,&nbsp;any,&nbsp;any,...])any</td>
* <td>Call 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.</td>
* </tr>
* <tr>
* <td>{@code dyn:new}</td>
* <td>(any[,&nbsp;any,&nbsp;any,...])any</td>
* <td>Call a constructor. The first argument is the constructor itself,
* and the rest are arguments passed to the constructor call.</td>
* </tr>
* </tbody>
* </table>
* 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".
* <h2>Composite operations</h2>
* 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.
* <p>
* 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.
* <h2>Language-specific linkers</h2>
* 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

View File

@ -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
* <a href="https://blogs.oracle.com/jrose/entry/symbolic_freedom_in_the_vm"
* target="_blank">"Symbolic Freedom in the VM"</a> 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"</a> 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 @@ public final class NameCodec {
* @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();

View File

@ -683,9 +683,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// (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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
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

View File

@ -107,6 +107,7 @@ import jdk.nashorn.internal.runtime.ScriptObject;
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 @@ import jdk.nashorn.internal.runtime.options.Options;
* 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 @@ public class MethodEmitter {
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 @@ public class MethodEmitter {
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 @@ public class MethodEmitter {
}
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 @@ public class MethodEmitter {
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 @@ public class MethodEmitter {
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 @@ public class MethodEmitter {
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 @@ public class MethodEmitter {
}
}
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) {

View File

@ -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 {
/**

View File

@ -39,6 +39,7 @@ import java.util.LinkedHashSet;
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.signature.SignatureReader;
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 @@ import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
* 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<Label> labelIter;
@ -498,7 +501,16 @@ public final class NashornTextifier extends Printer {
final StringBuilder sb = new StringBuilder();
appendOpcode(sb, Opcodes.INVOKEDYNAMIC).append(' ');
sb.append(name);
final boolean isNashornBootstrap = isNashornBootstrap(bsm);
if (isNashornBootstrap) {
sb.append(NashornCallSiteDescriptor.getOperationName((Integer)bsmArgs[0]));
final String decodedName = NameCodec.decode(name);
if (!decodedName.isEmpty()) {
sb.append(':').append(decodedName);
}
} else {
sb.append(name);
}
appendDescriptor(sb, METHOD_DESCRIPTOR, desc);
final int len = sb.length();
for (int i = 0; i < 80 - len ; i++) {
@ -516,7 +528,7 @@ public final class NashornTextifier extends Printer {
sb.append(((Type)cst).getDescriptor()).append(".class");
} else if (cst instanceof Handle) {
appendHandle(sb, (Handle)cst);
} else if (cst instanceof Integer) {
} else if (cst instanceof Integer && isNashornBootstrap) {
final int c = (Integer)cst;
final int pp = c >> CALLSITE_PROGRAM_POINT_SHIFT;
if (pp != 0) {
@ -535,6 +547,10 @@ public final class NashornTextifier extends Printer {
addText(sb);
}
private static boolean isNashornBootstrap(final Handle bsm) {
return "bootstrap".equals(bsm.getName()) && BOOTSTRAP_CLASS_NAME.equals(bsm.getOwner());
}
private static boolean noFallThru(final int opcode) {
switch (opcode) {
case Opcodes.GOTO:

View File

@ -48,6 +48,7 @@ import java.util.concurrent.ConcurrentHashMap;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.api.scripting.ClassFilter;
@ -2148,17 +2149,17 @@ public final class Global extends Scope {
}
@Override
public GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
public GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final StandardOperation operation) {
final String name = NashornCallSiteDescriptor.getOperand(desc);
final boolean isScope = NashornCallSiteDescriptor.isScope(desc);
if (lexicalScope != null && isScope && !NashornCallSiteDescriptor.isApplyToCall(desc)) {
if (lexicalScope.hasOwnProperty(name)) {
return lexicalScope.findGetMethod(desc, request, operator);
return lexicalScope.findGetMethod(desc, request, operation);
}
}
final GuardedInvocation invocation = super.findGetMethod(desc, request, operator);
final GuardedInvocation invocation = super.findGetMethod(desc, request, operation);
// We want to avoid adding our generic lexical scope switchpoint to global constant invocations,
// because those are invalidated per-key in the addBoundProperties method above.
@ -2188,7 +2189,7 @@ public final class Global extends Scope {
final boolean isScope = NashornCallSiteDescriptor.isScope(desc);
if (lexicalScope != null && isScope) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final String name = NashornCallSiteDescriptor.getOperand(desc);
if (lexicalScope.hasOwnProperty(name)) {
return lexicalScope.findSetMethod(desc, request);
}
@ -2725,8 +2726,8 @@ public final class Global extends Scope {
}
@Override
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
return filterInvocation(super.findGetMethod(desc, request, operator));
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final StandardOperation operation) {
return filterInvocation(super.findGetMethod(desc, request, operation));
}
@Override

View File

@ -144,15 +144,6 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
setIsArray();
}
@Override
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
final GuardedInvocation inv = getArray().findFastGetMethod(getArray().getClass(), desc, request, operator);
if (inv != null) {
return inv;
}
return super.findGetMethod(desc, request, operator);
}
@Override
protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
final GuardedInvocation inv = getArray().findFastGetIndexMethod(getArray().getClass(), desc, request);
@ -187,7 +178,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
new Callable<MethodHandle>() {
@Override
public MethodHandle call() {
return Bootstrap.createDynamicInvoker("dyn:call", rtype, Object.class, Object.class, Object.class,
return Bootstrap.createDynamicCallInvoker(rtype, Object.class, Object.class, Object.class,
long.class, Object.class);
}
});
@ -218,7 +209,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
new Callable<MethodHandle>() {
@Override
public MethodHandle call() {
return Bootstrap.createDynamicInvoker("dyn:call", Object.class, Object.class,
return Bootstrap.createDynamicCallInvoker(Object.class, Object.class,
Undefined.class, Object.class, Object.class, long.class, Object.class);
}
});
@ -229,7 +220,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
new Callable<MethodHandle>() {
@Override
public MethodHandle call() {
return Bootstrap.createDynamicInvoker("dyn:call", double.class,
return Bootstrap.createDynamicCallInvoker(double.class,
ScriptFunction.class, Object.class, Object.class, Object.class);
}
});

View File

@ -37,6 +37,7 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.internal.lookup.Lookup;
@ -49,6 +50,7 @@ import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.scripts.JO;
/**
@ -588,7 +590,7 @@ public final class NativeJSAdapter extends ScriptObject {
@Override
protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
if (overrides && super.hasOwnProperty(desc.getNameToken(2))) {
if (overrides && super.hasOwnProperty(NashornCallSiteDescriptor.getOperand(desc))) {
try {
final GuardedInvocation inv = super.findCallMethodMethod(desc, request);
if (inv != null) {
@ -603,8 +605,8 @@ public final class NativeJSAdapter extends ScriptObject {
}
@Override
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operation) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final StandardOperation operation) {
final String name = NashornCallSiteDescriptor.getOperand(desc);
if (overrides && super.hasOwnProperty(name)) {
try {
final GuardedInvocation inv = super.findGetMethod(desc, request, operation);
@ -617,10 +619,10 @@ public final class NativeJSAdapter extends ScriptObject {
}
switch(operation) {
case "getProp":
case "getElem":
case GET_PROPERTY:
case GET_ELEMENT:
return findHook(desc, __get__);
case "getMethod":
case GET_METHOD:
final FindProperty find = adaptee.findProperty(__call__, true);
if (find != null) {
final Object value = find.getObjectValue();
@ -634,7 +636,7 @@ public final class NativeJSAdapter extends ScriptObject {
adaptee.getProtoSwitchPoints(__call__, find.getOwner()), null);
}
}
throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this));
throw typeError("no.such.function", name, ScriptRuntime.safeToString(this));
default:
break;
}
@ -644,7 +646,7 @@ public final class NativeJSAdapter extends ScriptObject {
@Override
protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
if (overrides && super.hasOwnProperty(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND))) {
if (overrides && super.hasOwnProperty(NashornCallSiteDescriptor.getOperand(desc))) {
try {
final GuardedInvocation inv = super.findSetMethod(desc, request);
if (inv != null) {
@ -691,7 +693,7 @@ public final class NativeJSAdapter extends ScriptObject {
final FindProperty findData = adaptee.findProperty(hook, true);
final MethodType type = desc.getMethodType();
if (findData != null) {
final String name = desc.getNameTokenCount() > 2 ? desc.getNameToken(2) : null;
final String name = NashornCallSiteDescriptor.getOperand(desc);
final Object value = findData.getObjectValue();
if (value instanceof ScriptFunction) {
final ScriptFunction func = (ScriptFunction)value;
@ -709,7 +711,7 @@ public final class NativeJSAdapter extends ScriptObject {
switch (hook) {
case __call__:
throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this));
throw typeError("no.such.function", NashornCallSiteDescriptor.getOperand(desc), ScriptRuntime.safeToString(this));
default:
final MethodHandle methodHandle = hook.equals(__put__) ?
MH.asType(Lookup.EMPTY_SETTER, type) :

View File

@ -76,7 +76,7 @@ public final class NativeJSON extends ScriptObject {
new Callable<MethodHandle>() {
@Override
public MethodHandle call() {
return Bootstrap.createDynamicInvoker("dyn:call", Object.class,
return Bootstrap.createDynamicCallInvoker(Object.class,
ScriptFunction.class, ScriptObject.class, Object.class, Object.class);
}
});

View File

@ -43,6 +43,7 @@ import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
/**
* This is "JavaImporter" constructor. This constructor allows you to use Java types omitting explicit package names.
@ -143,7 +144,7 @@ public final class NativeJavaImporter extends ScriptObject {
}
private boolean createAndSetProperty(final CallSiteDescriptor desc) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final String name = NashornCallSiteDescriptor.getOperand(desc);
final Object value = createProperty(name);
if(value != null) {
set(name, value, 0);

View File

@ -39,6 +39,10 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.NamedOperation;
import jdk.internal.dynalink.Operation;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.internal.dynalink.linker.GuardedInvocation;
@ -694,10 +698,7 @@ public final class NativeObject {
// make accessor properties using dynamic invoker getters and setters
final AccessorProperty[] props = new AccessorProperty[keys.length];
for (int idx = 0; idx < keys.length; idx++) {
final String name = keys[idx];
final MethodHandle getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getElem:" + name, MIRROR_GETTER_TYPE);
final MethodHandle setter = Bootstrap.createDynamicInvoker("dyn:setProp|setElem:" + name, MIRROR_SETTER_TYPE);
props[idx] = AccessorProperty.create(name, 0, getter, setter);
props[idx] = createAccessorProperty(keys[idx]);
}
targetObj.addBoundProperties(source, props);
@ -716,6 +717,12 @@ public final class NativeObject {
return target;
}
private static AccessorProperty createAccessorProperty(final String name) {
final MethodHandle getter = Bootstrap.createDynamicInvoker(name, NashornCallSiteDescriptor.GET_METHOD_PROPERTY, MIRROR_GETTER_TYPE);
final MethodHandle setter = Bootstrap.createDynamicInvoker(name, NashornCallSiteDescriptor.SET_PROPERTY, MIRROR_SETTER_TYPE);
return AccessorProperty.create(name, 0, getter, setter);
}
/**
* Binds the source mirror object's properties to the target object. Binding
* properties allows two-way read/write for the properties of the source object.
@ -732,9 +739,7 @@ public final class NativeObject {
final AccessorProperty[] props = new AccessorProperty[keys.size()];
int idx = 0;
for (final String name : keys) {
final MethodHandle getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getElem:" + name, MIRROR_GETTER_TYPE);
final MethodHandle setter = Bootstrap.createDynamicInvoker("dyn:setProp|setElem:" + name, MIRROR_SETTER_TYPE);
props[idx] = AccessorProperty.create(name, 0, getter, setter);
props[idx] = createAccessorProperty(name);
idx++;
}
@ -759,7 +764,7 @@ public final class NativeObject {
for(final String methodName: methodNames) {
final MethodHandle method;
try {
method = getBeanOperation(linker, "dyn:getMethod:" + methodName, getterType, source);
method = getBeanOperation(linker, StandardOperation.GET_METHOD, methodName, getterType, source);
} catch(final IllegalAccessError e) {
// Presumably, this was a caller sensitive method. Ignore it and carry on.
continue;
@ -771,7 +776,7 @@ public final class NativeObject {
MethodHandle getter;
if(readablePropertyNames.contains(propertyName)) {
try {
getter = getBeanOperation(linker, "dyn:getProp:" + propertyName, getterType, source);
getter = getBeanOperation(linker, StandardOperation.GET_PROPERTY, propertyName, getterType, source);
} catch(final IllegalAccessError e) {
// Presumably, this was a caller sensitive method. Ignore it and carry on.
getter = Lookup.EMPTY_GETTER;
@ -783,7 +788,7 @@ public final class NativeObject {
MethodHandle setter;
if(isWritable) {
try {
setter = getBeanOperation(linker, "dyn:setProp:" + propertyName, setterType, source);
setter = getBeanOperation(linker, StandardOperation.SET_PROPERTY, propertyName, setterType, source);
} catch(final IllegalAccessError e) {
// Presumably, this was a caller sensitive method. Ignore it and carry on.
setter = Lookup.EMPTY_SETTER;
@ -801,7 +806,7 @@ public final class NativeObject {
private static MethodHandle getBoundBeanMethodGetter(final Object source, final MethodHandle methodGetter) {
try {
// NOTE: we're relying on the fact that "dyn:getMethod:..." return value is constant for any given method
// NOTE: we're relying on the fact that StandardOperation.GET_METHOD return value is constant for any given method
// name and object linked with BeansLinker. (Actually, an even stronger assumption is true: return value is
// constant for any given method name and object's class.)
return MethodHandles.dropArguments(MethodHandles.constant(Object.class,
@ -813,11 +818,11 @@ public final class NativeObject {
}
}
private static MethodHandle getBeanOperation(final GuardingDynamicLinker linker, final String operation,
final MethodType methodType, final Object source) {
private static MethodHandle getBeanOperation(final GuardingDynamicLinker linker, final StandardOperation operation,
final String name, final MethodType methodType, final Object source) {
final GuardedInvocation inv;
try {
inv = NashornBeansLinker.getGuardedInvocation(linker, createLinkRequest(operation, methodType, source), Bootstrap.getLinkerServices());
inv = NashornBeansLinker.getGuardedInvocation(linker, createLinkRequest(new NamedOperation(operation, name), methodType, source), Bootstrap.getLinkerServices());
assert passesGuard(source, inv.getGuard());
} catch(RuntimeException|Error e) {
throw e;
@ -833,9 +838,9 @@ public final class NativeObject {
return guard == null || (boolean)guard.invoke(obj);
}
private static LinkRequest createLinkRequest(final String operation, final MethodType methodType, final Object source) {
return new SimpleLinkRequest(NashornCallSiteDescriptor.get(MethodHandles.publicLookup(), operation,
methodType, 0), false, source);
private static LinkRequest createLinkRequest(final Operation operation, final MethodType methodType, final Object source) {
return new SimpleLinkRequest(new CallSiteDescriptor(MethodHandles.publicLookup(), operation,
methodType), false, source);
}
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {

View File

@ -33,7 +33,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Constructor;
import jdk.nashorn.internal.objects.annotations.Function;
@ -808,7 +807,7 @@ public final class NativeRegExp extends ScriptObject {
new Callable<MethodHandle>() {
@Override
public MethodHandle call() {
return Bootstrap.createDynamicInvoker("dyn:call", String.class, ScriptFunction.class, Object.class, Object[].class);
return Bootstrap.createDynamicCallInvoker(String.class, ScriptFunction.class, Object.class, Object[].class);
}
});
}

View File

@ -41,6 +41,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Set;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.internal.lookup.MethodHandleFactory.LookupException;
@ -60,6 +61,7 @@ import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.linker.NashornGuards;
import jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
@ -138,15 +140,15 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
// This is to support length as method call as well.
@Override
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
final String name = desc.getNameToken(2);
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final StandardOperation operation) {
final String name = NashornCallSiteDescriptor.getOperand(desc);
// if str.length(), then let the bean linker handle it
if ("length".equals(name) && "getMethod".equals(operator)) {
if ("length".equals(name) && operation == StandardOperation.GET_METHOD) {
return null;
}
return super.findGetMethod(desc, request, operator);
return super.findGetMethod(desc, request, operation);
}
// This is to provide array-like access to string characters without creating a NativeString wrapper.

View File

@ -357,7 +357,7 @@ public final class GlobalConstants implements Loggable {
return null;
}
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final String name = NashornCallSiteDescriptor.getOperand(desc);
synchronized (this) {
final Access acc = getOrCreateSwitchPoint(name);
@ -432,7 +432,7 @@ public final class GlobalConstants implements Loggable {
final boolean isOptimistic = NashornCallSiteDescriptor.isOptimistic(desc);
final int programPoint = isOptimistic ? getProgramPoint(desc) : INVALID_PROGRAM_POINT;
final Class<?> retType = desc.getMethodType().returnType();
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final String name = NashornCallSiteDescriptor.getOperand(desc);
synchronized (this) {
final Access acc = getOrCreateSwitchPoint(name);

View File

@ -45,7 +45,7 @@ public final class JSONFunctions {
new Callable<MethodHandle>() {
@Override
public MethodHandle call() {
return Bootstrap.createDynamicInvoker("dyn:call", Object.class,
return Bootstrap.createDynamicCallInvoker(Object.class,
ScriptFunction.class, ScriptObject.class, String.class, Object.class);
}
});

View File

@ -400,7 +400,7 @@ public class ListAdapter extends AbstractList<Object> implements RandomAccess, D
return new Callable<MethodHandle>() {
@Override
public MethodHandle call() {
return Bootstrap.createDynamicInvoker("dyn:call", rtype, ptypes);
return Bootstrap.createDynamicCallInvoker(rtype, ptypes);
}
};
}

View File

@ -41,6 +41,7 @@ import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
/**
* An object that exposes Java packages and classes as its properties. Packages are exposed as objects that have further
@ -200,7 +201,7 @@ public final class NativeJavaPackage extends ScriptObject {
*/
@Override
public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
final String propertyName = desc.getNameToken(2);
final String propertyName = NashornCallSiteDescriptor.getOperand(desc);
createProperty(propertyName);
return super.lookup(desc, request);
}

View File

@ -813,7 +813,7 @@ public class ScriptFunction extends ScriptObject {
}
/**
* dyn:call call site signature: (callee, thiz, [args...]) generated method
* StandardOperation.CALL call site signature: (callee, thiz, [args...]) generated method
* signature: (callee, thiz, [args...])
*
* cases:

View File

@ -66,6 +66,8 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.LongAdder;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.NamedOperation;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
@ -1835,32 +1837,35 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @return GuardedInvocation for the callsite
*/
public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
final int c = desc.getNameTokenCount();
// JavaScript is "immune" to all currently defined Dynalink composite operation - getProp is the same as getElem
// is the same as getMethod as JavaScript objects have a single namespace for all three. Therefore, we don't
// care about them, and just link to whatever is the first operation.
final String operator = desc.tokenizeOperators().get(0);
// NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself
// emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are
// NOTE: we support GET_ELEMENT and SET_ELEMENT as JavaScript doesn't distinguish items from properties. Nashorn itself
// emits "GET_PROPERTY|GET_ELEMENT|GET_METHOD:identifier" for "<expr>.<identifier>" and "GET_ELEMENT|GET_PROPERTY|GET_METHOD" for "<expr>[<expr>]", but we are
// more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
// operation has an associated name or not.
switch (operator) {
case "getProp":
case "getElem":
case "getMethod":
return c > 2 ? findGetMethod(desc, request, operator) : findGetIndexMethod(desc, request);
case "setProp":
case "setElem":
return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc, request);
case "call":
return findCallMethod(desc, request);
case "new":
return findNewMethod(desc, request);
case "callMethod":
return findCallMethodMethod(desc, request);
default:
final StandardOperation op = NashornCallSiteDescriptor.getFirstStandardOperation(desc);
if (op == null) {
return null;
}
switch (op) {
case GET_PROPERTY:
case GET_ELEMENT:
case GET_METHOD:
return desc.getOperation() instanceof NamedOperation
? findGetMethod(desc, request, op)
: findGetIndexMethod(desc, request);
case SET_PROPERTY:
case SET_ELEMENT:
return desc.getOperation() instanceof NamedOperation
? findSetMethod(desc, request)
: findSetIndexMethod(desc, request);
case CALL:
return findCallMethod(desc, request);
case NEW:
return findNewMethod(desc, request);
case CALL_METHOD:
return findCallMethodMethod(desc, request);
default:
}
return null;
}
/**
@ -1893,10 +1898,10 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
}
/**
* Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses
* "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another
* one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external
* callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle.
* Find an implementation for a CALL_METHOD operation. Note that Nashorn internally never uses
* CALL_METHOD, but instead always emits two call sites in bytecode, one for GET_METHOD, and then another
* one for CALL. Explicit support for CALL_METHOD is provided for the benefit of potential external
* callers. The implementation itself actually folds a GET_METHOD method handle into a CALL method handle.
*
* @param desc the call site descriptor.
* @param request the link request
@ -1908,12 +1913,12 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
final MethodType callType = desc.getMethodType();
// use type Object(P0) for the getter
final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod");
final GuardedInvocation getter = findGetMethod(getterType, request, StandardOperation.GET_METHOD);
// Object(P0) => Object(P0, P1, ...)
final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
// R(Object, P0, P1, ...)
final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
final MethodHandle invoker = Bootstrap.createDynamicInvoker("", NashornCallSiteDescriptor.CALL, callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
// Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
}
@ -1954,15 +1959,14 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
*
* @param desc the call site descriptor
* @param request the link request
* @param operator operator for get: getProp, getMethod, getElem etc
* @param operation operation for get: getProp, getMethod, getElem etc
*
* @return GuardedInvocation to be invoked at call site.
*/
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final StandardOperation operation) {
final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
String name;
name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
String name = NashornCallSiteDescriptor.getOperand(desc);
if (NashornCallSiteDescriptor.isApplyToCall(desc)) {
if (Global.isBuiltinFunctionPrototypeApply()) {
name = "call";
@ -1970,21 +1974,21 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
}
if (request.isCallSiteUnstable() || hasWithScope()) {
return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator));
return findMegaMorphicGetMethod(desc, name, operation == StandardOperation.GET_METHOD);
}
final FindProperty find = findProperty(name, true);
MethodHandle mh;
if (find == null) {
switch (operator) {
case "getElem": // getElem only gets here if element name is constant, so treat it like a property access
case "getProp":
switch (operation) {
case GET_ELEMENT: // getElem only gets here if element name is constant, so treat it like a property access
case GET_PROPERTY:
return noSuchProperty(desc, request);
case "getMethod":
case GET_METHOD:
return noSuchMethod(desc, request);
default:
throw new AssertionError(operator); // never invoked with any other operation
throw new AssertionError(operation); // never invoked with any other operation
}
}
@ -2162,7 +2166,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @return GuardedInvocation to be invoked at call site.
*/
protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final String name = NashornCallSiteDescriptor.getOperand(desc);
if (request.isCallSiteUnstable() || hasWithScope()) {
return findMegaMorphicSetMethod(desc, name);
@ -2224,7 +2228,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
}
private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String strictErrorMessage, final boolean canBeFastScope) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final String name = NashornCallSiteDescriptor.getOperand(desc);
if (NashornCallSiteDescriptor.isStrict(desc)) {
throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString(this));
}
@ -2306,7 +2310,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @return GuardedInvocation to be invoked at call site.
*/
public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) {
final String name = desc.getNameToken(2);
final String name = NashornCallSiteDescriptor.getOperand(desc);
final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true);
final boolean scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc);
@ -2344,7 +2348,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @return GuardedInvocation to be invoked at call site.
*/
public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final String name = NashornCallSiteDescriptor.getOperand(desc);
final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
@ -2684,7 +2688,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @param newLength new length to set
*/
public final void setLength(final long newLength) {
ArrayData data = getArray();
final ArrayData data = getArray();
final long arrayLength = data.length();
if (newLength == arrayLength) {
return;

View File

@ -380,8 +380,8 @@ public final class ScriptRuntime {
/**
* Call a function given self and args. If the number of the arguments is known in advance, you can likely achieve
* better performance by {@link Bootstrap#createDynamicInvoker(String, Class, Class...) creating a dynamic invoker}
* for operation {@code "dyn:call"}, then using its {@link MethodHandle#invokeExact(Object...)} method instead.
* better performance by creating a dynamic invoker using {@link Bootstrap#createDynamicCallInvoker(Class, Class...)}
* then using its {@link MethodHandle#invokeExact(Object...)} method instead.
*
* @param target ScriptFunction object.
* @param self Receiver in call.

View File

@ -28,6 +28,7 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.SwitchPoint;
import jdk.internal.dynalink.CallSiteDescriptor;
@ -69,7 +70,7 @@ final class SetMethodCreator {
}
private String getName() {
return desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
return NashornCallSiteDescriptor.getOperand(desc);
}
private PropertyMap getMap() {
@ -196,7 +197,7 @@ final class SetMethodCreator {
final PropertyMap oldMap = getMap();
final PropertyMap newMap = getNewMap(property);
final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final String name = NashornCallSiteDescriptor.getOperand(desc);
//fast type specific setter
final MethodHandle fastSetter = property.getSetter(type, newMap); //0 sobj, 1 value, slot folded for spill property already

View File

@ -31,6 +31,8 @@ import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.NamedOperation;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.support.Guards;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
@ -91,44 +93,39 @@ public final class Undefined extends DefaultPropertyAccess {
* @return GuardedInvocation to be invoked at call site.
*/
public static GuardedInvocation lookup(final CallSiteDescriptor desc) {
final String operator = desc.tokenizeOperators().get(0);
switch (operator) {
case "new":
case "call": {
final String name = NashornCallSiteDescriptor.getFunctionDescription(desc);
final StandardOperation op = NashornCallSiteDescriptor.getFirstStandardOperation(desc);
switch (op) {
case CALL:
case NEW:
final String name = NashornCallSiteDescriptor.getOperand(desc);
final String msg = name != null? "not.a.function" : "cant.call.undefined";
throw typeError(msg, name);
}
case "callMethod":
case CALL_METHOD:
throw lookupTypeError("cant.read.property.of.undefined", desc);
// NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself
// emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are
// more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
// operation has an associated name or not.
case "getProp":
case "getElem":
case "getMethod":
if (desc.getNameTokenCount() < 3) {
case GET_PROPERTY:
case GET_ELEMENT:
case GET_METHOD:
// NOTE: we support GET_ELEMENT and SET_ELEMENT as JavaScript doesn't distinguish items from properties. Nashorn itself
// emits "GET_PROPERTY|GET_ELEMENT|GET_METHOD:identifier" for "<expr>.<identifier>" and "GET_ELEMENT|GET_PROPERTY|GET_METHOD" for "<expr>[<expr>]", but we are
// more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
// operation has an associated name or not.
if (!(desc.getOperation() instanceof NamedOperation)) {
return findGetIndexMethod(desc);
}
return findGetMethod(desc);
case "setProp":
case "setElem":
if (desc.getNameTokenCount() < 3) {
case SET_PROPERTY:
case SET_ELEMENT:
if (!(desc.getOperation() instanceof NamedOperation)) {
return findSetIndexMethod(desc);
}
return findSetMethod(desc);
default:
break;
}
return null;
}
private static ECMAException lookupTypeError(final String msg, final CallSiteDescriptor desc) {
final String name = desc.getNameToken(2);
final String name = NashornCallSiteDescriptor.getOperand(desc);
return typeError(msg, name != null && !name.isEmpty()? name : null);
}
@ -136,7 +133,7 @@ public final class Undefined extends DefaultPropertyAccess {
private static final MethodHandle SET_METHOD = MH.insertArguments(findOwnMH("set", void.class, Object.class, Object.class, int.class), 3, NashornCallSiteDescriptor.CALLSITE_STRICT);
private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc) {
return new GuardedInvocation(MH.insertArguments(GET_METHOD, 1, desc.getNameToken(2)), UNDEFINED_GUARD).asType(desc);
return new GuardedInvocation(MH.insertArguments(GET_METHOD, 1, NashornCallSiteDescriptor.getOperand(desc)), UNDEFINED_GUARD).asType(desc);
}
private static GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc) {
@ -144,7 +141,7 @@ public final class Undefined extends DefaultPropertyAccess {
}
private static GuardedInvocation findSetMethod(final CallSiteDescriptor desc) {
return new GuardedInvocation(MH.insertArguments(SET_METHOD, 1, desc.getNameToken(2)), UNDEFINED_GUARD).asType(desc);
return new GuardedInvocation(MH.insertArguments(SET_METHOD, 1, NashornCallSiteDescriptor.getOperand(desc)), UNDEFINED_GUARD).asType(desc);
}
private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc) {

View File

@ -92,10 +92,10 @@ public final class UserAccessorProperty extends SpillProperty {
static MethodHandle getINVOKE_UA_GETTER(final Class<?> returnType, final int programPoint) {
if (UnwarrantedOptimismException.isValid(programPoint)) {
final int flags = NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC | programPoint << CALLSITE_PROGRAM_POINT_SHIFT;
return Bootstrap.createDynamicInvoker("dyn:call", flags, returnType, Object.class, Object.class);
final int flags = NashornCallSiteDescriptor.CALL | NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC | programPoint << CALLSITE_PROGRAM_POINT_SHIFT;
return Bootstrap.createDynamicInvoker("", flags, returnType, Object.class, Object.class);
} else {
return Bootstrap.createDynamicInvoker("dyn:call", Object.class, Object.class, Object.class);
return Bootstrap.createDynamicCallInvoker(Object.class, Object.class, Object.class);
}
}
@ -110,7 +110,7 @@ public final class UserAccessorProperty extends SpillProperty {
}
static MethodHandle getINVOKE_UA_SETTER(final Class<?> valueType) {
return Bootstrap.createDynamicInvoker("dyn:call", void.class, Object.class, Object.class, valueType);
return Bootstrap.createDynamicCallInvoker(void.class, Object.class, Object.class, valueType);
}
/**
@ -295,7 +295,7 @@ public final class UserAccessorProperty extends SpillProperty {
return super.getGetter(Object.class).asType(MethodType.methodType(Accessors.class, Object.class));
}
// User defined getter and setter are always called by "dyn:call". Note that the user
// User defined getter and setter are always called by StandardOperation.CALL. Note that the user
// getter/setter may be inherited. If so, proto is bound during lookup. In either
// inherited or self case, slot is also bound during lookup. Actual ScriptFunction
// to be called is retrieved everytime and applied.

View File

@ -33,6 +33,9 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.NamedOperation;
import jdk.internal.dynalink.Operation;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.api.scripting.AbstractJSObject;
@ -102,9 +105,10 @@ public final class WithObject extends Scope {
final boolean isNamedOperation;
final String name;
if (desc.getNameTokenCount() > 2) {
final Operation op = desc.getOperation();
if (op instanceof NamedOperation) {
isNamedOperation = true;
name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
name = ((NamedOperation)op).getName().toString();
} else {
isNamedOperation = false;
name = null;
@ -136,16 +140,13 @@ public final class WithObject extends Scope {
if (self != null) {
final String fallBack;
final String operator = desc.tokenizeOperators().get(0);
switch (operator) {
case "callMethod":
throw new AssertionError(); // Nashorn never emits callMethod
case "getMethod":
final StandardOperation firstOp = ndesc.getFirstOperation();
switch (firstOp) {
case GET_METHOD:
fallBack = NO_SUCH_METHOD_NAME;
break;
case "getProp":
case "getElem":
case GET_PROPERTY:
case GET_ELEMENT:
fallBack = NO_SUCH_PROPERTY_NAME;
break;
default:
@ -156,12 +157,12 @@ public final class WithObject extends Scope {
if (fallBack != null) {
find = self.findProperty(fallBack, true);
if (find != null) {
switch (operator) {
case "getMethod":
switch (firstOp) {
case GET_METHOD:
link = self.noSuchMethod(desc, request);
break;
case "getProp":
case "getElem":
case GET_PROPERTY:
case GET_ELEMENT:
link = self.noSuchProperty(desc, request);
break;
default:
@ -262,7 +263,7 @@ public final class WithObject extends Scope {
private static GuardedInvocation fixExpressionCallSite(final NashornCallSiteDescriptor desc, final GuardedInvocation link) {
// If it's not a getMethod, just add an expression filter that converts WithObject in "this" position to its
// expression.
if (!"getMethod".equals(desc.getFirstOperator())) {
if (desc.getFirstOperation() != StandardOperation.GET_METHOD) {
return fixReceiverType(link, WITHEXPRESSIONFILTER).filterArguments(0, WITHEXPRESSIONFILTER);
}

View File

@ -910,19 +910,6 @@ public abstract class ArrayData {
return null;
}
/**
* Find a fast property getter if one exists
*
* @param clazz array data class
* @param desc callsite descriptor
* @param request link request
* @param operator operator
* @return fast property getter if one is found
*/
public GuardedInvocation findFastGetMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
return null;
}
/**
* Find a fast element getter if one exists
*

View File

@ -45,6 +45,7 @@ import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
import jdk.internal.dynalink.linker.support.TypeUtilities;
import jdk.internal.dynalink.support.NameCodec;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.lookup.MethodHandleFactory;
@ -260,130 +261,156 @@ public final class Bootstrap {
}
/**
* Returns a dynamic invoker for a specified dynamic operation using the public lookup. You can use this method to
* create a method handle that when invoked acts completely as if it were a Nashorn-linked call site. An overview of
* available dynamic operations can be found in the
* <a href="https://github.com/szegedi/dynalink/wiki/User-Guide-0.6">Dynalink User Guide</a>, but we'll show few
* examples here:
* Returns a dynamic invoker for a specified dynamic operation using the
* public lookup. You can use this method to create a method handle that
* when invoked acts completely as if it were a Nashorn-linked call site.
* Note that the available operations are encoded in the flags, see
* {@link NashornCallSiteDescriptor} operation constants. If the operation
* takes a name, it should be set otherwise empty name (not null) should be
* used. All names (including the empty one) should be encoded using
* {@link NameCodec#encode(String)}. Few examples:
* <ul>
* <li>Get a named property with fixed name:
* <pre>
* MethodHandle getColor = Boostrap.createDynamicInvoker("dyn:getProp:color", Object.class, Object.class);
* MethodHandle getColor = Boostrap.createDynamicInvoker(
* "color",
* NashornCallSiteDescriptor.GET_PROPERTY,
* Object.class, Object.class);
* Object obj = ...; // somehow obtain the object
* Object color = getColor.invokeExact(obj);
* </pre>
* </li>
* <li>Get a named property with variable name:
* <pre>
* MethodHandle getProperty = Boostrap.createDynamicInvoker("dyn:getElem", Object.class, Object.class, String.class);
* MethodHandle getProperty = Boostrap.createDynamicInvoker(
* NameCodec.encode(""),
* NashornCallSiteDescriptor.GET_PROPERTY,
* Object.class, Object.class, String.class);
* Object obj = ...; // somehow obtain the object
* Object color = getProperty.invokeExact(obj, "color");
* Object shape = getProperty.invokeExact(obj, "shape");
* MethodHandle getNumProperty = Boostrap.createDynamicInvoker("dyn:getElem", Object.class, Object.class, int.class);
*
* MethodHandle getNumProperty = Boostrap.createDynamicInvoker(
* NameCodec.encode(""),
* NashornCallSiteDescriptor.GET_ELEMENT,
* Object.class, Object.class, int.class);
* Object elem42 = getNumProperty.invokeExact(obj, 42);
* </pre>
* </li>
* <li>Set a named property with fixed name:
* <pre>
* MethodHandle setColor = Boostrap.createDynamicInvoker("dyn:setProp:color", void.class, Object.class, Object.class);
* MethodHandle setColor = Boostrap.createDynamicInvoker(
* "color",
* NashornCallSiteDescriptor.SET_PROPERTY,
* void.class, Object.class, Object.class);
* Object obj = ...; // somehow obtain the object
* setColor.invokeExact(obj, Color.BLUE);
* </pre>
* </li>
* <li>Set a property with variable name:
* <pre>
* MethodHandle setProperty = Boostrap.createDynamicInvoker("dyn:setElem", void.class, Object.class, String.class, Object.class);
* MethodHandle setProperty = Boostrap.createDynamicInvoker(
* NameCodec.encode(""),
* NashornCallSiteDescriptor.SET_PROPERTY,
* void.class, Object.class, String.class, Object.class);
* Object obj = ...; // somehow obtain the object
* setProperty.invokeExact(obj, "color", Color.BLUE);
* setProperty.invokeExact(obj, "shape", Shape.CIRCLE);
* </pre>
* </li>
* <li>Call a function on an object; two-step variant. This is the actual variant used by Nashorn-generated code:
* <li>Call a function on an object; note it's a two-step process: get the
* method, then invoke the method. This is the actual:
* <pre>
* MethodHandle findFooFunction = Boostrap.createDynamicInvoker("dyn:getMethod:foo", Object.class, Object.class);
* MethodHandle findFooFunction = Boostrap.createDynamicInvoker(
* "foo",
* NashornCallSiteDescriptor.GET_METHOD,
* Object.class, Object.class);
* Object obj = ...; // somehow obtain the object
* Object foo_fn = findFooFunction.invokeExact(obj);
* MethodHandle callFunctionWithTwoArgs = Boostrap.createDynamicInvoker("dyn:call", Object.class, Object.class, Object.class, Object.class, Object.class);
* MethodHandle callFunctionWithTwoArgs = Boostrap.createDynamicCallInvoker(
* Object.class, Object.class, Object.class, Object.class, Object.class);
* // Note: "call" operation takes a function, then a "this" value, then the arguments:
* Object foo_retval = callFunctionWithTwoArgs.invokeExact(foo_fn, obj, arg1, arg2);
* </pre>
* </li>
* <li>Call a function on an object; single-step variant. Although Nashorn doesn't use this variant and never
* emits any INVOKEDYNAMIC instructions with {@code dyn:getMethod}, it still supports this standard Dynalink
* operation:
* <pre>
* MethodHandle callFunctionFooWithTwoArgs = Boostrap.createDynamicInvoker("dyn:callMethod:foo", Object.class, Object.class, Object.class, Object.class);
* Object obj = ...; // somehow obtain the object
* Object foo_retval = callFunctionFooWithTwoArgs.invokeExact(obj, arg1, arg2);
* </pre>
* </li>
* </ul>
* Few additional remarks:
* <ul>
* <li>Just as Nashorn works with any Java object, the invokers returned from this method can also be applied to
* arbitrary Java objects in addition to Nashorn JavaScript objects.</li>
* <li>For invoking a named function on an object, you can also use the {@link InvokeByName} convenience class.</li>
* <li>For Nashorn objects {@code getElem}, {@code getProp}, and {@code getMethod} are handled almost identically,
* since JavaScript doesn't distinguish between different kinds of properties on an object. Either can be used with
* fixed property name or a variable property name. The only significant difference is handling of missing
* properties: {@code getMethod} for a missing member will link to a potential invocation of
* {@code __noSuchMethod__} on the object, {@code getProp} for a missing member will link to a potential invocation
* of {@code __noSuchProperty__}, while {@code getElem} for a missing member will link to an empty getter.</li>
* <li>In similar vein, {@code setElem} and {@code setProp} are handled identically on Nashorn objects.</li>
* <li>There's no rule that the variable property identifier has to be a {@code String} for {@code getProp/setProp}
* and {@code int} for {@code getElem/setElem}. You can declare their type to be {@code int}, {@code double},
* {@code Object}, and so on regardless of the kind of the operation.</li>
* <li>You can be as specific in parameter types as you want. E.g. if you know that the receiver of the operation
* will always be {@code ScriptObject}, you can pass {@code ScriptObject.class} as its parameter type. If you happen
* to link to a method that expects different types, (you can use these invokers on POJOs too, after all, and end up
* linking with their methods that have strongly-typed signatures), all necessary conversions allowed by either Java
* or JavaScript will be applied: if invoked methods specify either primitive or wrapped Java numeric types, or
* {@code String} or {@code boolean/Boolean}, then the parameters might be subjected to standard ECMAScript
* {@code ToNumber}, {@code ToString}, and {@code ToBoolean} conversion, respectively. Less obviously, if the
* expected parameter type is a SAM type, and you pass a JavaScript function, a proxy object implementing the SAM
* type and delegating to the function will be passed. Linkage can often be optimized when linkers have more
* specific type information than "everything can be an object".</li>
* <li>You can also be as specific in return types as you want. For return types any necessary type conversion
* available in either Java or JavaScript will be automatically applied, similar to the process described for
* parameters, only in reverse direction: if you specify any either primitive or wrapped Java numeric type, or
* {@code String} or {@code boolean/Boolean}, then the return values will be subjected to standard ECMAScript
* {@code ToNumber}, {@code ToString}, and {@code ToBoolean} conversion, respectively. Less obviously, if the return
* type is a SAM type, and the return value is a JavaScript function, a proxy object implementing the SAM type and
* delegating to the function will be returned.</li>
* <li>Just as Nashorn works with any Java object, the invokers returned
* from this method can also be applied to arbitrary Java objects in
* addition to Nashorn JavaScript objects.</li>
* <li>For invoking a named function on an object, you can also use the
* {@link InvokeByName} convenience class.</li>
* <li>There's no rule that the variable property identifier has to be a
* {@code String} for {@code GET_PROPERTY/SET_PROPERTY} and {@code int} for
* {@code GET_ELEMENT/SET_ELEMENT}. You can declare their type to be
* {@code int}, {@code double}, {@code Object}, and so on regardless of the
* kind of the operation.</li>
* <li>You can be as specific in parameter types as you want. E.g. if you
* know that the receiver of the operation will always be
* {@code ScriptObject}, you can pass {@code ScriptObject.class} as its
* parameter type. If you happen to link to a method that expects different
* types, (you can use these invokers on POJOs too, after all, and end up
* linking with their methods that have strongly-typed signatures), all
* necessary conversions allowed by either Java or JavaScript will be
* applied: if invoked methods specify either primitive or wrapped Java
* numeric types, or {@code String} or {@code boolean/Boolean}, then the
* parameters might be subjected to standard ECMAScript {@code ToNumber},
* {@code ToString}, and {@code ToBoolean} conversion, respectively. Less
* obviously, if the expected parameter type is a SAM type, and you pass a
* JavaScript function, a proxy object implementing the SAM type and
* delegating to the function will be passed. Linkage can often be optimized
* when linkers have more specific type information than "everything can be
* an object".</li>
* <li>You can also be as specific in return types as you want. For return
* types any necessary type conversion available in either Java or
* JavaScript will be automatically applied, similar to the process
* described for parameters, only in reverse direction: if you specify any
* either primitive or wrapped Java numeric type, or {@code String} or
* {@code boolean/Boolean}, then the return values will be subjected to
* standard ECMAScript {@code ToNumber}, {@code ToString}, and
* {@code ToBoolean} conversion, respectively. Less obviously, if the return
* type is a SAM type, and the return value is a JavaScript function, a
* proxy object implementing the SAM type and delegating to the function
* will be returned.</li>
* </ul>
* @param opDesc Dynalink dynamic operation descriptor.
* @param name name at the call site. Must not be null. Must be encoded
* using {@link NameCodec#encode(String)}. If the operation does not take a
* name, use empty string (also has to be encoded).
* @param flags the call site flags for the operation; see
* {@link NashornCallSiteDescriptor} for available flags. The most important
* part of the flags are the ones encoding the actual operation.
* @param rtype the return type for the operation
* @param ptypes the parameter types for the operation
* @return MethodHandle for invoking the operation.
*/
public static MethodHandle createDynamicInvoker(final String opDesc, final Class<?> rtype, final Class<?>... ptypes) {
return createDynamicInvoker(opDesc, MethodType.methodType(rtype, ptypes));
public static MethodHandle createDynamicInvoker(final String name, final int flags, final Class<?> rtype, final Class<?>... ptypes) {
return bootstrap(MethodHandles.publicLookup(), name, MethodType.methodType(rtype, ptypes), flags).dynamicInvoker();
}
/**
* Returns a dynamic invoker for a specified dynamic operation using the public lookup. Similar to
* {@link #createDynamicInvoker(String, Class, Class...)} but with an additional parameter to
* set the call site flags of the dynamic invoker.
* @param opDesc Dynalink dynamic operation descriptor.
* @param flags the call site flags for the operation
* Returns a dynamic invoker for the {@link NashornCallSiteDescriptor#CALL}
* operation using the public lookup.
* @param rtype the return type for the operation
* @param ptypes the parameter types for the operation
* @return MethodHandle for invoking the operation.
* @return a dynamic invoker for the {@code CALL} operation.
*/
public static MethodHandle createDynamicInvoker(final String opDesc, final int flags, final Class<?> rtype, final Class<?>... ptypes) {
return bootstrap(MethodHandles.publicLookup(), opDesc, MethodType.methodType(rtype, ptypes), flags).dynamicInvoker();
public static MethodHandle createDynamicCallInvoker(final Class<?> rtype, final Class<?>... ptypes) {
return createDynamicInvoker("", NashornCallSiteDescriptor.CALL, rtype, ptypes);
}
/**
* Returns a dynamic invoker for a specified dynamic operation using the public lookup. Similar to
* {@link #createDynamicInvoker(String, Class, Class...)} but with return and parameter types composed into a
* method type in the signature. See the discussion of that method for details.
* @param opDesc Dynalink dynamic operation descriptor.
* Returns a dynamic invoker for a specified dynamic operation using the
* public lookup. Similar to
* {@link #createDynamicInvoker(String, int, Class, Class...)} but with
* already precomposed method type.
* @param name name at the call site.
* @param flags flags at the call site
* @param type the method type for the operation
* @return MethodHandle for invoking the operation.
*/
public static MethodHandle createDynamicInvoker(final String opDesc, final MethodType type) {
return bootstrap(MethodHandles.publicLookup(), opDesc, type, 0).dynamicInvoker();
public static MethodHandle createDynamicInvoker(final String name, final int flags, final MethodType type) {
return bootstrap(MethodHandles.publicLookup(), name, type, flags).dynamicInvoker();
}
/**

View File

@ -30,6 +30,9 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.NamedOperation;
import jdk.internal.dynalink.Operation;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
@ -38,7 +41,7 @@ import jdk.internal.dynalink.linker.support.Guards;
/**
* Links {@link BoundCallable} objects. Passes through to linker services for linking a callable (for either
* "dyn:call" or "dyn:new"), and modifies the returned invocation to deal with the receiver and argument binding.
* StandardOperation.CALL or .NEW, and modifies the returned invocation to deal with the receiver and argument binding.
*/
final class BoundCallableLinker implements TypeBasedGuardingDynamicLinker {
@Override
@ -54,19 +57,16 @@ final class BoundCallableLinker implements TypeBasedGuardingDynamicLinker {
}
final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
if (descriptor.getNameTokenCount() < 2 || !"dyn".equals(descriptor.getNameToken(CallSiteDescriptor.SCHEME))) {
return null;
}
final String operation = descriptor.getNameToken(CallSiteDescriptor.OPERATOR);
// We need to distinguish "dyn:new" from "dyn:call" because "dyn:call" sites have parameter list of the form
// "callee, this, args", while "dyn:call" sites have "callee, args" -- they lack the "this" parameter.
final Operation operation = NamedOperation.getBaseOperation(descriptor.getOperation());
// We need to distinguish NEW from CALL because CALL sites have parameter list of the form
// "callee, this, args", while NEW sites have "callee, args" -- they lack the "this" parameter.
final boolean isCall;
if ("new".equals(operation)) {
if (operation == StandardOperation.NEW) {
isCall = false;
} else if ("call".equals(operation)) {
} else if (operation == StandardOperation.CALL) {
isCall = true;
} else {
// Only dyn:call and dyn:new are supported.
// Only CALL and NEW are supported.
return null;
}
final BoundCallable boundCallable = (BoundCallable)objBoundCallable;
@ -93,7 +93,7 @@ final class BoundCallableLinker implements TypeBasedGuardingDynamicLinker {
System.arraycopy(args, firstArgIndex, newArgs, firstArgIndex + boundArgsLen, argsLen - firstArgIndex);
// Use R(T0, T1, T2, ...) => R(callable.class, boundThis.class, boundArg0.class, ..., boundArgn.class, T2, ...)
// call site type when delegating to underlying linker (for dyn:new, there's no this).
// call site type when delegating to underlying linker (for NEW, there's no this).
final MethodType type = descriptor.getMethodType();
// Use R(T0, ...) => R(callable.class, ...)
MethodType newMethodType = descriptor.getMethodType().changeParameterType(0, callable.getClass());

View File

@ -35,6 +35,7 @@ import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObject
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
@ -100,11 +101,6 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
final CallSiteDescriptor desc = request.getCallSiteDescriptor();
checkJSObjectClass();
if (desc.getNameTokenCount() < 2 || !"dyn".equals(desc.getNameToken(CallSiteDescriptor.SCHEME))) {
// We only support standard "dyn:*[:*]" operations
return null;
}
GuardedInvocation inv;
if (jsObjectClass.isInstance(self)) {
inv = lookup(desc, request, linkerServices);
@ -117,8 +113,6 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
}
private GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request, final LinkerServices linkerServices) throws Exception {
final String operator = desc.tokenizeOperators().get(0);
final int c = desc.getNameTokenCount();
GuardedInvocation inv;
try {
inv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
@ -126,26 +120,30 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
inv = null;
}
switch (operator) {
case "getProp":
case "getElem":
case "getMethod":
return c > 2? findGetMethod(desc, inv) : findGetIndexMethod(inv);
case "setProp":
case "setElem":
return c > 2? findSetMethod(desc, inv) : findSetIndexMethod();
case "call":
return findCallMethod(desc);
default:
return null;
final StandardOperation op = NashornCallSiteDescriptor.getFirstStandardOperation(desc);
if (op == null) {
return null;
}
final String name = NashornCallSiteDescriptor.getOperand(desc);
switch (op) {
case GET_PROPERTY:
case GET_ELEMENT:
case GET_METHOD:
return name != null ? findGetMethod(name, inv) : findGetIndexMethod(inv);
case SET_PROPERTY:
case SET_ELEMENT:
return name != null ? findSetMethod(name, inv) : findSetIndexMethod();
case CALL:
return findCallMethod(desc);
default:
}
return null;
}
private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final GuardedInvocation inv) {
private static GuardedInvocation findGetMethod(final String name, final GuardedInvocation inv) {
if (inv != null) {
return inv;
}
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final MethodHandle getter = MH.insertArguments(JSOBJECT_GETMEMBER, 1, name);
return new GuardedInvocation(getter, IS_JSOBJECT_GUARD);
}
@ -155,11 +153,11 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
return inv.replaceMethods(getter, inv.getGuard());
}
private static GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final GuardedInvocation inv) {
private static GuardedInvocation findSetMethod(final String name, final GuardedInvocation inv) {
if (inv != null) {
return inv;
}
final MethodHandle getter = MH.insertArguments(JSOBJECT_SETMEMBER, 1, desc.getNameToken(2));
final MethodHandle getter = MH.insertArguments(JSOBJECT_SETMEMBER, 1, name);
return new GuardedInvocation(getter, IS_JSOBJECT_GUARD);
}

View File

@ -83,7 +83,7 @@ public final class InvokeByName {
*/
public InvokeByName(final String name, final Class<?> targetClass, final Class<?> rtype, final Class<?>... ptypes) {
this.name = name;
getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getElem:" + name, Object.class, targetClass);
getter = Bootstrap.createDynamicInvoker(name, NashornCallSiteDescriptor.GET_METHOD_PROPERTY, Object.class, targetClass);
final Class<?>[] finalPtypes;
final int plength = ptypes.length;
@ -95,7 +95,7 @@ public final class InvokeByName {
finalPtypes[1] = targetClass;
System.arraycopy(ptypes, 0, finalPtypes, 2, plength);
}
invoker = Bootstrap.createDynamicInvoker("dyn:call", rtype, finalPtypes);
invoker = Bootstrap.createDynamicCallInvoker(rtype, finalPtypes);
}
/**

View File

@ -32,6 +32,7 @@ import java.lang.invoke.MethodHandles;
import java.util.Map;
import javax.script.Bindings;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
@ -70,11 +71,6 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker {
final Object self = request.getReceiver();
final CallSiteDescriptor desc = request.getCallSiteDescriptor();
if (desc.getNameTokenCount() < 2 || !"dyn".equals(desc.getNameToken(CallSiteDescriptor.SCHEME))) {
// We only support standard "dyn:*[:*]" operations
return null;
}
GuardedInvocation inv;
if (self instanceof JSObject) {
inv = lookup(desc, request, linkerServices);
@ -92,33 +88,34 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker {
}
private GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request, final LinkerServices linkerServices) throws Exception {
final String operator = desc.tokenizeOperators().get(0);
final int c = desc.getNameTokenCount();
switch (operator) {
case "getProp":
case "getElem":
case "getMethod":
if (c > 2) {
return findGetMethod(desc);
}
final StandardOperation op = NashornCallSiteDescriptor.getFirstStandardOperation(desc);
if (op == null) {
return null;
}
final String name = NashornCallSiteDescriptor.getOperand(desc);
switch (op) {
case GET_PROPERTY:
case GET_ELEMENT:
case GET_METHOD:
if (name != null) {
return findGetMethod(name);
}
// For indexed get, we want get GuardedInvocation beans linker and pass it.
// JSObjectLinker.get uses this fallback getter for explicit signature method access.
return findGetIndexMethod(nashornBeansLinker.getGuardedInvocation(request, linkerServices));
case "setProp":
case "setElem":
return c > 2 ? findSetMethod(desc) : findSetIndexMethod();
case "call":
return findCallMethod(desc);
case "new":
return findNewMethod(desc);
default:
return null;
case SET_PROPERTY:
case SET_ELEMENT:
return name != null ? findSetMethod(name) : findSetIndexMethod();
case CALL:
return findCallMethod(desc);
case NEW:
return findNewMethod(desc);
default:
}
return null;
}
private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
private static GuardedInvocation findGetMethod(final String name) {
final MethodHandle getter = MH.insertArguments(JSOBJECT_GETMEMBER, 1, name);
return new GuardedInvocation(getter, IS_JSOBJECT_GUARD);
}
@ -128,8 +125,8 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker {
return inv.replaceMethods(getter, inv.getGuard());
}
private static GuardedInvocation findSetMethod(final CallSiteDescriptor desc) {
final MethodHandle getter = MH.insertArguments(JSOBJECT_SETMEMBER, 1, desc.getNameToken(2));
private static GuardedInvocation findSetMethod(final String name) {
final MethodHandle getter = MH.insertArguments(JSOBJECT_SETMEMBER, 1, name);
return new GuardedInvocation(getter, IS_JSOBJECT_GUARD);
}

View File

@ -46,6 +46,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.internal.dynalink.linker.support.SimpleLinkRequest;
import jdk.nashorn.internal.runtime.Context;
@ -189,8 +191,8 @@ public final class JavaAdapterFactory {
public static MethodHandle getConstructor(final Class<?> sourceType, final Class<?> targetType, final MethodHandles.Lookup lookup) throws Exception {
final StaticClass adapterClass = getAdapterClassFor(new Class<?>[] { targetType }, null, lookup);
return MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new SimpleLinkRequest(
NashornCallSiteDescriptor.get(lookup, "dyn:new",
MethodType.methodType(targetType, StaticClass.class, sourceType), 0), false,
new CallSiteDescriptor(lookup, StandardOperation.NEW,
MethodType.methodType(targetType, StaticClass.class, sourceType)), false,
adapterClass, null)).getInvocation(), adapterClass);
}

View File

@ -32,6 +32,9 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.NamedOperation;
import jdk.internal.dynalink.Operation;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
@ -46,10 +49,6 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
*
*/
final class JavaSuperAdapterLinker implements TypeBasedGuardingDynamicLinker {
private static final String GET_METHOD = "getMethod";
private static final String DYN_GET_METHOD = "dyn:" + GET_METHOD;
private static final String DYN_GET_METHOD_FIXED = DYN_GET_METHOD + ":" + SUPER_PREFIX;
private static final MethodHandle ADD_PREFIX_TO_METHOD_NAME;
private static final MethodHandle BIND_DYNAMIC_METHOD;
private static final MethodHandle GET_ADAPTER;
@ -77,8 +76,9 @@ final class JavaSuperAdapterLinker implements TypeBasedGuardingDynamicLinker {
}
final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
if(!descriptor.tokenizeOperators().contains(GET_METHOD)) {
// We only handle getMethod
if(NashornCallSiteDescriptor.getFirstStandardOperation(descriptor) != StandardOperation.GET_METHOD) {
// We only handle GET_METHOD
return null;
}
@ -91,13 +91,13 @@ final class JavaSuperAdapterLinker implements TypeBasedGuardingDynamicLinker {
// Use R(T0, ...) => R(adapter.class, ...) call site type when delegating to BeansLinker.
final MethodType type = descriptor.getMethodType();
final Class<?> adapterClass = adapter.getClass();
final boolean hasFixedName = descriptor.getNameTokenCount() > 2;
final String opName = hasFixedName ? (DYN_GET_METHOD_FIXED + descriptor.getNameToken(
CallSiteDescriptor.NAME_OPERAND)) : DYN_GET_METHOD;
final String name = NashornCallSiteDescriptor.getOperand(descriptor);
final Operation newOp = name == null ? StandardOperation.GET_METHOD :
new NamedOperation(StandardOperation.GET_METHOD, SUPER_PREFIX + name);
final CallSiteDescriptor newDescriptor = NashornCallSiteDescriptor.get(
NashornCallSiteDescriptor.getLookupInternal(descriptor), opName,
type.changeParameterType(0, adapterClass), 0);
final CallSiteDescriptor newDescriptor = new CallSiteDescriptor(
NashornCallSiteDescriptor.getLookupInternal(descriptor), newOp,
type.changeParameterType(0, adapterClass));
// Delegate to BeansLinker
final GuardedInvocation guardedInv = NashornBeansLinker.getGuardedInvocation(
@ -122,16 +122,16 @@ final class JavaSuperAdapterLinker implements TypeBasedGuardingDynamicLinker {
final MethodHandle droppingBinder = MethodHandles.dropArguments(typedBinder, 2,
invType.parameterList().subList(1, invType.parameterCount()));
// Finally, fold the invocation into the binder to produce a method handle that will bind every returned
// DynamicMethod object from dyn:getMethod calls to the actual receiver
// DynamicMethod object from StandardOperation.GET_METHOD calls to the actual receiver
// Object(R(T0, T1, ...), T0, T1, ...)
final MethodHandle bindingInvocation = MethodHandles.foldArguments(droppingBinder, invocation);
final MethodHandle typedGetAdapter = asFilterType(GET_ADAPTER, 0, invType, type);
final MethodHandle adaptedInvocation;
if(hasFixedName) {
if(name != null) {
adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter);
} else {
// Add a filter that'll prepend "super$" to each name passed to the variable-name "dyn:getMethod".
// Add a filter that'll prepend "super$" to each name passed to the variable-name StandardOperation.GET_METHOD.
final MethodHandle typedAddPrefix = asFilterType(ADD_PREFIX_TO_METHOD_NAME, 1, invType, type);
adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter, typedAddPrefix);
}

View File

@ -124,7 +124,7 @@ public class LinkerCallSite extends ChainedCallSite {
}
private MethodHandle getIncreaseMissCounter(final Class<?> type) {
final MethodHandle missCounterWithDesc = MH.bindTo(INCREASE_MISS_COUNTER, getDescriptor().getName() + " @ " + getScriptLocation());
final MethodHandle missCounterWithDesc = MH.bindTo(INCREASE_MISS_COUNTER, getDescriptor().getOperation() + " @ " + getScriptLocation());
if (type == Object.class) {
return missCounterWithDesc;
}
@ -291,7 +291,7 @@ public class LinkerCallSite extends ChainedCallSite {
int index = 0;
for (final ProfilingLinkerCallSite callSite : profileCallSites) {
out.println("" + (index++) + '\t' +
callSite.getDescriptor().getName() + '\t' +
callSite.getDescriptor().getOperation() + '\t' +
callSite.totalTime + '\t' +
callSite.hitCount);
}
@ -402,7 +402,7 @@ public class LinkerCallSite extends ChainedCallSite {
private void tracePrint(final PrintWriter out, final String tag, final Object[] args, final Object result) {
//boolean isVoid = type().returnType() == void.class;
out.print(Debug.id(this) + " TAG " + tag);
out.print(getDescriptor().getName() + "(");
out.print(getDescriptor().getOperation() + "(");
if (args.length > 0) {
printObject(out, args[0]);

View File

@ -33,6 +33,8 @@ import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.NamedOperation;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.linker.ConversionComparator.Comparison;
import jdk.internal.dynalink.linker.GuardedInvocation;
@ -99,19 +101,19 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
return invocation == null ? null : invocation.filterArguments(0, FILTER_CONSSTRING);
}
if (self != null && "call".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) {
// Support dyn:call on any object that supports some @FunctionalInterface
if (self != null && NamedOperation.getBaseOperation(desc.getOperation()) == StandardOperation.CALL) {
// Support CALL on any object that supports some @FunctionalInterface
// annotated interface. This way Java method, constructor references or
// implementations of java.util.function.* interfaces can be called as though
// those are script functions.
final String name = getFunctionalInterfaceMethodName(self.getClass());
if (name != null) {
final MethodType callType = desc.getMethodType();
// drop callee (Undefined ScriptFunction) and change the request to be dyn:callMethod:<name>
final NashornCallSiteDescriptor newDesc = NashornCallSiteDescriptor.get(
NashornCallSiteDescriptor.getLookupInternal(desc), "dyn:callMethod:" + name,
desc.getMethodType().dropParameterTypes(1, 2),
NashornCallSiteDescriptor.getFlags(desc));
// drop callee (Undefined ScriptFunction) and change the request to be CALL_METHOD:<name>
final CallSiteDescriptor newDesc = new CallSiteDescriptor(
NashornCallSiteDescriptor.getLookupInternal(desc),
new NamedOperation(StandardOperation.CALL_METHOD, name),
desc.getMethodType().dropParameterTypes(1, 2));
final GuardedInvocation gi = getGuardedInvocation(beansLinker,
linkRequest.replaceArguments(newDesc, linkRequest.getArguments()),
new NashornBeansLinkerServices(linkerServices));

View File

@ -37,6 +37,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.NamedOperation;
import jdk.internal.dynalink.Operation;
import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.GuardingDynamicLinker;
@ -86,9 +88,8 @@ final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeCo
private static GuardedInvocation linkBean(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
final NashornCallSiteDescriptor desc = (NashornCallSiteDescriptor)linkRequest.getCallSiteDescriptor();
final Object self = linkRequest.getReceiver();
final String operator = desc.getFirstOperator();
switch (operator) {
case "new":
switch (desc.getFirstOperation()) {
case NEW:
if(BeansLinker.isDynamicConstructor(self)) {
throw typeError("no.constructor.matches.args", ScriptRuntime.safeToString(self));
}
@ -96,7 +97,7 @@ final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeCo
throw typeError("method.not.constructor", ScriptRuntime.safeToString(self));
}
throw typeError("not.a.function", desc.getFunctionErrorMessage(self));
case "call":
case CALL:
if(BeansLinker.isDynamicConstructor(self)) {
throw typeError("constructor.requires.new", ScriptRuntime.safeToString(self));
}
@ -104,13 +105,13 @@ final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeCo
throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self));
}
throw typeError("not.a.function", desc.getFunctionErrorMessage(self));
case "callMethod":
case CALL_METHOD:
throw typeError("no.such.function", getArgument(linkRequest), ScriptRuntime.safeToString(self));
case "getMethod":
case GET_METHOD:
// evaluate to undefined, later on Undefined will take care of throwing TypeError
return getInvocation(MH.dropArguments(GET_UNDEFINED.get(TYPE_OBJECT_INDEX), 0, Object.class), self, linkerServices, desc);
case "getProp":
case "getElem":
case GET_PROPERTY:
case GET_ELEMENT:
if(NashornCallSiteDescriptor.isOptimistic(desc)) {
throw new UnwarrantedOptimismException(UNDEFINED, NashornCallSiteDescriptor.getProgramPoint(desc), Type.OBJECT);
}
@ -118,8 +119,8 @@ final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeCo
return getInvocation(EMPTY_PROP_GETTER, self, linkerServices, desc);
}
return getInvocation(EMPTY_ELEM_GETTER, self, linkerServices, desc);
case "setProp":
case "setElem": {
case SET_PROPERTY:
case SET_ELEMENT:
final boolean strict = NashornCallSiteDescriptor.isStrict(desc);
if (strict) {
throw typeError("cant.set.property", getArgument(linkRequest), ScriptRuntime.safeToString(self));
@ -128,11 +129,9 @@ final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeCo
return getInvocation(EMPTY_PROP_SETTER, self, linkerServices, desc);
}
return getInvocation(EMPTY_ELEM_SETTER, self, linkerServices, desc);
}
default:
break;
throw new AssertionError("unknown call type " + desc);
}
throw new AssertionError("unknown call type " + desc);
}
@Override
@ -170,24 +169,22 @@ final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeCo
private static GuardedInvocation linkNull(final LinkRequest linkRequest) {
final NashornCallSiteDescriptor desc = (NashornCallSiteDescriptor)linkRequest.getCallSiteDescriptor();
final String operator = desc.getFirstOperator();
switch (operator) {
case "new":
case "call":
switch (desc.getFirstOperation()) {
case NEW:
case CALL:
throw typeError("not.a.function", "null");
case "callMethod":
case "getMethod":
case CALL_METHOD:
case GET_METHOD:
throw typeError("no.such.function", getArgument(linkRequest), "null");
case "getProp":
case "getElem":
case GET_PROPERTY:
case GET_ELEMENT:
throw typeError("cant.get.property", getArgument(linkRequest), "null");
case "setProp":
case "setElem":
case SET_PROPERTY:
case SET_ELEMENT:
throw typeError("cant.set.property", getArgument(linkRequest), "null");
default:
break;
throw new AssertionError("unknown call type " + desc);
}
throw new AssertionError("unknown call type " + desc);
}
private static final Map<Class<?>, MethodHandle> CONVERTERS = new HashMap<>();
@ -200,9 +197,9 @@ final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeCo
}
private static String getArgument(final LinkRequest linkRequest) {
final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor();
if (desc.getNameTokenCount() > 2) {
return desc.getNameToken(2);
final Operation op = linkRequest.getCallSiteDescriptor().getOperation();
if (op instanceof NamedOperation) {
return ((NamedOperation)op).getName().toString();
}
return ScriptRuntime.safeToString(linkRequest.getArguments()[1]);
}

View File

@ -34,48 +34,86 @@ import java.security.PrivilegedAction;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
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.support.NameCodec;
import jdk.nashorn.internal.ir.debug.NashornTextifier;
import jdk.nashorn.internal.runtime.AccessControlContextFactory;
import jdk.nashorn.internal.runtime.ScriptRuntime;
/**
* Nashorn-specific implementation of Dynalink's {@link CallSiteDescriptor}. The reason we have our own subclass is that
* we can have a more compact representation, as we know that we're always only using {@code "dyn:*"} operations; also
* we're storing flags in an additional primitive field.
* Nashorn-specific implementation of Dynalink's {@link CallSiteDescriptor}.
* The reason we have our own subclass is that we're storing flags in an
* additional primitive field. The class also exposes some useful utilities in
* form of static methods.
*/
public final class NashornCallSiteDescriptor extends CallSiteDescriptor {
// Lowest three bits describe the operation
/** Property getter operation {@code obj.prop} */
public static final int GET_PROPERTY = 0;
/** Element getter operation {@code obj[index]} */
public static final int GET_ELEMENT = 1;
/** Property getter operation, subsequently invoked {@code obj.prop()} */
public static final int GET_METHOD_PROPERTY = 2;
/** Element getter operation, subsequently invoked {@code obj[index]()} */
public static final int GET_METHOD_ELEMENT = 3;
/** Property setter operation {@code obj.prop = value} */
public static final int SET_PROPERTY = 4;
/** Element setter operation {@code obj[index] = value} */
public static final int SET_ELEMENT = 5;
/** Call operation {@code fn(args...)} */
public static final int CALL = 6;
/** New operation {@code new Constructor(args...)} */
public static final int NEW = 7;
private static final int OPERATION_MASK = 7;
// Correspond to the operation indices above.
private static final Operation[] OPERATIONS = new Operation[] {
new CompositeOperation(StandardOperation.GET_PROPERTY, StandardOperation.GET_ELEMENT, StandardOperation.GET_METHOD),
new CompositeOperation(StandardOperation.GET_ELEMENT, StandardOperation.GET_PROPERTY, StandardOperation.GET_METHOD),
new CompositeOperation(StandardOperation.GET_METHOD, StandardOperation.GET_PROPERTY, StandardOperation.GET_ELEMENT),
new CompositeOperation(StandardOperation.GET_METHOD, StandardOperation.GET_ELEMENT, StandardOperation.GET_PROPERTY),
new CompositeOperation(StandardOperation.SET_PROPERTY, StandardOperation.SET_ELEMENT),
new CompositeOperation(StandardOperation.SET_ELEMENT, StandardOperation.SET_PROPERTY),
StandardOperation.CALL,
StandardOperation.NEW
};
/** Flags that the call site references a scope variable (it's an identifier reference or a var declaration, not a
* property access expression. */
public static final int CALLSITE_SCOPE = 1 << 0;
public static final int CALLSITE_SCOPE = 1 << 3;
/** Flags that the call site is in code that uses ECMAScript strict mode. */
public static final int CALLSITE_STRICT = 1 << 1;
public static final int CALLSITE_STRICT = 1 << 4;
/** Flags that a property getter or setter call site references a scope variable that is located at a known distance
* in the scope chain. Such getters and setters can often be linked more optimally using these assumptions. */
public static final int CALLSITE_FAST_SCOPE = 1 << 2;
public static final int CALLSITE_FAST_SCOPE = 1 << 5;
/** Flags that a callsite type is optimistic, i.e. we might get back a wider return value than encoded in the
* descriptor, and in that case we have to throw an UnwarrantedOptimismException */
public static final int CALLSITE_OPTIMISTIC = 1 << 3;
public static final int CALLSITE_OPTIMISTIC = 1 << 6;
/** Is this really an apply that we try to call as a call? */
public static final int CALLSITE_APPLY_TO_CALL = 1 << 4;
public static final int CALLSITE_APPLY_TO_CALL = 1 << 7;
/** Does this a callsite for a variable declaration? */
public static final int CALLSITE_DECLARE = 1 << 5;
public static final int CALLSITE_DECLARE = 1 << 8;
/** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit
* code where call sites have this flag set. */
public static final int CALLSITE_PROFILE = 1 << 6;
public static final int CALLSITE_PROFILE = 1 << 9;
/** Flags that the call site is traced; Contexts that have {@code "trace.callsites"} property set emit code where
* call sites have this flag set. */
public static final int CALLSITE_TRACE = 1 << 7;
public static final int CALLSITE_TRACE = 1 << 10;
/** Flags that the call site linkage miss (and thus, relinking) is traced; Contexts that have the keyword
* {@code "miss"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
public static final int CALLSITE_TRACE_MISSES = 1 << 8;
public static final int CALLSITE_TRACE_MISSES = 1 << 11;
/** Flags that entry/exit to/from the method linked at call site are traced; Contexts that have the keyword
* {@code "enterexit"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 9;
public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 12;
/** Flags that values passed as arguments to and returned from the method linked at call site are traced; Contexts
* that have the keyword {@code "values"} in their {@code "trace.callsites"} property emit code where call sites
* have this flag set. */
public static final int CALLSITE_TRACE_VALUES = 1 << 10;
public static final int CALLSITE_TRACE_VALUES = 1 << 13;
//we could have more tracing flags here, for example CALLSITE_TRACE_SCOPE, but bits are a bit precious
//right now given the program points
@ -87,10 +125,20 @@ public final class NashornCallSiteDescriptor extends CallSiteDescriptor {
* TODO: rethink if we need the various profile/trace flags or the linker can use the Context instead to query its
* trace/profile settings.
*/
public static final int CALLSITE_PROGRAM_POINT_SHIFT = 11;
public static final int CALLSITE_PROGRAM_POINT_SHIFT = 14;
/**
* Maximum program point value. 21 bits should be enough for anyone
* Maximum program point value. We have 18 bits left over after flags, and
* it should be plenty. Program points are local to a single function. Every
* function maps to a single JVM bytecode method that can have at most 65535
* bytes. (Large functions are synthetically split into smaller functions.)
* A single invokedynamic is 5 bytes; even if a method consists of only
* invokedynamic instructions that leaves us with at most 65535/5 = 13107
* program points for the largest single method; those can be expressed on
* 14 bits. It is true that numbering of program points is independent of
* bytecode representation, but if a function would need more than ~14 bits
* for the program points, then it is reasonable to presume splitter
* would've split it into several smaller functions already.
*/
public static final int MAX_PROGRAM_POINT_VALUE = (1 << 32 - CALLSITE_PROGRAM_POINT_SHIFT) - 1;
@ -110,8 +158,6 @@ public final class NashornCallSiteDescriptor extends CallSiteDescriptor {
private static final AccessControlContext GET_LOOKUP_PERMISSION_CONTEXT =
AccessControlContextFactory.createAccessControlContext(CallSiteDescriptor.GET_LOOKUP_PERMISSION_NAME);
private final String operator;
private final String operand;
private final int flags;
/**
@ -142,61 +188,57 @@ public final class NashornCallSiteDescriptor extends CallSiteDescriptor {
return sb.length() == 0 ? "" : " " + sb.toString().trim();
}
/**
* Given call site flags, returns the operation name encoded in them.
* @param flags flags
* @return the operation name
*/
public static String getOperationName(final int flags) {
switch(flags & OPERATION_MASK) {
case 0: return "GET_PROPERTY";
case 1: return "GET_ELEMENT";
case 2: return "GET_METHOD_PROPERTY";
case 3: return "GET_METHOD_ELEMENT";
case 4: return "SET_PROPERTY";
case 5: return "SET_ELEMENT";
case 6: return "CALL";
case 7: return "NEW";
default: throw new AssertionError();
}
}
/**
* Retrieves a Nashorn call site descriptor with the specified values. Since call site descriptors are immutable
* this method is at liberty to retrieve canonicalized instances (although it is not guaranteed it will do so).
* @param lookup the lookup describing the script
* @param name the name at the call site, e.g. {@code "dyn:getProp|getElem|getMethod:color"}.
* @param name the name at the call site. Can not be null, but it can be empty.
* @param methodType the method type at the call site
* @param flags Nashorn-specific call site flags
* @return a call site descriptor with the specified values.
*/
public static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String name,
final MethodType methodType, final int flags) {
final String[] tokenizedName = CallSiteDescriptor.tokenizeName(name);
assert tokenizedName.length >= 2;
assert "dyn".equals(tokenizedName[0]);
assert tokenizedName[1] != null;
// TODO: see if we can move mangling/unmangling into Dynalink
return get(lookup, name, tokenizedName[1], tokenizedName.length == 3 ? tokenizedName[2].intern() : null,
methodType, flags);
final Operation baseOp = OPERATIONS[flags & OPERATION_MASK];
final String decodedName = NameCodec.decode(name);
// TODO: introduce caching of NamedOperation
final Operation op = decodedName.isEmpty() ? baseOp : new NamedOperation(baseOp, decodedName);
return get(lookup, op, methodType, flags);
}
private static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String name, final String operator, final String operand, final MethodType methodType, final int flags) {
final NashornCallSiteDescriptor csd = new NashornCallSiteDescriptor(lookup, name, operator, operand, methodType, flags);
private static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final Operation operation, final MethodType methodType, final int flags) {
final NashornCallSiteDescriptor csd = new NashornCallSiteDescriptor(lookup, operation, methodType, flags);
// Many of these call site descriptors are identical (e.g. every getter for a property color will be
// "dyn:getProp:color(Object)Object", so it makes sense canonicalizing them.
// "GET_PROPERTY:color(Object)Object", so it makes sense canonicalizing them.
final ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> classCanonicals = canonicals.get(lookup.lookupClass());
final NashornCallSiteDescriptor canonical = classCanonicals.putIfAbsent(csd, csd);
return canonical != null ? canonical : csd;
}
private NashornCallSiteDescriptor(final MethodHandles.Lookup lookup, final String name, final String operator, final String operand,
final MethodType methodType, final int flags) {
super(lookup, name, methodType);
this.operator = operator;
this.operand = operand;
private NashornCallSiteDescriptor(final MethodHandles.Lookup lookup, final Operation operation, final MethodType methodType, final int flags) {
super(lookup, operation, methodType);
this.flags = flags;
}
@Override
public int getNameTokenCount() {
return operand == null ? 2 : 3;
}
@Override
public String getNameToken(final int i) {
switch(i) {
case 0: return "dyn";
case 1: return operator;
case 2:
if(operand != null) {
return operand;
}
}
throw new IndexOutOfBoundsException(String.valueOf(i));
}
static Lookup getLookupInternal(final CallSiteDescriptor csd) {
if (csd instanceof NashornCallSiteDescriptor) {
return ((NashornCallSiteDescriptor)csd).getLookupPrivileged();
@ -215,80 +257,99 @@ public final class NashornCallSiteDescriptor extends CallSiteDescriptor {
}
/**
* Returns the operator (e.g. {@code "getProp"}) in this call site descriptor's name. Equivalent to
* {@code getNameToken(CallSiteDescriptor.OPERATOR)}. The returned operator can be composite.
* @return the operator in this call site descriptor's name.
*/
public String getOperator() {
return operator;
}
/**
* Returns the first operator in this call site descriptor's name. E.g. if this call site descriptor has a composite
* operation {@code "getProp|getMethod|getElem"}, it will return {@code "getProp"}. Nashorn - being a ECMAScript
* engine - does not distinguish between property, element, and method namespace; ECMAScript objects just have one
* single property namespace for all these, therefore it is largely irrelevant what the composite operation is
* structured like; if the first operation can't be satisfied, neither can the others. The first operation is
* however sometimes used to slightly alter the semantics; for example, a distinction between {@code "getProp"} and
* {@code "getMethod"} being the first operation can translate into whether {@code "__noSuchProperty__"} or
* {@code "__noSuchMethod__"} will be executed in case the property is not found.
* @return the first operator in this call site descriptor's name.
*/
public String getFirstOperator() {
final int delim = operator.indexOf(CallSiteDescriptor.OPERATOR_DELIMITER);
return delim == -1 ? operator : operator.substring(0, delim);
}
/**
* Returns the named operand in this descriptor's name. Equivalent to
* {@code getNameToken(CallSiteDescriptor.NAME_OPERAND)}. E.g. for operation {@code "dyn:getProp:color"}, returns
* {@code "color"}. For call sites without named operands (e.g. {@code "dyn:new"}) returns null.
* @return the named operand in this descriptor's name.
* Returns the named operand in this descriptor's operation. Equivalent to
* {@code ((NamedOperation)getOperation()).getName().toString()} for call
* sites with a named operand. For call sites without named operands returns null.
* @return the named operand in this descriptor's operation.
*/
public String getOperand() {
return operand;
return getOperand(this);
}
/**
* If this is a dyn:call or dyn:new, this returns function description from callsite.
* Caller has to make sure this is a dyn:call or dyn:new call site.
*
* @return function description if available (or null)
* Returns the named operand in the passed descriptor's operation.
* Equivalent to
* {@code ((NamedOperation)desc.getOperation()).getName().toString()} for
* descriptors with a named operand. For descriptors without named operands
* returns null.
* @param desc the call site descriptors
* @return the named operand in this descriptor's operation.
*/
public String getFunctionDescription() {
assert getFirstOperator().equals("call") || getFirstOperator().equals("new");
return getNameTokenCount() > 2? getNameToken(2) : null;
public static String getOperand(final CallSiteDescriptor desc) {
final Operation operation = desc.getOperation();
return operation instanceof NamedOperation ? ((NamedOperation)operation).getName().toString() : null;
}
/**
* If this is a dyn:call or dyn:new, this returns function description from callsite.
* Caller has to make sure this is a dyn:call or dyn:new call site.
*
* @param desc call site descriptor
* @return function description if available (or null)
* Returns the first operation in this call site descriptor's potentially
* composite operation. E.g. if this call site descriptor has a composite
* operation {@code GET_PROPERTY|GET_METHOD|GET_ELEM}, it will return
* {@code GET_PROPERTY}. Nashorn - being a ECMAScript engine - does not
* distinguish between property, element, and method namespace; ECMAScript
* objects just have one single property namespace for all these, therefore
* it is largely irrelevant what the composite operation is structured like;
* if the first operation can't be satisfied, neither can the others. The
* first operation is however sometimes used to slightly alter the
* semantics; for example, a distinction between {@code GET_PROPERTY} and
* {@code GET_METHOD} being the first operation can translate into whether
* {@code "__noSuchProperty__"} or {@code "__noSuchMethod__"} will be
* executed in case the property is not found. Note that if a call site
* descriptor comes from outside of Nashorn, its class will be different,
* and there is no guarantee about the way it composes its operations. For
* that reason, for potentially foreign call site descriptors you should use
* {@link #getFirstStandardOperation(CallSiteDescriptor)} instead.
* @return the first operation in this call site descriptor. Note this will
* always be a {@code StandardOperation} as Nashorn internally only uses
* standard operations.
*/
public static String getFunctionDescription(final CallSiteDescriptor desc) {
return desc instanceof NashornCallSiteDescriptor ?
((NashornCallSiteDescriptor)desc).getFunctionDescription() : null;
public StandardOperation getFirstOperation() {
final Operation base = NamedOperation.getBaseOperation(getOperation());
if (base instanceof CompositeOperation) {
return (StandardOperation)((CompositeOperation)base).getOperation(0);
}
return (StandardOperation)base;
}
/**
* Returns the first standard operation in the (potentially composite)
* operation of the passed call site descriptor.
* @param desc the call site descriptor.
* @return Returns the first standard operation in the (potentially
* composite) operation of the passed call site descriptor. Can return null
* if the call site contains no standard operations.
*/
public static StandardOperation getFirstStandardOperation(final CallSiteDescriptor desc) {
final Operation base = NamedOperation.getBaseOperation(desc.getOperation());
if (base instanceof StandardOperation) {
return (StandardOperation)base;
} else if (base instanceof CompositeOperation) {
final CompositeOperation cop = (CompositeOperation)base;
for(int i = 0; i < cop.getOperationCount(); ++i) {
final Operation op = cop.getOperation(i);
if (op instanceof StandardOperation) {
return (StandardOperation)op;
}
}
}
return null;
}
/**
* Returns the error message to be used when dyn:call or dyn:new is used on a non-function.
* Returns the error message to be used when CALL or NEW is used on a non-function.
*
* @param obj object on which dyn:call or dyn:new is used
* @param obj object on which CALL or NEW is used
* @return error message
*/
public String getFunctionErrorMessage(final Object obj) {
final String funcDesc = getFunctionDescription();
final String funcDesc = getOperand();
return funcDesc != null? funcDesc : ScriptRuntime.safeToString(obj);
}
/**
* Returns the error message to be used when dyn:call or dyn:new is used on a non-function.
* Returns the error message to be used when CALL or NEW is used on a non-function.
*
* @param desc call site descriptor
* @param obj object on which dyn:call or dyn:new is used
* @param obj object on which CALL or NEW is used
* @return error message
*/
public static String getFunctionErrorMessage(final CallSiteDescriptor desc, final Object obj) {
@ -446,6 +507,6 @@ public final class NashornCallSiteDescriptor extends CallSiteDescriptor {
@Override
public CallSiteDescriptor changeMethodTypeInternal(final MethodType newMethodType) {
return get(getLookupPrivileged(), getName(), operator, operand, newMethodType, flags);
return get(getLookupPrivileged(), getOperation(), newMethodType, flags);
}
}

View File

@ -90,18 +90,13 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp
@Override
public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices) throws Exception {
final Object self = request.getReceiver();
final CallSiteDescriptor desc = request.getCallSiteDescriptor();
if (desc.getNameTokenCount() < 2 || !"dyn".equals(desc.getNameToken(CallSiteDescriptor.SCHEME))) {
// We only support standard "dyn:*[:*]" operations
return null;
}
return Bootstrap.asTypeSafeReturn(getGuardedInvocation(self, request, desc), linkerServices, desc);
return Bootstrap.asTypeSafeReturn(getGuardedInvocation(request, desc), linkerServices, desc);
}
private static GuardedInvocation getGuardedInvocation(final Object self, final LinkRequest request, final CallSiteDescriptor desc) {
private static GuardedInvocation getGuardedInvocation(final LinkRequest request, final CallSiteDescriptor desc) {
final Object self = request.getReceiver();
final GuardedInvocation inv;
if (self instanceof ScriptObject) {
inv = ((ScriptObject)self).lookup(desc, request);

View File

@ -27,6 +27,8 @@ package jdk.nashorn.internal.runtime.linker;
import java.lang.reflect.Modifier;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.NamedOperation;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.internal.dynalink.linker.GuardedInvocation;
@ -69,7 +71,7 @@ final class NashornStaticClassLinker implements TypeBasedGuardingDynamicLinker {
Bootstrap.checkReflectionAccess(receiverClass, true);
final CallSiteDescriptor desc = request.getCallSiteDescriptor();
// We intercept "new" on StaticClass instances to provide additional capabilities
if ("new".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) {
if (NamedOperation.getBaseOperation(desc.getOperation()) == StandardOperation.NEW) {
if (! Modifier.isPublic(receiverClass.getModifiers())) {
throw ECMAErrors.typeError("new.on.nonpublic.javatype", receiverClass.getName());
}

View File

@ -32,7 +32,6 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.support.Guards;
@ -96,24 +95,17 @@ public final class PrimitiveLookup {
public static GuardedInvocation lookupPrimitive(final LinkRequest request, final MethodHandle guard,
final ScriptObject wrappedReceiver, final MethodHandle wrapFilter,
final MethodHandle protoFilter) {
final CallSiteDescriptor desc = request.getCallSiteDescriptor();
final String name;
final FindProperty find;
// lookupPrimitive is only ever invoked from NashornPrimitiveLinker,
// which is a linker private to Nashorn, therefore the call site
// descriptor class will always be NashornCallSiteDescriptor.
final NashornCallSiteDescriptor desc = (NashornCallSiteDescriptor)request.getCallSiteDescriptor();
final String name = desc.getOperand();
final FindProperty find = name != null ? wrappedReceiver.findProperty(name, true) : null;
if (desc.getNameTokenCount() > 2) {
name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
find = wrappedReceiver.findProperty(name, true);
} else {
name = null;
find = null;
}
final String firstOp = desc.tokenizeOperators().get(0);
switch (firstOp) {
case "getProp":
case "getElem":
case "getMethod":
switch (desc.getFirstOperation()) {
case GET_PROPERTY:
case GET_ELEMENT:
case GET_METHOD:
//checks whether the property name is hard-coded in the call-site (i.e. a getProp vs a getElem, or setProp vs setElem)
//if it is we can make assumptions on the property: that if it is not defined on primitive wrapper itself it never will be.
//so in that case we can skip creation of primitive wrapper and start our search with the prototype.
@ -144,8 +136,8 @@ public final class PrimitiveLookup {
}
}
break;
case "setProp":
case "setElem":
case SET_PROPERTY:
case SET_ELEMENT:
return getPrimitiveSetter(name, guard, wrapFilter, NashornCallSiteDescriptor.isStrict(desc));
default:
break;

View File

@ -30,6 +30,7 @@ import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.StandardOperation;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
@ -128,14 +129,10 @@ final class ReflectionCheckLinker implements TypeBasedGuardingDynamicLinker{
// allow 'static' access on Class objects representing public classes of non-restricted packages
if ((self instanceof Class) && Modifier.isPublic(((Class<?>)self).getModifiers())) {
final CallSiteDescriptor desc = request.getCallSiteDescriptor();
if(desc.tokenizeOperators().contains("getProp")) {
if (desc.getNameTokenCount() > CallSiteDescriptor.NAME_OPERAND &&
"static".equals(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND))) {
if (Context.isAccessibleClass((Class<?>)self) && !isReflectionClass((Class<?>)self)) {
// If "getProp:static" passes access checks, allow access.
return;
}
if ("static".equals(NashornCallSiteDescriptor.getOperand(desc)) && NashornCallSiteDescriptor.getFirstStandardOperation(desc) == StandardOperation.GET_PROPERTY) {
if (Context.isAccessibleClass((Class<?>)self) && !isReflectionClass((Class<?>)self)) {
// If "GET_PROPERTY:static" passes access checks, allow access.
return;
}
}
}