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.NativeJava;
  39 import jdk.nashorn.internal.objects.annotations.Attribute;
  40 import jdk.nashorn.internal.objects.annotations.Function;
  41 
  42 /**
  43  * An object that exposes Java packages and classes as its properties. Packages are exposed as objects that have further
  44  * sub-packages and classes as their properties. Normally, three instances of this class are exposed as built-in objects
  45  * in Nashorn: {@code "Packages"}, {@code "java"}, and {@code "javax"}. Typical usages are:
  46  * <pre>
  47  * var list = new java.util.ArrayList()
  48  * var sprocket = new Packages.com.acme.Sprocket()
  49  * </pre>
  50  * or you can store the type objects in a variable for later reuse:
  51  * <pre>
  52  * var ArrayList = java.util.ArrayList
  53  * var list = new ArrayList
  54  * </pre>
  55  * You can also use {@link NativeJava#type(Object, Object)} to access Java classes. These two statements are mostly
  56  * equivalent:
  57  * <pre>
  58  * var listType1 = java.util.ArrayList
  59  * var listType2 = Java.type("java.util.ArrayList")
  60  * </pre>
  61  * The difference is that {@code Java.type()} will throw an error if the class does not exist, while the first
  62  * expression will return an empty object, as it must treat all non-existent classes as potentially being further
  63  * subpackages. As such, {@code Java.type()} has the potential to catch typos earlier. A further difference is that
  64  * {@code Java.type()} doesn't recognize {@code .} (dot) as the separator between outer class name and inner class name,
  65  * it only recognizes the dollar sign. These are equivalent:
  66  * <pre>
  67  * var ftype1 = java.awt.geom.Arc2D$Float
  68  * var ftype2 = java.awt.geom.Arc2D.Float
  69  * var ftype3 = Java.asType("java.awt.geom.Arc2D$Float")
  70  * var ftype4 = Java.asType("java.awt.geom.Arc2D").Float
  71  * </pre>
  72  */
  73 public final class NativeJavaPackage extends ScriptObject {
  74     private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
  75     private static final MethodHandle CLASS_NOT_FOUND = findOwnMH("classNotFound", Void.TYPE, NativeJavaPackage.class);
  76     private static final MethodHandle TYPE_GUARD = Guards.getClassGuard(NativeJavaPackage.class);
  77 
  78     /** Full name of package (includes path.) */
  79     private final String name;
  80 
  81     /**
  82      * Public constructor to be accessible from {@link jdk.nashorn.internal.objects.Global}
  83      * @param name  package name
  84      * @param proto proto
  85      */
  86     public NativeJavaPackage(final String name, final ScriptObject proto) {
  87         super(proto, null);
  88         this.name = name;
  89     }
  90 
  91     @Override
  92     public String getClassName() {
  93         return "JavaPackage";
  94     }
  95 
  96     @Override
  97     public boolean equals(final Object other) {
  98         if (other instanceof NativeJavaPackage) {
  99             return name.equals(((NativeJavaPackage)other).name);
 100         }
 101         return false;
 102     }
 103 
 104     @Override
 105     public int hashCode() {
 106         return name == null ? 0 : name.hashCode();
 107     }
 108 
 109     /**
 110      * Get the full name of the package
 111      * @return the name
 112      */
 113     public String getName() {
 114         return name;
 115     }
 116 
 117     @Override
 118     public String safeToString() {
 119         return toString();
 120     }
 121 
 122     @Override
 123     public String toString() {
 124         return "[JavaPackage " + name + "]";
 125     }
 126 
 127     @Override
 128     public Object getDefaultValue(final Class<?> hint) {
 129         if (hint == String.class) {
 130             return toString();
 131         }
 132 
 133         return super.getDefaultValue(hint);
 134     }
 135 
 136     @Override
 137     protected GuardedInvocation findNewMethod(CallSiteDescriptor desc) {
 138         return createClassNotFoundInvocation(desc);
 139     }
 140 
 141     @Override
 142     protected GuardedInvocation findCallMethod(CallSiteDescriptor desc, LinkRequest request) {
 143         return createClassNotFoundInvocation(desc);
 144     }
 145 
 146     private static GuardedInvocation createClassNotFoundInvocation(final CallSiteDescriptor desc) {
 147         // If NativeJavaPackage is invoked either as a constructor or as a function, throw a ClassNotFoundException as
 148         // we can assume the user attempted to instantiate a non-existent class.
 149         final MethodType type = desc.getMethodType();
 150         return new GuardedInvocation(
 151                 MH.dropArguments(CLASS_NOT_FOUND, 1, type.parameterList().subList(1, type.parameterCount())),
 152                 type.parameterType(0) == NativeJavaPackage.class ? null : TYPE_GUARD);
 153     }
 154 
 155     @SuppressWarnings("unused")
 156     private static void classNotFound(final NativeJavaPackage pkg) throws ClassNotFoundException {
 157         throw new ClassNotFoundException(pkg.name);
 158     }
 159 
 160     /**
 161      * "No such property" call placeholder.
 162      *
 163      * This can never be called as we override {@link ScriptObject#noSuchProperty}. We do declare it here as it's a signal
 164      * to {@link WithObject} that it's worth trying doing a {@code noSuchProperty} on this object.
 165      *
 166      * @param self self reference
 167      * @param name property name
 168      * @return never returns
 169      */
 170     @Function(attributes = Attribute.NOT_ENUMERABLE)
 171     public static Object __noSuchProperty__(final Object self, final Object name) {
 172         throw new AssertionError("__noSuchProperty__ placeholder called");
 173     }
 174 
 175     /**
 176      * "No such method call" placeholder
 177      *
 178      * This can never be called as we override {@link ScriptObject#noSuchMethod}. We do declare it here as it's a signal
 179      * to {@link WithObject} that it's worth trying doing a noSuchProperty on this object.
 180      *
 181      * @param self self reference
 182      * @param args arguments to method
 183      * @return never returns
 184      */
 185     @Function(attributes = Attribute.NOT_ENUMERABLE)
 186     public static Object __noSuchMethod__(final Object self, final Object... args) {
 187         throw new AssertionError("__noSuchMethod__ placeholder called");
 188     }
 189 
 190     /**
 191      * Handle creation of new attribute.
 192      * @param desc the call site descriptor
 193      * @param request the link request
 194      * @return Link to be invoked at call site.
 195      */
 196     @Override
 197     public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
 198         final String propertyName = desc.getNameToken(2);
 199         final String fullName     = name.isEmpty() ? propertyName : name + "." + propertyName;
 200 
 201         final Context context = Context.getContextTrusted();
 202 
 203         Class<?> javaClass = null;
 204         try {
 205             javaClass = context.findClass(fullName);
 206         } catch (final NoClassDefFoundError | ClassNotFoundException e) {
 207             //ignored
 208         }
 209 
 210         if (javaClass == null) {
 211             set(propertyName, new NativeJavaPackage(fullName, getProto()), false);
 212         } else {
 213             set(propertyName, StaticClass.forClass(javaClass), false);
 214         }
 215 
 216         return super.lookup(desc, request);
 217     }
 218 
 219     @Override
 220     public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) {
 221         return noSuchProperty(desc, request);
 222     }
 223 
 224     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
 225         return MH.findStatic(MethodHandles.lookup(), NativeJavaPackage.class, name, MH.type(rtype, types));
 226     }
 227 }
--- EOF ---