MOP2: first step in implementing the constant meta class and facade
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/7285ffcf Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/7285ffcf Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/7285ffcf Branch: refs/heads/parrot Commit: 7285ffcfe5efbac1f47a59b1417bf7cf72b75606 Parents: 9e25a9a Author: Jochen Theodorou <blackd...@gmx.org> Authored: Sun Oct 9 18:23:07 2016 +0200 Committer: Jochen Theodorou <blackd...@gmx.org> Committed: Sun Oct 9 18:23:32 2016 +0200 ---------------------------------------------------------------------- .../internal/metaclass/MetaClassConstant.java | 50 +++++++++++ .../apache/groovy/internal/util/Function.java | 31 +++++++ .../internal/util/ReevaluatingReference.java | 88 ++++++++++++++++++++ .../apache/groovy/internal/util/Supplier.java | 31 +++++++ .../groovy/internal/util/UncheckedThrow.java | 1 + .../org/apache/groovy/metaclass/MetaClass.java | 41 +++++++++ src/main/org/apache/groovy/metaclass/Realm.java | 51 ++++++++---- 7 files changed, 278 insertions(+), 15 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/groovy/blob/7285ffcf/src/main/org/apache/groovy/internal/metaclass/MetaClassConstant.java ---------------------------------------------------------------------- diff --git a/src/main/org/apache/groovy/internal/metaclass/MetaClassConstant.java b/src/main/org/apache/groovy/internal/metaclass/MetaClassConstant.java new file mode 100644 index 0000000..df5a7ec --- /dev/null +++ b/src/main/org/apache/groovy/internal/metaclass/MetaClassConstant.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the 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. + */ + +package org.apache.groovy.internal.metaclass; + +import groovy.lang.MetaClassImpl; +import groovy.lang.MetaMethod; +import org.apache.groovy.lang.annotation.Incubating; + +import java.lang.invoke.SwitchPoint; + +/** + * The one and only implementation of a meta class. + * INTERNAL USE ONLY. + */ +@Incubating +public final class MetaClassConstant<T> { + private final SwitchPoint switchPoint = new SwitchPoint(); + //TODO Joche: replace with real implementation + private final MetaClassImpl impl; + + public MetaClassConstant(Class<T> clazz) { + impl = new MetaClassImpl(clazz); + } + + public SwitchPoint getSwitchPoint() { + return switchPoint; + } + + // TODO Jochen: replace with new MetaMethod + public MetaMethod getMethod(String name, Class[] parameters) { + return impl.pickMethod(name, parameters); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/7285ffcf/src/main/org/apache/groovy/internal/util/Function.java ---------------------------------------------------------------------- diff --git a/src/main/org/apache/groovy/internal/util/Function.java b/src/main/org/apache/groovy/internal/util/Function.java new file mode 100644 index 0000000..3a4fea5 --- /dev/null +++ b/src/main/org/apache/groovy/internal/util/Function.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the 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. + */ + +package org.apache.groovy.internal.util; + +import org.apache.groovy.lang.annotation.Incubating; + +/** + * Backport of Java8 Function. + * INTERNAL USE ONLY. + */ +@Incubating +public interface Function<T, R> { + R apply(T t); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/groovy/blob/7285ffcf/src/main/org/apache/groovy/internal/util/ReevaluatingReference.java ---------------------------------------------------------------------- diff --git a/src/main/org/apache/groovy/internal/util/ReevaluatingReference.java b/src/main/org/apache/groovy/internal/util/ReevaluatingReference.java new file mode 100644 index 0000000..feeeea8 --- /dev/null +++ b/src/main/org/apache/groovy/internal/util/ReevaluatingReference.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the 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. + */ + +package org.apache.groovy.internal.util; + +import org.apache.groovy.lang.annotation.Incubating; +import org.codehaus.groovy.GroovyBugError; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.SwitchPoint; +import java.lang.ref.WeakReference; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +/** + * This class represents a reference to the most actual incarnation of a Metaclass. + * INTERNAL USE ONLY. + */ +@Incubating +public class ReevaluatingReference<T> { + private static final MethodHandle FALLBACK_HANDLE; + static { + try { + //TODO Jochen: move the findSpecial to a central place together with others to easy security configuration + FALLBACK_HANDLE = AccessController.doPrivileged(new PrivilegedExceptionAction<MethodHandle>() { + @Override + public MethodHandle run() throws Exception { + return MethodHandles.lookup().findSpecial( + ReevaluatingReference.class, "replacePayLoad", + MethodType.methodType(Object.class), + ReevaluatingReference.class); + } + }); + } catch (PrivilegedActionException e) { + throw new GroovyBugError(e); + } + } + + private final Supplier<T> valueSupplier; + private final Function<T, SwitchPoint> validationSupplier; + private final WeakReference<Class<T>> clazzRef; + private MethodHandle returnRef; + + + public ReevaluatingReference(Class clazz, Supplier<T> valueSupplier, Function<T, SwitchPoint> validationSupplier) { + this.valueSupplier = valueSupplier; + this.validationSupplier = validationSupplier; + clazzRef = new WeakReference<Class<T>>(clazz); + replacePayLoad(); + } + + private T replacePayLoad() { + T payload = valueSupplier.get(); + MethodHandle ref = MethodHandles.constant(clazzRef.get(), payload); + SwitchPoint sp = validationSupplier.apply(payload); + returnRef = sp.guardWithTest(ref, FALLBACK_HANDLE); + return payload; + } + + public T getPayload() { + T ref = null; + try { + ref = (T) returnRef.invokeExact(); + } catch (Throwable throwable) { + UncheckedThrow.rethrow(throwable); + } + return ref; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/7285ffcf/src/main/org/apache/groovy/internal/util/Supplier.java ---------------------------------------------------------------------- diff --git a/src/main/org/apache/groovy/internal/util/Supplier.java b/src/main/org/apache/groovy/internal/util/Supplier.java new file mode 100644 index 0000000..3a01785 --- /dev/null +++ b/src/main/org/apache/groovy/internal/util/Supplier.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the 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. + */ + +package org.apache.groovy.internal.util; + +import org.apache.groovy.lang.annotation.Incubating; + +/** + * Backport of Java8 Supplier. + * INTERNAL USE ONLY. + */ +@Incubating +public interface Supplier<T> { + T get(); +} http://git-wip-us.apache.org/repos/asf/groovy/blob/7285ffcf/src/main/org/apache/groovy/internal/util/UncheckedThrow.java ---------------------------------------------------------------------- diff --git a/src/main/org/apache/groovy/internal/util/UncheckedThrow.java b/src/main/org/apache/groovy/internal/util/UncheckedThrow.java index 75f2071..7f6cc8a 100644 --- a/src/main/org/apache/groovy/internal/util/UncheckedThrow.java +++ b/src/main/org/apache/groovy/internal/util/UncheckedThrow.java @@ -23,6 +23,7 @@ import org.apache.groovy.lang.annotation.Incubating; /** * Allows to throw a checked exception unchecked. + * INTERNAL USE ONLY. */ @Incubating public class UncheckedThrow { http://git-wip-us.apache.org/repos/asf/groovy/blob/7285ffcf/src/main/org/apache/groovy/metaclass/MetaClass.java ---------------------------------------------------------------------- diff --git a/src/main/org/apache/groovy/metaclass/MetaClass.java b/src/main/org/apache/groovy/metaclass/MetaClass.java new file mode 100644 index 0000000..6ebe4fc --- /dev/null +++ b/src/main/org/apache/groovy/metaclass/MetaClass.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the 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. + */ + +package org.apache.groovy.metaclass; + +import groovy.lang.MetaMethod; +import org.apache.groovy.internal.metaclass.MetaClassConstant; +import org.apache.groovy.internal.util.ReevaluatingReference; +import org.apache.groovy.lang.annotation.Incubating; + +/** + * A MetaClass within Groovy defines the behaviour of any given Groovy or Java class + */ +@Incubating +public final class MetaClass<T> { + private final ReevaluatingReference<MetaClassConstant<T>> implRef; + + MetaClass(ReevaluatingReference<MetaClassConstant<T>> implRef) { + this.implRef = implRef; + } + + public MetaMethod getMethod(String name, Class[] parameters) { + return implRef.getPayload().getMethod(name, parameters); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/7285ffcf/src/main/org/apache/groovy/metaclass/Realm.java ---------------------------------------------------------------------- diff --git a/src/main/org/apache/groovy/metaclass/Realm.java b/src/main/org/apache/groovy/metaclass/Realm.java index 53b097a..d398c1c 100644 --- a/src/main/org/apache/groovy/metaclass/Realm.java +++ b/src/main/org/apache/groovy/metaclass/Realm.java @@ -19,30 +19,29 @@ package org.apache.groovy.metaclass; -import groovy.lang.GroovySystem; -import groovy.lang.MetaClass; -import groovy.lang.MetaClassImpl; -import groovy.lang.MetaClassRegistry; +import org.apache.groovy.internal.metaclass.MetaClassConstant; +import org.apache.groovy.internal.util.Function; +import org.apache.groovy.internal.util.ReevaluatingReference; +import org.apache.groovy.internal.util.Supplier; import org.apache.groovy.lang.annotation.Incubating; -import java.lang.ref.WeakReference; -import java.util.Map; +import java.lang.invoke.SwitchPoint; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; /** * A Realm is the representation of a metaclass layer in a tree of realm objects. */ @Incubating public final class Realm { - public final static Realm ROOT = new Realm("Root", null); + private final static Realm ROOT = new Realm("ROOT", null); private final String name; private final Realm parent; - private final ClassValue<MetaClass> cv = new ClassValue<MetaClass>() { + private final ClassValue<MetaClassConstant<?>> cv = new ClassValue<MetaClassConstant<?>>() { @Override - protected MetaClass computeValue(Class type) { - return new MetaClassImpl(type); + @SuppressWarnings("unchecked") + protected MetaClassConstant<?> computeValue(Class<?> type) { + return new MetaClassConstant(type); } }; @@ -51,9 +50,13 @@ public final class Realm { this.parent = parent; } - public Realm createRealm(String name) { + public static Realm newRealm(String name, Realm parent) { Objects.requireNonNull(name, "missing realm name"); - return new Realm(name, this); + if (parent == null) { + return new Realm(name, ROOT); + } else { + return new Realm(name, parent); + } } @Override @@ -64,7 +67,25 @@ public final class Realm { '}'; } - public MetaClass getMetaClass(Class<?> theClass) { - return cv.get(theClass); + public <T> MetaClass<T> getMetaClass(final Class<T> theClass) { + Supplier<MetaClassConstant<T>> valueSupplier = new Supplier<MetaClassConstant<T>>() { + @Override + @SuppressWarnings("unchecked") + public MetaClassConstant<T> get() { + return (MetaClassConstant<T>) cv.get(theClass); + } + }; + Function<MetaClassConstant<T>, SwitchPoint> validationSupplier = new Function<MetaClassConstant<T>, SwitchPoint>() { + @Override + public SwitchPoint apply(MetaClassConstant<T> metaClassImpl) { + return metaClassImpl.getSwitchPoint(); + } + }; + ReevaluatingReference<MetaClassConstant<T>> ref = new ReevaluatingReference<>( + MetaClassConstant.class, + valueSupplier, + validationSupplier + ); + return new MetaClass<>(ref); } }