8139931: Introduce Operation objects in Dynalink instead of string encoding
Reviewed-by: hannesw, sundar
This commit is contained in:
parent
df445c6578
commit
fe13034c1d
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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 };
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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, propertyName)→value</tt> or
|
||||
* <tt>(receiver)→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, propertyName, value)→void</tt> or
|
||||
* <tt>(receiver, value)→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, index)→value</tt> or
|
||||
* <tt>(receiver)→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, index, value)→void</tt> or
|
||||
* <tt>(receiver, value)→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)→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, methodName)→value</tt>, or
|
||||
* <tt>(receiver)→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, methodName, arguments...)→value</tt> or
|
||||
* <tt>(receiver, arguments...)→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, arguments...)→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, arguments...)→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
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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()));
|
||||
|
@ -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>
|
||||
* <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, 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, 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, any, 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, 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, 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, any, 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, 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[, any, 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[, any, 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[, any, 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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
/**
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
@ -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) :
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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]);
|
||||
|
@ -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));
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user