diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java index 08b25a68d9c1..078dcff95e70 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java @@ -318,25 +318,13 @@ public List> getRequiredFeatures() { @Override public void duringSetup(DuringSetupAccess c) { - DuringSetupAccessImpl config = (DuringSetupAccessImpl) c; - AnalysisMetaAccess aMetaAccess = config.getMetaAccess(); - - try { - /* - * Check early that the classpath is set up correctly. The base class of SubstrateType - * is the NodeClass from Truffle. So we require Truffle on the class path for any images - * and tests that use Graal at run time. - */ - aMetaAccess.lookupJavaType(SubstrateType.class); - } catch (NoClassDefFoundError ex) { - throw VMError.shouldNotReachHere("Building a native image with Graal support requires Truffle on the class path. For unit tests run with 'svmtest', add the option '--truffle'."); - } - ImageSingletons.add(GraalSupport.class, new GraalSupport()); - if (!ImageSingletons.contains(RuntimeGraalSetup.class)) { ImageSingletons.add(RuntimeGraalSetup.class, new SubstrateRuntimeGraalSetup()); } + + DuringSetupAccessImpl config = (DuringSetupAccessImpl) c; + AnalysisMetaAccess aMetaAccess = config.getMetaAccess(); GraalProviderObjectReplacements providerReplacements = ImageSingletons.lookup(RuntimeGraalSetup.class).getProviderObjectReplacements(aMetaAccess); objectReplacer = new GraalObjectReplacer(config.getUniverse(), aMetaAccess, providerReplacements); config.registerObjectReplacer(objectReplacer); diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalObjectReplacer.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalObjectReplacer.java index aaef0e09eaa5..14f7f9fd02a4 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalObjectReplacer.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalObjectReplacer.java @@ -263,7 +263,7 @@ public synchronized SubstrateField createField(ResolvedJavaField original) { if (ReadableJavaField.injectFinalForRuntimeCompilation(aField.wrapped)) { modifiers = modifiers | Modifier.FINAL; } - sField = new SubstrateField(aMetaAccess, aField, modifiers, stringTable); + sField = new SubstrateField(aField, modifiers, stringTable); fields.put(aField, sField); sField.setLinks(createType(aField.getType()), createType(aField.getDeclaringClass())); diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java index c86e6cc00a1e..1c8036836ccd 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java @@ -38,16 +38,11 @@ import com.oracle.svm.core.meta.SharedField; import com.oracle.svm.core.util.HostedStringDeduplication; import com.oracle.svm.core.util.VMError; -import com.oracle.truffle.api.nodes.Node.Child; -import com.oracle.truffle.api.nodes.Node.Children; -import com.oracle.truffle.api.nodes.NodeCloneable; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.PrimitiveConstant; import jdk.vm.ci.meta.ResolvedJavaField; -import jdk.vm.ci.meta.ResolvedJavaType; public class SubstrateField implements SharedField { @@ -66,21 +61,12 @@ public class SubstrateField implements SharedField { @UnknownObjectField(types = {DirectSubstrateObjectConstant.class, PrimitiveConstant.class}, fullyQualifiedTypes = "jdk.vm.ci.meta.NullConstant")// JavaConstant constantValue; - /* Truffle access this information frequently, so it is worth caching it in a field. */ - final boolean truffleChildField; - final boolean truffleChildrenField; - final boolean truffleCloneableField; - - public SubstrateField(MetaAccessProvider originalMetaAccess, ResolvedJavaField original, int modifiers, HostedStringDeduplication stringTable) { + public SubstrateField(ResolvedJavaField original, int modifiers, HostedStringDeduplication stringTable) { VMError.guarantee(!original.isInternal(), "Internal fields are not supported for JIT compilation"); this.modifiers = modifiers; this.name = stringTable.deduplicate(original.getName(), true); this.hashCode = original.hashCode(); - - truffleChildField = original.getAnnotation(Child.class) != null; - truffleChildrenField = original.getAnnotation(Children.class) != null; - truffleCloneableField = originalMetaAccess.lookupJavaType(NodeCloneable.class).isAssignableFrom((ResolvedJavaType) original.getType()); } @Platforms(Platform.HOSTED_ONLY.class) diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateType.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateType.java index a6a433915658..4ffcead8b2cf 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateType.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateType.java @@ -25,11 +25,7 @@ package com.oracle.svm.graal.meta; import java.lang.annotation.Annotation; -import java.lang.reflect.Modifier; import java.util.Arrays; -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.function.Predicate; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -42,11 +38,7 @@ import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.util.VMError; -import com.oracle.truffle.api.nodes.DenyReplace; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.NodeClass; -import jdk.internal.misc.Unsafe; import jdk.vm.ci.meta.Assumptions.AssumptionResult; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; @@ -55,10 +47,7 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; -public class SubstrateType extends NodeClass implements SharedType { - - protected static final SubstrateType[] EMPTY_ARRAY = new SubstrateType[0]; - +public class SubstrateType implements SharedType { private final JavaKind kind; private final DynamicHub hub; @@ -74,9 +63,6 @@ public class SubstrateType extends NodeClass implements SharedType { @UnknownObjectField(types = {DynamicHub.class}) protected DynamicHub uniqueConcreteImplementation; public SubstrateType(JavaKind kind, DynamicHub hub) { - /* The constructor does not use the parameter, so we can pass whatever we want. */ - super(Node.class); - this.kind = kind; this.hub = hub; } @@ -468,283 +454,4 @@ public boolean equals(Object obj) { public String toString() { return "SubstrateType<" + toJavaName(true) + ">"; } - - /* - * Implementation of Truffle NodeClass interface - */ - - @Override - public Iterator makeIterator(Node node) { - return new SubstrateNodeIterator(node, this); - } - - @Override - @SuppressWarnings("unchecked") - public Class getType() { - assert Node.class.isAssignableFrom(DynamicHub.toClass(getHub())); - return (Class) DynamicHub.toClass(getHub()); - } - - @Override - protected SubstrateField[] getNodeFieldArray() { - if (rawAllInstanceFields == null) { - /* - * The type was created at run time from the Class, so we do not have field information. - * If we need the fields for a type, the type has to be created during image generation. - */ - throw SubstrateNodeFieldIterator.noFieldsError(this); - } else { - return rawAllInstanceFields; - } - } - - @Override - public void putFieldObject(Object field, Node receiver, Object value) { - assert !getFieldType(field).isPrimitive(); - assert value == null || getFieldType(field).isInstance(value); - long offset = makeOffset((SubstrateField) field); - Unsafe.getUnsafe().putObject(receiver, offset, value); - } - - @Override - public Object getFieldObject(Object field, Node receiver) { - assert !getFieldType(field).isPrimitive(); - long offset = makeOffset((SubstrateField) field); - return Unsafe.getUnsafe().getObject(receiver, offset); - } - - @Override - public Object getFieldValue(Object field, Node node) { - Class fieldType = getFieldType(field); - long offset = makeOffset((SubstrateField) field); - if (fieldType == boolean.class) { - return Unsafe.getUnsafe().getBoolean(node, offset); - } else if (fieldType == byte.class) { - return Unsafe.getUnsafe().getByte(node, offset); - } else if (fieldType == short.class) { - return Unsafe.getUnsafe().getShort(node, offset); - } else if (fieldType == char.class) { - return Unsafe.getUnsafe().getChar(node, offset); - } else if (fieldType == int.class) { - return Unsafe.getUnsafe().getInt(node, offset); - } else if (fieldType == long.class) { - return Unsafe.getUnsafe().getLong(node, offset); - } else if (fieldType == float.class) { - return Unsafe.getUnsafe().getFloat(node, offset); - } else if (fieldType == double.class) { - return Unsafe.getUnsafe().getDouble(node, offset); - } else { - return Unsafe.getUnsafe().getObject(node, offset); - } - } - - @Override - public boolean isChildField(Object field) { - return isChildField((SubstrateField) field); - } - - @Override - public boolean isChildrenField(Object field) { - return isChildrenField((SubstrateField) field); - } - - @Override - public boolean isCloneableField(Object field) { - return ((SubstrateField) field).truffleCloneableField; - } - - @Override - protected boolean isReplaceAllowed() { - boolean replaceDenied = Modifier.isFinal(getModifiers()) && getAnnotation(DenyReplace.class) != null; - return !replaceDenied; - } - - @Override - public Class getFieldType(Object field) { - return makeType((SubstrateField) field); - } - - @Override - public String getFieldName(Object field) { - return ((SubstrateField) field).getName(); - } - - protected static boolean isChildrenField(SubstrateField field) { - return field.truffleChildrenField; - } - - protected static boolean isChildField(SubstrateField field) { - return field.truffleChildField; - } - - static Class makeType(SubstrateField field) { - if (field.getType().getStorageKind().isPrimitive()) { - /* For fields with a Word type, we have to return the primitive class. */ - return field.getType().getStorageKind().toJavaClass(); - } else { - return DynamicHub.toClass(field.getType().getHub()); - } - } - - static Class makeDeclaringClass(SubstrateField field) { - return DynamicHub.toClass(field.getDeclaringClass().getHub()); - } - - static long makeOffset(SubstrateField field) { - assert field.getLocation() >= 0; - return field.getLocation(); - } - -} - -class SubstrateNodeFieldIterator implements Iterator { - private final SubstrateType type; - private final Predicate filter; - private int nextFieldInType = 0; - private SubstrateField nextField; - - SubstrateNodeFieldIterator(SubstrateType type, Predicate filter) { - this.type = type; - this.filter = filter; - computeNext(); - } - - private void computeNext() { - SubstrateField[] rawAllInstanceFields = type.rawAllInstanceFields; - if (rawAllInstanceFields == null) { - /* - * The type was created at run time from the Class, so we do not have field information. - * If we need the fields for a type, the type has to be created during image generation. - */ - throw noFieldsError(type); - - } else { - SubstrateField[] fields = rawAllInstanceFields; - while (nextFieldInType < fields.length) { - SubstrateField field = fields[nextFieldInType]; - nextFieldInType++; - if (filter == null || filter.test(field)) { - nextField = field; - return; - } - } - } - - nextField = null; - } - - @Override - public boolean hasNext() { - return nextField != null; - } - - @Override - public SubstrateField next() { - SubstrateField result = nextField; - if (result == null) { - throw new NoSuchElementException(); - } - computeNext(); - return result; - } - - static RuntimeException noFieldsError(SubstrateType type) { - throw VMError.shouldNotReachHere("no instance fields for " + type.getHub().getName() + " available"); - } -} - -class SubstrateNodeIterator implements Iterator { - - private final Node node; - - private final SubstrateType type; - private int nextFieldInType; - - private Object[] children; - private int nextChildInChildren; - - private Node next; - - protected SubstrateNodeIterator(Node node, SubstrateType type) { - this.node = node; - this.type = type; - computeNext(); - } - - private void computeNext() { - if (computeNextFromChildren()) { - /* We have another array element from the last @Children field. */ - return; - } - - SubstrateField[] rawAllInstanceFields = type.rawAllInstanceFields; - if (rawAllInstanceFields == null) { - /* - * The type was created at run time from the Class, so we do not have field information. - * If we need the fields for a type, the type has to be created during image generation. - */ - throw SubstrateNodeFieldIterator.noFieldsError(type); - - } else { - SubstrateField[] fields = rawAllInstanceFields; - while (nextFieldInType < fields.length) { - SubstrateField field = fields[nextFieldInType]; - nextFieldInType++; - if (computeNextFromField(field)) { - return; - } - } - } - - next = null; - } - - private boolean computeNextFromField(SubstrateField field) { - if (SubstrateType.isChildField(field)) { - long offset = field.getLocation(); - next = (Node) Unsafe.getUnsafe().getObject(node, offset); - if (next != null) { - return true; - } - } else if (SubstrateType.isChildrenField(field)) { - long offset = field.getLocation(); - children = (Object[]) Unsafe.getUnsafe().getObject(node, offset); - nextChildInChildren = 0; - return computeNextFromChildren(); - } - return false; - } - - private boolean computeNextFromChildren() { - if (children == null) { - return false; - } - - while (nextChildInChildren < children.length) { - next = (Node) children[nextChildInChildren]; - nextChildInChildren++; - if (next != null) { - return true; - } - } - - children = null; - nextChildInChildren = 0; - return false; - } - - @Override - public boolean hasNext() { - return next != null; - } - - @Override - public Node next() { - Node result = next; - if (result == null) { - throw new NoSuchElementException(); - } - computeNext(); - return result; - } } diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/NodeClassSupport.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/NodeClassSupport.java new file mode 100644 index 000000000000..17ad657366a7 --- /dev/null +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/NodeClassSupport.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022, 2022, 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. + */ +package com.oracle.svm.truffle; + +import org.graalvm.collections.EconomicMap; +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.svm.core.util.ImageHeapMap; +import com.oracle.truffle.api.nodes.NodeClass; + +class NodeClassSupport { + final EconomicMap, NodeClass> nodeClasses = ImageHeapMap.create(); + + static NodeClassSupport singleton() { + return ImageSingletons.lookup(NodeClassSupport.class); + } +} diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java index 7259d3deed48..20b3ffb1b0e9 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java @@ -80,17 +80,16 @@ import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.configure.ResourcesRegistry; -import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.graal.hosted.GraalObjectReplacer; import com.oracle.svm.graal.hosted.GraalProviderObjectReplacements; import com.oracle.svm.graal.hosted.RuntimeGraalSetup; import com.oracle.svm.graal.hosted.SubstrateRuntimeGraalSetup; -import com.oracle.svm.graal.meta.SubstrateType; import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; +import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.snippets.SubstrateGraphBuilderPlugins; import com.oracle.svm.truffle.api.SubstrateTruffleRuntime; @@ -299,37 +298,22 @@ public void duringSetup(DuringSetupAccess access) { VMError.shouldNotReachHere("TruffleFeature is required for SubstrateTruffleRuntime."); } - FeatureImpl.DuringSetupAccessImpl config = (FeatureImpl.DuringSetupAccessImpl) access; - - metaAccess = ((FeatureImpl.DuringSetupAccessImpl) access).getMetaAccess(); + ImageSingletons.add(NodeClassSupport.class, new NodeClassSupport()); if (!ImageSingletons.contains(RuntimeGraalSetup.class)) { ImageSingletons.add(RuntimeGraalSetup.class, new SubstrateRuntimeGraalSetup()); } + + DuringSetupAccessImpl config = (DuringSetupAccessImpl) access; + metaAccess = config.getMetaAccess(); GraalProviderObjectReplacements providerReplacements = ImageSingletons.lookup(RuntimeGraalSetup.class) .getProviderObjectReplacements(metaAccess); graalObjectReplacer = new GraalObjectReplacer(config.getUniverse(), metaAccess, providerReplacements); - Class nodeFieldData = access.findClassByName("com.oracle.truffle.api.nodes.NodeClassImpl$NodeFieldData"); - access.registerObjectReplacer((e) -> replaceNodeFieldAccessor(nodeFieldData, e)); - layoutInfoMapField = config.findField("com.oracle.truffle.object.DefaultLayout$LayoutInfo", "LAYOUT_INFO_MAP"); layoutMapField = config.findField("com.oracle.truffle.object.DefaultLayout", "LAYOUT_MAP"); libraryFactoryCacheField = config.findField("com.oracle.truffle.api.library.LibraryFactory$ResolvedDispatch", "CACHE"); } - @SuppressWarnings("deprecation") - private Object replaceNodeFieldAccessor(Class invalidNodeFieldType, Object source) { - if (source != null && source.getClass() == invalidNodeFieldType) { - throw VMError.shouldNotReachHere("Cannot have NodeFieldData in image, they must be created lazily"); - } else if (source instanceof NodeClass && !(source instanceof SubstrateType)) { - NodeClass nodeClass = (NodeClass) source; - NodeClass replacement = graalObjectReplacer.createType(metaAccess.lookupJavaType(nodeClass.getType())); - assert replacement != null; - return replacement; - } - return source; - } - @SuppressWarnings("deprecation") @Override public void beforeAnalysis(BeforeAnalysisAccess access) { @@ -416,6 +400,7 @@ private void registerUnsafeAccess(DuringAnalysisAccess access, registeredClasses.add(clazz); NodeClass nodeClass = NodeClass.get(clazz); + NodeClassSupport.singleton().nodeClasses.put(clazz, nodeClass); Field[] fields; try { @@ -941,12 +926,11 @@ final class Target_com_oracle_truffle_object_CoreLocations_DynamicLongFieldLocat @TargetClass(className = "com.oracle.truffle.api.nodes.NodeClass", onlyWith = TruffleBaseFeature.IsEnabled.class) final class Target_com_oracle_truffle_api_nodes_NodeClass { - @Substitute public static NodeClass get(Class clazz) { CompilerAsserts.neverPartOfCompilation(); - NodeClass nodeClass = (NodeClass) DynamicHub.fromClass(clazz).getMetaType(); + NodeClass nodeClass = NodeClassSupport.singleton().nodeClasses.get(clazz); if (nodeClass == null) { throw shouldNotReachHere("Unknown node class: " + clazz.getName()); } @@ -960,3 +944,29 @@ final class Target_com_oracle_truffle_api_nodes_Node { @NeverInline("") public native void adoptChildren(); } + +@TargetClass(className = "com.oracle.truffle.api.nodes.NodeClassImpl", innerClass = "NodeFieldData", onlyWith = TruffleBaseFeature.IsEnabled.class) +final class Target_com_oracle_truffle_api_nodes_NodeClassImpl_NodeFieldData { + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = OffsetComputer.class) // + private long offset; + + private static class OffsetComputer implements RecomputeFieldValue.CustomFieldValueComputer { + @Override + public RecomputeFieldValue.ValueAvailability valueAvailability() { + return RecomputeFieldValue.ValueAvailability.AfterAnalysis; + } + + @Override + public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + Class declaringClass = ReflectionUtil.readField(receiver.getClass(), "declaringClass", receiver); + String name = ReflectionUtil.readField(receiver.getClass(), "name", receiver); + Field field = ReflectionUtil.lookupField(declaringClass, name); + return (long) metaAccess.lookupJavaField(field).getOffset(); + } + + @Override + public Class[] types() { + return new Class[]{long.class}; + } + } +} diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeClassImpl.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeClassImpl.java index cb0945666c70..95238edf6dba 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeClassImpl.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeClassImpl.java @@ -153,7 +153,11 @@ Field[] getAccessedFields() { */ Field[] reflectionFields = new Field[fields.length]; for (int i = 0; i < fields.length; i++) { - reflectionFields[i] = fields[i].field; + try { + reflectionFields[i] = fields[i].declaringClass.getDeclaredField(fields[i].name); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } } return reflectionFields; } @@ -209,12 +213,12 @@ protected Object getFieldValue(Object field, Node receiver) { @Override protected Class getFieldType(Object field) { - return ((NodeFieldData) field).field.getType(); + return ((NodeFieldData) field).type; } @Override protected String getFieldName(Object field) { - return ((NodeFieldData) field).field.getName(); + return ((NodeFieldData) field).name; } @Override @@ -246,13 +250,17 @@ enum NodeFieldKind { static final class NodeFieldData { final NodeFieldKind kind; - final Field field; + final Class type; + final String name; + final Class declaringClass; final long offset; final boolean clonable; NodeFieldData(NodeFieldKind kind, Field field) { this.kind = kind; - this.field = field; + this.type = field.getType(); + this.name = field.getName(); + this.declaringClass = field.getDeclaringClass(); this.offset = UNSAFE.objectFieldOffset(field); this.clonable = kind == NodeFieldKind.DATA && NodeCloneable.class.isAssignableFrom(field.getType()); } @@ -267,7 +275,6 @@ public void putObject(Node receiver, Object value) { } private boolean validateAccess(Node receiver, Object value) { - Class type = field.getType(); if (type.isPrimitive() || !type.isInstance(value)) { throw illegalArgumentException(value); } @@ -289,11 +296,11 @@ private boolean validateAccess(Node receiver, Object value) { } private IllegalArgumentException illegalArgumentException(Object value) { - return new IllegalArgumentException("Cannot set " + field.getType().getName() + " field " + toString() + " to " + (value == null ? "null" : value.getClass().getName())); + return new IllegalArgumentException("Cannot set " + type.getName() + " field " + toString() + " to " + (value == null ? "null" : value.getClass().getName())); } public Object getObject(Node receiver) { - if (!field.getType().isPrimitive()) { + if (!type.isPrimitive()) { return UNSAFE.getObject(receiver, getOffset()); } else { throw new IllegalArgumentException(); @@ -301,7 +308,6 @@ public Object getObject(Node receiver) { } public Object getObjectOrPrimitive(Node node) { - Class type = field.getType(); if (type == boolean.class) { return UNSAFE.getBoolean(node, getOffset()); } else if (type == byte.class) { @@ -341,7 +347,7 @@ private static Unsafe getUnsafe() { @Override public String toString() { - return field.getDeclaringClass().getName() + "." + field.getName(); + return declaringClass.getName() + "." + name; } } }