AllocationStrategy.java revision 1786:80120e9b3273
1/*
2 * Copyright (c) 2014, 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 */
25package jdk.nashorn.internal.runtime;
26
27import static jdk.nashorn.internal.lookup.Lookup.MH;
28
29import java.io.Serializable;
30import java.lang.invoke.MethodHandle;
31import java.lang.invoke.MethodHandles;
32import java.lang.ref.WeakReference;
33import jdk.nashorn.internal.codegen.Compiler;
34import jdk.nashorn.internal.codegen.CompilerConstants;
35import jdk.nashorn.internal.codegen.ObjectClassGenerator;
36
37/**
38 * Encapsulates the allocation strategy for a function when used as a constructor.
39 */
40final public class AllocationStrategy implements Serializable {
41    private static final long serialVersionUID = 1L;
42
43    private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
44
45    /** Number of fields in the allocated object */
46    private final int fieldCount;
47
48    /** Whether to use dual field representation */
49    private final boolean dualFields;
50
51    /** Name of class where allocator function resides */
52    private transient String allocatorClassName;
53
54    /** lazily generated allocator */
55    private transient MethodHandle allocator;
56
57    /** Last used allocator map */
58    private transient AllocatorMap lastMap;
59
60    /**
61     * Construct an allocation strategy with the given map and class name.
62     * @param fieldCount number of fields in the allocated object
63     * @param dualFields whether to use dual field representation
64     */
65    public AllocationStrategy(final int fieldCount, final boolean dualFields) {
66        this.fieldCount = fieldCount;
67        this.dualFields = dualFields;
68    }
69
70    private String getAllocatorClassName() {
71        if (allocatorClassName == null) {
72            // These classes get loaded, so an interned variant of their name is most likely around anyway.
73            allocatorClassName = Compiler.binaryName(ObjectClassGenerator.getClassName(fieldCount, dualFields)).intern();
74        }
75        return allocatorClassName;
76    }
77
78    /**
79     * Get the property map for the allocated object.
80     * @param prototype the prototype object
81     * @return the property map
82     */
83    synchronized PropertyMap getAllocatorMap(final ScriptObject prototype) {
84        assert prototype != null;
85        final PropertyMap protoMap = prototype.getMap();
86
87        if (lastMap != null) {
88            if (!lastMap.hasSharedProtoMap()) {
89                if (lastMap.hasSamePrototype(prototype)) {
90                    return lastMap.allocatorMap;
91                }
92                if (lastMap.hasSameProtoMap(protoMap) && lastMap.hasUnchangedProtoMap()) {
93                    // Convert to shared prototype map. Allocated objects will use the same property map
94                    // that can be used as long as none of the prototypes modify the shared proto map.
95                    final PropertyMap allocatorMap = PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0);
96                    final SharedPropertyMap sharedProtoMap = new SharedPropertyMap(protoMap);
97                    allocatorMap.setSharedProtoMap(sharedProtoMap);
98                    prototype.setMap(sharedProtoMap);
99                    lastMap = new AllocatorMap(prototype, protoMap, allocatorMap);
100                    return allocatorMap;
101                }
102            }
103
104            if (lastMap.hasValidSharedProtoMap() && lastMap.hasSameProtoMap(protoMap)) {
105                prototype.setMap(lastMap.getSharedProtoMap());
106                return lastMap.allocatorMap;
107            }
108        }
109
110        final PropertyMap allocatorMap = PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0);
111        lastMap = new AllocatorMap(prototype, protoMap, allocatorMap);
112
113        return allocatorMap;
114    }
115
116    /**
117     * Allocate an object with the given property map
118     * @param map the property map
119     * @return the allocated object
120     */
121    ScriptObject allocate(final PropertyMap map) {
122        try {
123            if (allocator == null) {
124                allocator = MH.findStatic(LOOKUP, Context.forStructureClass(getAllocatorClassName()),
125                        CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
126            }
127            return (ScriptObject)allocator.invokeExact(map);
128        } catch (final RuntimeException | Error e) {
129            throw e;
130        } catch (final Throwable t) {
131            throw new RuntimeException(t);
132        }
133    }
134
135    @Override
136    public String toString() {
137        return "AllocationStrategy[fieldCount=" + fieldCount + "]";
138    }
139
140    static class AllocatorMap {
141        final private WeakReference<ScriptObject> prototype;
142        final private WeakReference<PropertyMap> prototypeMap;
143
144        private final PropertyMap allocatorMap;
145
146        AllocatorMap(final ScriptObject prototype, final PropertyMap protoMap, final PropertyMap allocMap) {
147            this.prototype = new WeakReference<>(prototype);
148            this.prototypeMap = new WeakReference<>(protoMap);
149            this.allocatorMap = allocMap;
150        }
151
152        boolean hasSamePrototype(final ScriptObject proto) {
153            return prototype.get() == proto;
154        }
155
156        boolean hasSameProtoMap(final PropertyMap protoMap) {
157            return prototypeMap.get() == protoMap || allocatorMap.getSharedProtoMap() == protoMap;
158        }
159
160        boolean hasUnchangedProtoMap() {
161            final ScriptObject proto = prototype.get();
162            return proto != null && proto.getMap() == prototypeMap.get();
163        }
164
165        boolean hasSharedProtoMap() {
166            return getSharedProtoMap() != null;
167        }
168
169        boolean hasValidSharedProtoMap() {
170            return hasSharedProtoMap() && getSharedProtoMap().isValidSharedProtoMap();
171        }
172
173        PropertyMap getSharedProtoMap() {
174            return allocatorMap.getSharedProtoMap();
175        }
176
177    }
178}
179