blob: 5c4c03ff13b8e7fb1b220baa35a2e419ca095375 [file] [log] [blame]
/*
* Copyright (c) 2014, 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 jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.ref.WeakReference;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
/**
* Encapsulates the allocation strategy for a function when used as a constructor.
*/
final public class AllocationStrategy implements Serializable {
private static final long serialVersionUID = 1L;
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
/** Number of fields in the allocated object */
private final int fieldCount;
/** Whether to use dual field representation */
private final boolean dualFields;
/** Name of class where allocator function resides */
private transient String allocatorClassName;
/** lazily generated allocator */
private transient MethodHandle allocator;
/** Last used allocator map */
private transient AllocatorMap lastMap;
/**
* Construct an allocation strategy with the given map and class name.
* @param fieldCount number of fields in the allocated object
* @param dualFields whether to use dual field representation
*/
public AllocationStrategy(final int fieldCount, final boolean dualFields) {
this.fieldCount = fieldCount;
this.dualFields = dualFields;
}
private String getAllocatorClassName() {
if (allocatorClassName == null) {
// These classes get loaded, so an interned variant of their name is most likely around anyway.
allocatorClassName = Compiler.binaryName(ObjectClassGenerator.getClassName(fieldCount, dualFields)).intern();
}
return allocatorClassName;
}
/**
* Get the property map for the allocated object.
* @param prototype the prototype object
* @return the property map
*/
synchronized PropertyMap getAllocatorMap(final ScriptObject prototype) {
assert prototype != null;
final PropertyMap protoMap = prototype.getMap();
if (lastMap != null) {
if (!lastMap.hasSharedProtoMap()) {
if (lastMap.hasSamePrototype(prototype)) {
return lastMap.allocatorMap;
}
if (lastMap.hasSameProtoMap(protoMap) && lastMap.hasUnchangedProtoMap()) {
// Convert to shared prototype map. Allocated objects will use the same property map
// that can be used as long as none of the prototypes modify the shared proto map.
final PropertyMap allocatorMap = PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0);
final SharedPropertyMap sharedProtoMap = new SharedPropertyMap(protoMap);
allocatorMap.setSharedProtoMap(sharedProtoMap);
prototype.setMap(sharedProtoMap);
lastMap = new AllocatorMap(prototype, protoMap, allocatorMap);
return allocatorMap;
}
}
if (lastMap.hasValidSharedProtoMap() && lastMap.hasSameProtoMap(protoMap)) {
prototype.setMap(lastMap.getSharedProtoMap());
return lastMap.allocatorMap;
}
}
final PropertyMap allocatorMap = PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0);
lastMap = new AllocatorMap(prototype, protoMap, allocatorMap);
return allocatorMap;
}
/**
* Allocate an object with the given property map
* @param map the property map
* @return the allocated object
*/
ScriptObject allocate(final PropertyMap map) {
try {
if (allocator == null) {
allocator = MH.findStatic(LOOKUP, Context.forStructureClass(getAllocatorClassName()),
CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
}
return (ScriptObject)allocator.invokeExact(map);
} catch (final RuntimeException | Error e) {
throw e;
} catch (final Throwable t) {
throw new RuntimeException(t);
}
}
@Override
public String toString() {
return "AllocationStrategy[fieldCount=" + fieldCount + "]";
}
static class AllocatorMap {
final private WeakReference<ScriptObject> prototype;
final private WeakReference<PropertyMap> prototypeMap;
private PropertyMap allocatorMap;
AllocatorMap(final ScriptObject prototype, final PropertyMap protoMap, final PropertyMap allocMap) {
this.prototype = new WeakReference<>(prototype);
this.prototypeMap = new WeakReference<>(protoMap);
this.allocatorMap = allocMap;
}
boolean hasSamePrototype(final ScriptObject proto) {
return prototype.get() == proto;
}
boolean hasSameProtoMap(final PropertyMap protoMap) {
return prototypeMap.get() == protoMap || allocatorMap.getSharedProtoMap() == protoMap;
}
boolean hasUnchangedProtoMap() {
final ScriptObject proto = prototype.get();
return proto != null && proto.getMap() == prototypeMap.get();
}
boolean hasSharedProtoMap() {
return getSharedProtoMap() != null;
}
boolean hasValidSharedProtoMap() {
return hasSharedProtoMap() && getSharedProtoMap().isValidSharedProtoMap();
}
PropertyMap getSharedProtoMap() {
return allocatorMap.getSharedProtoMap();
}
}
}