/*
 * 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.
/** * 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
/** Number of fields in the allocated object */
private final int fieldCount;
Whether to use dual field representation
/** Whether to use dual field representation */
private final boolean dualFields;
Name of class where allocator function resides
/** Name of class where allocator function resides */
private transient String allocatorClassName;
lazily generated allocator
/** lazily generated allocator */
private transient MethodHandle allocator;
Last used allocator map
/** Last used allocator map */
private transient AllocatorMap lastMap;
Construct an allocation strategy with the given map and class name.
Params:
  • fieldCount – number of fields in the allocated object
  • dualFields – whether to use dual field representation
/** * 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.
Params:
  • prototype – the prototype object
Returns:the property map
/** * 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
Params:
  • map – the property map
Returns:the allocated object
/** * 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 final 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(); } } }