src/jdk/nashorn/internal/runtime/NativeJavaPackage.java

Thu, 24 May 2018 16:39:31 +0800

author
aoqi
date
Thu, 24 May 2018 16:39:31 +0800
changeset 1959
61ffdd1b89f2
parent 1534
7b64298633b2
parent 1205
4112748288bb
permissions
-rw-r--r--

Merge

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

mercurial