--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/DynamicLinkerFactory.java Tue Nov 24 10:19:34 2015 +0100
@@ -0,0 +1,527 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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 2009-2013 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.dynalink;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MutableCallSite;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.function.Supplier;
+import jdk.dynalink.beans.BeansLinker;
+import jdk.dynalink.internal.AccessControlContextFactory;
+import jdk.dynalink.linker.GuardedInvocation;
+import jdk.dynalink.linker.GuardedInvocationTransformer;
+import jdk.dynalink.linker.GuardingDynamicLinker;
+import jdk.dynalink.linker.GuardingDynamicLinkerExporter;
+import jdk.dynalink.linker.GuardingTypeConverterFactory;
+import jdk.dynalink.linker.LinkRequest;
+import jdk.dynalink.linker.LinkerServices;
+import jdk.dynalink.linker.MethodHandleTransformer;
+import jdk.dynalink.linker.MethodTypeConversionStrategy;
+import jdk.dynalink.linker.support.CompositeGuardingDynamicLinker;
+import jdk.dynalink.linker.support.CompositeTypeBasedGuardingDynamicLinker;
+import jdk.dynalink.linker.support.DefaultInternalObjectFilter;
+import jdk.dynalink.linker.support.TypeUtilities;
+
+/**
+ * A factory class for creating {@link DynamicLinker} objects. Dynamic linkers
+ * are the central objects in Dynalink; these are composed of several
+ * {@link GuardingDynamicLinker} objects and coordinate linking of call sites
+ * with them. The usual dynamic linker is a linker
+ * composed of all {@link GuardingDynamicLinker} objects explicitly pre-created
+ * by the user of the factory and configured with
+ * {@link #setPrioritizedLinkers(List)}, as well as any
+ * {@link #setClassLoader(ClassLoader) automatically discovered} ones, and
+ * finally the ones configured with {@link #setFallbackLinkers(List)}; this last
+ * category usually includes {@link BeansLinker}.
+ */
+public final class DynamicLinkerFactory {
+ private static final AccessControlContext GET_CLASS_LOADER_CONTEXT =
+ AccessControlContextFactory.createAccessControlContext("getClassLoader");
+
+ /**
+ * Default value for {@link #setUnstableRelinkThreshold(int) unstable relink
+ * threshold}.
+ */
+ private static final int DEFAULT_UNSTABLE_RELINK_THRESHOLD = 8;
+
+ private boolean classLoaderExplicitlySet = false;
+ private ClassLoader classLoader;
+
+ private List<? extends GuardingDynamicLinker> prioritizedLinkers;
+ private List<? extends GuardingDynamicLinker> fallbackLinkers;
+ private boolean syncOnRelink = false;
+ private int unstableRelinkThreshold = DEFAULT_UNSTABLE_RELINK_THRESHOLD;
+ private GuardedInvocationTransformer prelinkTransformer;
+ private MethodTypeConversionStrategy autoConversionStrategy;
+ private MethodHandleTransformer internalObjectsFilter;
+
+ private List<ServiceConfigurationError> autoLoadingErrors = Collections.emptyList();
+
+ /**
+ * Creates a new dynamic linker factory with default configuration. Upon
+ * creation, the factory can be configured using various {@code setXxx()}
+ * methods and used to create one or more dynamic linkers according to its
+ * current configuration using {@link #createLinker()}.
+ */
+ public DynamicLinkerFactory() {
+ }
+
+ /**
+ * Sets the class loader for automatic discovery of available guarding
+ * dynamic linkers. {@link GuardingDynamicLinkerExporter} implementations
+ * available through this class loader will be automatically instantiated
+ * using the {@link ServiceLoader} mechanism and the linkers they provide
+ * will be incorporated into {@code DynamicLinker}s that this factory
+ * creates. This allows for cross-language interoperability where call sites
+ * belonging to this language runtime can be linked by linkers from these
+ * automatically discovered runtimes if their native objects are passed to
+ * this runtime. If class loader is not set explicitly by invoking this
+ * method, then the thread context class loader of the thread invoking
+ * {@link #createLinker()} will be used. If this method is invoked
+ * explicitly with null then {@link ServiceLoader#loadInstalled(Class)} will
+ * be used to load the linkers.
+ *
+ * @param classLoader the class loader used for the automatic discovery of
+ * available linkers.
+ */
+ public void setClassLoader(final ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ classLoaderExplicitlySet = true;
+ }
+
+ /**
+ * Sets the prioritized guarding dynamic linkers. Language runtimes using
+ * Dynalink will usually have at least one linker for their own language.
+ * These linkers will be consulted first by the resulting dynamic linker
+ * when it is linking call sites, before any autodiscovered and fallback
+ * linkers. If the factory also autodiscovers a linker class matching one
+ * of the prioritized linkers, the autodiscovered class will be ignored and
+ * the explicit prioritized instance will be used.
+ *
+ * @param prioritizedLinkers the list of prioritized linkers. Can be null.
+ * @throws NullPointerException if any of the list elements are null.
+ */
+ public void setPrioritizedLinkers(final List<? extends GuardingDynamicLinker> prioritizedLinkers) {
+ this.prioritizedLinkers = copyListRequireNonNullElements(prioritizedLinkers);
+ }
+
+ /**
+ * Sets the prioritized guarding dynamic linkers. Identical to calling
+ * {@link #setPrioritizedLinkers(List)} with
+ * {@code Arrays.asList(prioritizedLinkers)}.
+ *
+ * @param prioritizedLinkers an array of prioritized linkers. Can be null.
+ * @throws NullPointerException if any of the array elements are null.
+ */
+ public void setPrioritizedLinkers(final GuardingDynamicLinker... prioritizedLinkers) {
+ setPrioritizedLinkers(prioritizedLinkers == null ? null : Arrays.asList(prioritizedLinkers));
+ }
+
+ /**
+ * Sets a single prioritized linker. Identical to calling
+ * {@link #setPrioritizedLinkers(List)} with a single-element list.
+ *
+ * @param prioritizedLinker the single prioritized linker. Must not be null.
+ * @throws NullPointerException if null is passed.
+ */
+ public void setPrioritizedLinker(final GuardingDynamicLinker prioritizedLinker) {
+ this.prioritizedLinkers = Collections.singletonList(Objects.requireNonNull(prioritizedLinker));
+ }
+
+ /**
+ * Sets the fallback guarding dynamic linkers. These linkers will be
+ * consulted last by the resulting dynamic linker when it is linking call
+ * sites, after any autodiscovered and prioritized linkers. If the factory
+ * also autodiscovers a linker class matching one of the fallback linkers,
+ * the autodiscovered class will be ignored and the explicit fallback
+ * instance will be used.
+ *
+ * @param fallbackLinkers the list of fallback linkers. Can be empty to
+ * indicate the caller wishes to set no fallback linkers. Note that if this
+ * method is not invoked explicitly or is passed null, then the factory
+ * will create an instance of {@link BeansLinker} to serve as the default
+ * fallback linker.
+ * @throws NullPointerException if any of the list elements are null.
+ */
+ public void setFallbackLinkers(final List<? extends GuardingDynamicLinker> fallbackLinkers) {
+ this.fallbackLinkers = copyListRequireNonNullElements(fallbackLinkers);
+ }
+
+ /**
+ * Sets the fallback guarding dynamic linkers. Identical to calling
+ * {@link #setFallbackLinkers(List)} with
+ * {@code Arrays.asList(fallbackLinkers)}.
+ *
+ * @param fallbackLinkers an array of fallback linkers. Can be empty to
+ * indicate the caller wishes to set no fallback linkers. Note that if this
+ * method is not invoked explicitly or is passed null, then the factory
+ * will create an instance of {@link BeansLinker} to serve as the default
+ * fallback linker.
+ * @throws NullPointerException if any of the array elements are null.
+ */
+ public void setFallbackLinkers(final GuardingDynamicLinker... fallbackLinkers) {
+ setFallbackLinkers(fallbackLinkers == null ? null : Arrays.asList(fallbackLinkers));
+ }
+
+ /**
+ * Sets whether the dynamic linker created by this factory will invoke
+ * {@link MutableCallSite#syncAll(MutableCallSite[])} after a call site is
+ * relinked. Defaults to false. You probably want to set it to true if your
+ * runtime supports multithreaded execution of dynamically linked code.
+ * @param syncOnRelink true for invoking sync on relink, false otherwise.
+ */
+ public void setSyncOnRelink(final boolean syncOnRelink) {
+ this.syncOnRelink = syncOnRelink;
+ }
+
+ /**
+ * Sets the unstable relink threshold; the number of times a call site is
+ * relinked after which it will be considered unstable, and subsequent link
+ * requests for it will indicate this. Defaults to 8 when not set explicitly.
+ * @param unstableRelinkThreshold the new threshold. Must not be less than
+ * zero. The value of zero means that call sites will never be considered
+ * unstable.
+ * @see LinkRequest#isCallSiteUnstable()
+ */
+ public void setUnstableRelinkThreshold(final int unstableRelinkThreshold) {
+ if(unstableRelinkThreshold < 0) {
+ throw new IllegalArgumentException("unstableRelinkThreshold < 0");
+ }
+ this.unstableRelinkThreshold = unstableRelinkThreshold;
+ }
+
+ /**
+ * Set the pre-link transformer. This is a
+ * {@link GuardedInvocationTransformer} that will get the final chance to
+ * modify the guarded invocation after it has been created by a component
+ * linker and before the dynamic linker links it into the call site. It is
+ * normally used to adapt the return value type of the invocation to the
+ * type of the call site. When not set explicitly, a default pre-link
+ * transformer will be used that simply calls
+ * {@link GuardedInvocation#asType(LinkerServices, MethodType)}. Customized
+ * pre-link transformers are rarely needed; they are mostly used as a
+ * building block for implementing advanced techniques such as code
+ * deoptimization strategies.
+ * @param prelinkTransformer the pre-link transformer for the dynamic
+ * linker. Can be null to have the factory use the default transformer.
+ */
+ public void setPrelinkTransformer(final GuardedInvocationTransformer prelinkTransformer) {
+ this.prelinkTransformer = prelinkTransformer;
+ }
+
+ /**
+ * Sets an object representing the conversion strategy for automatic type
+ * conversions. After
+ * {@link LinkerServices#asType(MethodHandle, MethodType)} has applied all
+ * custom conversions to a method handle, it still needs to effect
+ * {@link TypeUtilities#isMethodInvocationConvertible(Class, Class) method
+ * invocation conversions} that can usually be automatically applied as per
+ * {@link MethodHandle#asType(MethodType)}. However, sometimes language
+ * runtimes will want to customize even those conversions for their own call
+ * sites. A typical example is allowing unboxing of null return values,
+ * which is by default prohibited by ordinary
+ * {@code MethodHandles.asType()}. In this case, a language runtime can
+ * install its own custom automatic conversion strategy, that can deal with
+ * null values. Note that when the strategy's
+ * {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)}
+ * is invoked, the custom language conversions will already have been
+ * applied to the method handle, so by design the difference between the
+ * handle's current method type and the desired final type will always only
+ * be ones that can be subjected to method invocation conversions. The
+ * strategy also doesn't need to invoke a final
+ * {@code MethodHandle.asType()} as that will be done internally as the
+ * final step.
+ * @param autoConversionStrategy the strategy for applying method invocation
+ * conversions for the linker created by this factory. Can be null for no
+ * custom strategy.
+ */
+ public void setAutoConversionStrategy(final MethodTypeConversionStrategy autoConversionStrategy) {
+ this.autoConversionStrategy = autoConversionStrategy;
+ }
+
+ /**
+ * Sets a method handle transformer that is supposed to act as the
+ * implementation of
+ * {@link LinkerServices#filterInternalObjects(MethodHandle)} for linker
+ * services of dynamic linkers created by this factory. Some language
+ * runtimes can have internal objects that should not escape their scope.
+ * They can add a transformer here that will modify the method handle so
+ * that any parameters that can receive potentially internal language
+ * runtime objects will have a filter added on them to prevent them from
+ * escaping, potentially by wrapping them. The transformer can also
+ * potentially add an unwrapping filter to the return value.
+ * {@link DefaultInternalObjectFilter} is provided as a convenience class
+ * for easily creating such filtering transformers.
+ * @param internalObjectsFilter a method handle transformer filtering out
+ * internal objects, or null.
+ */
+ public void setInternalObjectsFilter(final MethodHandleTransformer internalObjectsFilter) {
+ this.internalObjectsFilter = internalObjectsFilter;
+ }
+
+ /**
+ * Creates a new dynamic linker based on the current configuration. This
+ * method can be invoked more than once to create multiple dynamic linkers.
+ * Automatically discovered linkers are newly instantiated on every
+ * invocation of this method. It is allowed to change the factory's
+ * configuration between invocations. The method is not thread safe. After
+ * invocation, callers can invoke {@link #getAutoLoadingErrors()} to
+ * retrieve a list of {@link ServiceConfigurationError}s that occurred while
+ * trying to load automatically discovered linkers. These are never thrown
+ * from the call to this method as it makes every effort to recover from
+ * them and ignore the failing linkers.
+ * @return the new dynamic Linker
+ */
+ public DynamicLinker createLinker() {
+ // Treat nulls appropriately
+ if(prioritizedLinkers == null) {
+ prioritizedLinkers = Collections.emptyList();
+ }
+ if(fallbackLinkers == null) {
+ fallbackLinkers = Collections.singletonList(new BeansLinker());
+ }
+
+ // Gather classes of all precreated (prioritized and fallback) linkers.
+ // We'll filter out any discovered linkers of the same class.
+ final Set<Class<? extends GuardingDynamicLinker>> knownLinkerClasses =
+ new HashSet<>();
+ addClasses(knownLinkerClasses, prioritizedLinkers);
+ addClasses(knownLinkerClasses, fallbackLinkers);
+
+ final List<GuardingDynamicLinker> discovered = discoverAutoLoadLinkers();
+
+ // Now, concatenate ...
+ final List<GuardingDynamicLinker> linkers =
+ new ArrayList<>(prioritizedLinkers.size() + discovered.size()
+ + fallbackLinkers.size());
+ // ... prioritized linkers, ...
+ linkers.addAll(prioritizedLinkers);
+ // ... filtered discovered linkers, ...
+ for(final GuardingDynamicLinker linker: discovered) {
+ if(!knownLinkerClasses.contains(linker.getClass())) {
+ linkers.add(linker);
+ }
+ }
+ // ... and finally fallback linkers.
+ linkers.addAll(fallbackLinkers);
+ final List<GuardingDynamicLinker> optimized = CompositeTypeBasedGuardingDynamicLinker.optimize(linkers);
+ final GuardingDynamicLinker composite;
+ switch(linkers.size()) {
+ case 0: {
+ composite = (r, s) -> null; // linker that can't link anything
+ break;
+ }
+ case 1: {
+ composite = optimized.get(0);
+ break;
+ }
+ default: {
+ composite = new CompositeGuardingDynamicLinker(optimized);
+ break;
+ }
+ }
+
+ final List<GuardingTypeConverterFactory> typeConverters = new LinkedList<>();
+ for(final GuardingDynamicLinker linker: linkers) {
+ if(linker instanceof GuardingTypeConverterFactory) {
+ typeConverters.add((GuardingTypeConverterFactory)linker);
+ }
+ }
+
+ if(prelinkTransformer == null) {
+ prelinkTransformer = (inv, request, linkerServices) -> inv.asType(linkerServices, request.getCallSiteDescriptor().getMethodType());
+ }
+
+ return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters,
+ autoConversionStrategy), composite, internalObjectsFilter), prelinkTransformer,
+ syncOnRelink, unstableRelinkThreshold);
+ }
+
+ /**
+ * Returns a list of {@link ServiceConfigurationError}s that were
+ * encountered while loading automatically discovered linkers during the
+ * last invocation of {@link #createLinker()}. They can be any non-Dynalink
+ * specific service configuration issues, as well as some Dynalink-specific
+ * errors when an exporter that the factory tried to automatically load:
+ * <ul>
+ * <li>did not have the runtime permission named
+ * {@link GuardingDynamicLinkerExporter#AUTOLOAD_PERMISSION_NAME} in a
+ * system with a security manager, or</li>
+ * <li>returned null from {@link GuardingDynamicLinkerExporter#get()}, or</li>
+ * <li>the list returned from {@link GuardingDynamicLinkerExporter#get()}
+ * had a null element.</li>
+ * </ul>
+ * @return an immutable list of encountered
+ * {@link ServiceConfigurationError}s. Can be empty.
+ */
+ public List<ServiceConfigurationError> getAutoLoadingErrors() {
+ return Collections.unmodifiableList(autoLoadingErrors);
+ }
+
+ private List<GuardingDynamicLinker> discoverAutoLoadLinkers() {
+ autoLoadingErrors = new LinkedList<>();
+ final ClassLoader effectiveClassLoader = classLoaderExplicitlySet ? classLoader : getThreadContextClassLoader();
+ final List<GuardingDynamicLinker> discovered = new LinkedList<>();
+ try {
+ final ServiceLoader<GuardingDynamicLinkerExporter> linkerLoader =
+ AccessController.doPrivileged((PrivilegedAction<ServiceLoader<GuardingDynamicLinkerExporter>>)()-> {
+ if (effectiveClassLoader == null) {
+ return ServiceLoader.loadInstalled(GuardingDynamicLinkerExporter.class);
+ }
+ return ServiceLoader.load(GuardingDynamicLinkerExporter.class, effectiveClassLoader);
+ });
+
+ for(final Iterator<GuardingDynamicLinkerExporter> it = linkerLoader.iterator(); it.hasNext();) {
+ try {
+ final GuardingDynamicLinkerExporter autoLoader = it.next();
+ try {
+ discovered.addAll(requireNonNullElements(
+ Objects.requireNonNull(autoLoader.get(),
+ ()->(autoLoader.getClass().getName() + " returned null from get()")),
+ ()->(autoLoader.getClass().getName() + " returned a list with at least one null element")));
+ } catch (final ServiceConfigurationError|VirtualMachineError e) {
+ // Don't wrap a SCE in another SCE. Also, don't ignore
+ // any VME (e.g. StackOverflowError or OutOfMemoryError).
+ throw e;
+ } catch (final Throwable t) {
+ throw new ServiceConfigurationError(t.getMessage(), t);
+ }
+ } catch (final ServiceConfigurationError e) {
+ // Catch SCE with an individual exporter, carry on with it.hasNext().
+ autoLoadingErrors.add(e);
+ }
+ }
+ } catch (final ServiceConfigurationError e) {
+ // Catch a top-level SCE; one either in ServiceLoader.load(),
+ // ServiceLoader.iterator(), or Iterator.hasNext().
+ autoLoadingErrors.add(e);
+ }
+ return discovered;
+ }
+
+ private static ClassLoader getThreadContextClassLoader() {
+ return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+ @Override
+ public ClassLoader run() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+ }, GET_CLASS_LOADER_CONTEXT);
+ }
+
+ private static void addClasses(final Set<Class<? extends GuardingDynamicLinker>> knownLinkerClasses,
+ final List<? extends GuardingDynamicLinker> linkers) {
+ for(final GuardingDynamicLinker linker: linkers) {
+ knownLinkerClasses.add(linker.getClass());
+ }
+ }
+
+ private static <T> List<T> copyListRequireNonNullElements(final List<T> list) {
+ if (list == null) {
+ return null;
+ }
+ return new ArrayList<>(requireNonNullElements(list, ()->"List has at least one null element"));
+ }
+
+ private static <T> List<T> requireNonNullElements(final List<T> list, final Supplier<String> msgSupplier) {
+ for(final T t: list) {
+ Objects.requireNonNull(t, msgSupplier);
+ }
+ return list;
+ }
+
+}