1 /*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.nashorn.internal.runtime;
27
28 import java.lang.invoke.MethodHandle;
29 import java.lang.invoke.MethodHandles;
30 import java.lang.invoke.MethodType;
31 import jdk.internal.dynalink.CallSiteDescriptor;
32 import jdk.internal.dynalink.beans.StaticClass;
33 import jdk.internal.dynalink.linker.GuardedInvocation;
34 import jdk.internal.dynalink.linker.LinkRequest;
35 import jdk.internal.dynalink.support.Guards;
36 import jdk.nashorn.internal.lookup.MethodHandleFactory;
37 import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
38 import jdk.nashorn.internal.objects.annotations.Attribute;
39 import jdk.nashorn.internal.objects.annotations.Function;
40
41 /**
42 * An object that exposes Java packages and classes as its properties. Packages are exposed as objects that have further
43 * sub-packages and classes as their properties. Normally, three instances of this class are exposed as built-in objects
44 * in Nashorn: {@code "Packages"}, {@code "java"}, and {@code "javax"}. Typical usages are:
45 * <pre>
46 * var list = new java.util.ArrayList()
47 * var sprocket = new Packages.com.acme.Sprocket()
48 * </pre>
49 * or you can store the type objects in a variable for later reuse:
50 * <pre>
51 * var ArrayList = java.util.ArrayList
52 * var list = new ArrayList
53 * </pre>
54 * You can also use {@link jdk.nashorn.internal.objects.NativeJava#type(Object, Object)} to access Java classes. These two statements are mostly
55 * equivalent:
56 * <pre>
57 * var listType1 = java.util.ArrayList
58 * var listType2 = Java.type("java.util.ArrayList")
59 * </pre>
60 * The difference is that {@code Java.type()} will throw an error if the class does not exist, while the first
61 * expression will return an empty object, as it must treat all non-existent classes as potentially being further
62 * subpackages. As such, {@code Java.type()} has the potential to catch typos earlier. A further difference is that
63 * {@code Java.type()} doesn't recognize {@code .} (dot) as the separator between outer class name and inner class name,
64 * it only recognizes the dollar sign. These are equivalent:
65 * <pre>
66 * var ftype1 = java.awt.geom.Arc2D$Float
67 * var ftype2 = java.awt.geom.Arc2D.Float
68 * var ftype3 = Java.asType("java.awt.geom.Arc2D$Float")
69 * var ftype4 = Java.asType("java.awt.geom.Arc2D").Float
70 * </pre>
71 */
72 public final class NativeJavaPackage extends ScriptObject {
73 private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
74 private static final MethodHandle CLASS_NOT_FOUND = findOwnMH("classNotFound", Void.TYPE, NativeJavaPackage.class);
75 private static final MethodHandle TYPE_GUARD = Guards.getClassGuard(NativeJavaPackage.class);
76
77 /** Full name of package (includes path.) */
78 private final String name;
79
80 /**
81 * Public constructor to be accessible from {@link jdk.nashorn.internal.objects.Global}
82 * @param name package name
83 * @param proto proto
84 */
85 public NativeJavaPackage(final String name, final ScriptObject proto) {
86 super(proto, null);
87 // defense-in-path, check here for sensitive packages
88 Context.checkPackageAccess(name);
89 this.name = name;
90 }
91
92 @Override
93 public String getClassName() {
94 return "JavaPackage";
95 }
96
97 @Override
98 public boolean equals(final Object other) {
99 if (other instanceof NativeJavaPackage) {
100 return name.equals(((NativeJavaPackage)other).name);
101 }
102 return false;
103 }
104
105 @Override
106 public int hashCode() {
107 return name == null ? 0 : name.hashCode();
108 }
109
110 /**
111 * Get the full name of the package
112 * @return the name
113 */
114 public String getName() {
115 return name;
116 }
117
118 @Override
119 public String safeToString() {
120 return toString();
121 }
122
123 @Override
124 public String toString() {
125 return "[JavaPackage " + name + "]";
126 }
127
128 @Override
129 public Object getDefaultValue(final Class<?> hint) {
130 if (hint == String.class) {
131 return toString();
132 }
133
134 return super.getDefaultValue(hint);
135 }
136
137 @Override
138 protected GuardedInvocation findNewMethod(CallSiteDescriptor desc) {
139 return createClassNotFoundInvocation(desc);
140 }
141
142 @Override
143 protected GuardedInvocation findCallMethod(CallSiteDescriptor desc, LinkRequest request) {
144 return createClassNotFoundInvocation(desc);
145 }
146
147 private static GuardedInvocation createClassNotFoundInvocation(final CallSiteDescriptor desc) {
148 // If NativeJavaPackage is invoked either as a constructor or as a function, throw a ClassNotFoundException as
149 // we can assume the user attempted to instantiate a non-existent class.
150 final MethodType type = desc.getMethodType();
151 return new GuardedInvocation(
152 MH.dropArguments(CLASS_NOT_FOUND, 1, type.parameterList().subList(1, type.parameterCount())),
153 type.parameterType(0) == NativeJavaPackage.class ? null : TYPE_GUARD);
154 }
155
156 @SuppressWarnings("unused")
157 private static void classNotFound(final NativeJavaPackage pkg) throws ClassNotFoundException {
158 throw new ClassNotFoundException(pkg.name);
159 }
160
161 /**
162 * "No such property" call placeholder.
163 *
164 * This can never be called as we override {@link ScriptObject#noSuchProperty}. We do declare it here as it's a signal
165 * to {@link WithObject} that it's worth trying doing a {@code noSuchProperty} on this object.
166 *
167 * @param self self reference
168 * @param name property name
169 * @return never returns
170 */
171 @Function(attributes = Attribute.NOT_ENUMERABLE)
172 public static Object __noSuchProperty__(final Object self, final Object name) {
173 throw new AssertionError("__noSuchProperty__ placeholder called");
174 }
175
176 /**
177 * "No such method call" placeholder
178 *
179 * This can never be called as we override {@link ScriptObject#noSuchMethod}. We do declare it here as it's a signal
180 * to {@link WithObject} that it's worth trying doing a noSuchProperty on this object.
181 *
182 * @param self self reference
183 * @param args arguments to method
184 * @return never returns
185 */
186 @Function(attributes = Attribute.NOT_ENUMERABLE)
187 public static Object __noSuchMethod__(final Object self, final Object... args) {
188 throw new AssertionError("__noSuchMethod__ placeholder called");
189 }
190
191 /**
192 * Handle creation of new attribute.
193 * @param desc the call site descriptor
194 * @param request the link request
195 * @return Link to be invoked at call site.
196 */
197 @Override
198 public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
199 final String propertyName = desc.getNameToken(2);
200 createProperty(propertyName);
201 return super.lookup(desc, request);
202 }
203
204 @Override
205 protected Object invokeNoSuchProperty(final String name) {
206 return createProperty(name);
207 }
208
209 @Override
210 public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) {
211 return noSuchProperty(desc, request);
212 }
213
214 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
215 return MH.findStatic(MethodHandles.lookup(), NativeJavaPackage.class, name, MH.type(rtype, types));
216 }
217
218 private Object createProperty(final String propertyName) {
219 final String fullName = name.isEmpty() ? propertyName : name + "." + propertyName;
220 final Context context = Context.getContextTrusted();
221
222 Class<?> javaClass = null;
223 try {
224 javaClass = context.findClass(fullName);
225 } catch (final NoClassDefFoundError | ClassNotFoundException e) {
226 //ignored
227 }
228
229 final Object propertyValue;
230 if (javaClass == null) {
231 propertyValue = new NativeJavaPackage(fullName, getProto());
232 } else {
233 propertyValue = StaticClass.forClass(javaClass);
234 }
235
236 set(propertyName, propertyValue, false);
237 return propertyValue;
238 }
239 }
--- EOF ---