1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
/*
* Copyright (c) 2010, 2013, 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.
*/
package jdk.nashorn.internal.runtime.linker;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Undefined;
/**
* Provides static utility services to generated Java adapter classes.
*/
public class JavaAdapterServices {
private static final ThreadLocal<ScriptObject> classOverrides = new ThreadLocal<>();
private JavaAdapterServices() {
}
/**
* Given a JS script function, binds it to null JS "this", and adapts its parameter types, return types, and arity
* to the specified type and arity. This method is public mainly for implementation reasons, so the adapter classes
* can invoke it from their constructors that take a ScriptFunction in its first argument to obtain the method
* handles for their abstract method implementations.
* @param fn the script function
* @param type the method type it has to conform to
* @param varArg if the Java method for which the function is being adapted is a variable arity method
* @return the appropriately adapted method handle for invoking the script function.
*/
public static MethodHandle getHandle(final ScriptFunction fn, final MethodType type, final boolean varArg) {
// JS "this" will be null for SAMs
return adaptHandle(fn.getBoundInvokeHandle(null), type, varArg);
}
/**
* Given a JS script object, retrieves a function from it by name, binds it to the script object as its "this", and
* adapts its parameter types, return types, and arity to the specified type and arity. This method is public mainly
* for implementation reasons, so the adapter classes can invoke it from their constructors that take a Object
* in its first argument to obtain the method handles for their method implementations.
* @param obj the script obj
* @param name the name of the property that contains the function
* @param type the method type it has to conform to
* @param varArg if the Java method for which the function is being adapted is a variable arity method
* @return the appropriately adapted method handle for invoking the script function, or null if the value of the
* property is either null or undefined, or "toString" was requested as the name, but the object doesn't directly
* define it but just inherits it through prototype.
*/
public static MethodHandle getHandle(final Object obj, final String name, final MethodType type, final boolean varArg) {
if (! (obj instanceof ScriptObject)) {
throw typeError("not.an.object", ScriptRuntime.safeToString(obj));
}
final ScriptObject sobj = (ScriptObject)obj;
// Since every JS Object has a toString, we only override "String toString()" it if it's explicitly specified
if ("toString".equals(name) && !sobj.hasOwnProperty("toString")) {
return null;
}
final Object fnObj = sobj.get(name);
if (fnObj instanceof ScriptFunction) {
return adaptHandle(((ScriptFunction)fnObj).getBoundInvokeHandle(sobj), type, varArg);
} else if(fnObj == null || fnObj instanceof Undefined) {
return null;
} else {
throw typeError("not.a.function", name);
}
}
/**
* Returns a thread-local JS object used to define methods for the adapter class being initialized on the current
* thread. This method is public solely for implementation reasons, so the adapter classes can invoke it from their
* static initializers.
* @return the thread-local JS object used to define methods for the class being initialized.
*/
public static ScriptObject getClassOverrides() {
final ScriptObject overrides = classOverrides.get();
assert overrides != null;
return overrides;
}
static void setClassOverrides(ScriptObject overrides) {
classOverrides.set(overrides);
}
private static MethodHandle adaptHandle(final MethodHandle handle, final MethodType type, final boolean varArg) {
return Bootstrap.getLinkerServices().asType(ScriptObject.pairArguments(handle, type, varArg), type);
}
}
|