提交 b642e31e 编写于 作者: H hannesw

8134609: Allow constructors with same prototoype map to share the allocator map

Reviewed-by: attila, sundar
上级 92853b17
......@@ -627,7 +627,7 @@ public final class NativeJSAdapter extends ScriptObject {
return new GuardedInvocation(MH.dropArguments(MH.constant(Object.class,
func.createBound(this, new Object[] { name })), 0, Object.class),
testJSAdaptor(adaptee, null, null, null),
adaptee.getProtoSwitchPoint(__call__, find.getOwner()));
adaptee.getProtoSwitchPoints(__call__, find.getOwner()), null);
}
}
throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this));
......@@ -698,7 +698,7 @@ public final class NativeJSAdapter extends ScriptObject {
return new GuardedInvocation(
methodHandle,
testJSAdaptor(adaptee, findData.getGetter(Object.class, INVALID_PROGRAM_POINT, null), findData.getOwner(), func),
adaptee.getProtoSwitchPoint(hook, findData.getOwner()));
adaptee.getProtoSwitchPoints(hook, findData.getOwner()), null);
}
}
}
......@@ -710,7 +710,7 @@ public final class NativeJSAdapter extends ScriptObject {
final MethodHandle methodHandle = hook.equals(__put__) ?
MH.asType(Lookup.EMPTY_SETTER, type) :
Lookup.emptyGetter(type.returnType());
return new GuardedInvocation(methodHandle, testJSAdaptor(adaptee, null, null, null), adaptee.getProtoSwitchPoint(hook, null));
return new GuardedInvocation(methodHandle, testJSAdaptor(adaptee, null, null, null), adaptee.getProtoSwitchPoints(hook, null), null);
}
}
......
......@@ -29,6 +29,7 @@ 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;
......@@ -53,6 +54,9 @@ final public class AllocationStrategy implements Serializable {
/** 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
......@@ -71,11 +75,49 @@ final public class AllocationStrategy implements Serializable {
return allocatorClassName;
}
PropertyMap getAllocatorMap() {
// Create a new map for each function instance
return PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0);
/**
* 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) {
......@@ -94,4 +136,43 @@ final public class AllocationStrategy implements Serializable {
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();
}
}
}
......@@ -26,6 +26,8 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.parser.TokenType.EOF;
import jdk.nashorn.api.scripting.NashornException;
import jdk.nashorn.internal.parser.Lexer;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenStream;
......@@ -62,6 +64,15 @@ public final class Debug {
return firstJSFrame(new Throwable());
}
/**
* Return a formatted script stack trace string with frames information separated by '\n'.
* This is a shortcut for {@code NashornException.getScriptStackString(new Throwable())}.
* @return formatted stack trace string
*/
public static String scriptStack() {
return NashornException.getScriptStackString(new Throwable());
}
/**
* Return the system identity hashcode for an object as a human readable
* string
......
......@@ -75,16 +75,20 @@ public class PropertyListeners {
}
/**
* Return listeners added to this ScriptObject.
* Return number of listeners added to a ScriptObject.
* @param obj the object
* @return the listener count
*/
public static int getListenerCount(final ScriptObject obj) {
final PropertyListeners propertyListeners = obj.getMap().getListeners();
if (propertyListeners != null) {
return propertyListeners.listeners == null ? 0 : propertyListeners.listeners.size();
}
return 0;
return obj.getMap().getListenerCount();
}
/**
* Return the number of listeners added to this PropertyListeners instance.
* @return the listener count;
*/
public int getListenerCount() {
return listeners == null ? 0 : listeners.size();
}
// Property listener management methods
......@@ -156,7 +160,7 @@ public class PropertyListeners {
final WeakPropertyMapSet set = listeners.get(prop.getKey());
if (set != null) {
for (final PropertyMap propertyMap : set.elements()) {
propertyMap.propertyAdded(prop);
propertyMap.propertyAdded(prop, false);
}
listeners.remove(prop.getKey());
if (Context.DEBUG) {
......@@ -176,7 +180,7 @@ public class PropertyListeners {
final WeakPropertyMapSet set = listeners.get(prop.getKey());
if (set != null) {
for (final PropertyMap propertyMap : set.elements()) {
propertyMap.propertyDeleted(prop);
propertyMap.propertyDeleted(prop, false);
}
listeners.remove(prop.getKey());
if (Context.DEBUG) {
......@@ -198,7 +202,7 @@ public class PropertyListeners {
final WeakPropertyMapSet set = listeners.get(oldProp.getKey());
if (set != null) {
for (final PropertyMap propertyMap : set.elements()) {
propertyMap.propertyModified(oldProp, newProp);
propertyMap.propertyModified(oldProp, newProp, false);
}
listeners.remove(oldProp.getKey());
if (Context.DEBUG) {
......@@ -215,7 +219,7 @@ public class PropertyListeners {
if (listeners != null) {
for (final WeakPropertyMapSet set : listeners.values()) {
for (final PropertyMap propertyMap : set.elements()) {
propertyMap.protoChanged();
propertyMap.protoChanged(false);
}
}
listeners.clear();
......
......@@ -368,8 +368,8 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
}
@Override
PropertyMap getAllocatorMap() {
return allocationStrategy.getAllocatorMap();
PropertyMap getAllocatorMap(final ScriptObject prototype) {
return allocationStrategy.getAllocatorMap(prototype);
}
@Override
......
......@@ -521,35 +521,39 @@ public class ScriptFunction extends ScriptObject {
assert !isBoundFunction(); // allocate never invoked on bound functions
final ScriptObject object = data.allocate(getAllocatorMap());
final ScriptObject prototype = getAllocatorPrototype();
final ScriptObject object = data.allocate(getAllocatorMap(prototype));
if (object != null) {
final Object prototype = getPrototype();
if (prototype instanceof ScriptObject) {
object.setInitialProto((ScriptObject) prototype);
}
if (object.getProto() == null) {
object.setInitialProto(getObjectPrototype());
}
object.setInitialProto(prototype);
}
return object;
}
private PropertyMap getAllocatorMap() {
if (allocatorMap == null) {
allocatorMap = data.getAllocatorMap();
/**
* Get the property map used by "allocate"
* @param prototype actual prototype object
* @return property map
*/
private synchronized PropertyMap getAllocatorMap(final ScriptObject prototype) {
if (allocatorMap == null || allocatorMap.isInvalidSharedMapFor(prototype)) {
// The prototype map has changed since this function was last used as constructor.
// Get a new allocator map.
allocatorMap = data.getAllocatorMap(prototype);
}
return allocatorMap;
}
/**
* Return Object.prototype - used by "allocate"
*
* @return Object.prototype
* Return the actual prototype used by "allocate"
* @return allocator prototype
*/
protected final ScriptObject getObjectPrototype() {
private ScriptObject getAllocatorPrototype() {
final Object prototype = getPrototype();
if (prototype instanceof ScriptObject) {
return (ScriptObject) prototype;
}
return Global.objectPrototype();
}
......@@ -591,10 +595,10 @@ public class ScriptFunction extends ScriptObject {
*
* @param newPrototype new prototype object
*/
public final void setPrototype(final Object newPrototype) {
public synchronized final void setPrototype(final Object newPrototype) {
if (newPrototype instanceof ScriptObject && newPrototype != this.prototype && allocatorMap != null) {
// Replace our current allocator map with one that is associated with the new prototype.
allocatorMap = allocatorMap.changeProto((ScriptObject) newPrototype);
// Unset allocator map to be replaced with one matching the new prototype.
allocatorMap = null;
}
this.prototype = newPrototype;
}
......
......@@ -389,9 +389,10 @@ public abstract class ScriptFunctionData implements Serializable {
/**
* Get the property map to use for objects allocated by this function.
*
* @param prototype the prototype of the allocated object
* @return the property map for allocated objects.
*/
PropertyMap getAllocatorMap() {
PropertyMap getAllocatorMap(final ScriptObject prototype) {
return null;
}
......
......@@ -809,9 +809,11 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
if (deep) {
final ScriptObject myProto = getProto();
if (myProto != null) {
return myProto.findProperty(key, deep, start);
}
final FindProperty find = myProto == null ? null : myProto.findProperty(key, true, start);
// checkSharedProtoMap must be invoked after myProto.checkSharedProtoMap to propagate
// shared proto invalidation up the prototype chain. It also must be invoked when prototype is null.
checkSharedProtoMap();
return find;
}
return null;
......@@ -832,7 +834,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
if (deep) {
final ScriptObject myProto = getProto();
if (myProto != null) {
return myProto.hasProperty(key, deep);
return myProto.hasProperty(key, true);
}
}
......@@ -1258,11 +1260,8 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
if (oldProto != newProto) {
proto = newProto;
// Let current listeners know that the prototype has changed and set our map
final PropertyListeners listeners = getMap().getListeners();
if (listeners != null) {
listeners.protoChanged();
}
// Let current listeners know that the prototype has changed
getMap().protoChanged(true);
// Replace our current allocator map with one that is associated with the new prototype.
setMap(getMap().changeProto(newProto));
}
......@@ -1314,7 +1313,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
}
p = p.getProto();
}
setProto((ScriptObject)newProto);
setProto((ScriptObject) newProto);
} else {
throw typeError("cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
}
......@@ -2012,11 +2011,11 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
final ScriptObject owner = find.getOwner();
final Class<ClassCastException> exception = explicitInstanceOfCheck ? null : ClassCastException.class;
final SwitchPoint protoSwitchPoint;
final SwitchPoint[] protoSwitchPoints;
if (mh == null) {
mh = Lookup.emptyGetter(returnType);
protoSwitchPoint = getProtoSwitchPoint(name, owner);
protoSwitchPoints = getProtoSwitchPoints(name, owner);
} else if (!find.isSelf()) {
assert mh.type().returnType().equals(returnType) :
"return type mismatch for getter " + mh.type().returnType() + " != " + returnType;
......@@ -2024,12 +2023,12 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
// Add a filter that replaces the self object with the prototype owning the property.
mh = addProtoFilter(mh, find.getProtoChainLength());
}
protoSwitchPoint = getProtoSwitchPoint(name, owner);
protoSwitchPoints = getProtoSwitchPoints(name, owner);
} else {
protoSwitchPoint = null;
protoSwitchPoints = null;
}
final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception);
final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoints, exception);
return inv.addSwitchPoint(findBuiltinSwitchPoint(name));
}
......@@ -2128,17 +2127,32 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @param owner the property owner, null if property is not defined
* @return a SwitchPoint or null
*/
public final SwitchPoint getProtoSwitchPoint(final String name, final ScriptObject owner) {
public final SwitchPoint[] getProtoSwitchPoints(final String name, final ScriptObject owner) {
if (owner == this || getProto() == null) {
return null;
}
final List<SwitchPoint> switchPoints = new ArrayList<>();
for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) {
final ScriptObject parent = obj.getProto();
parent.getMap().addListener(name, obj.getMap());
final SwitchPoint sp = parent.getMap().getSharedProtoSwitchPoint();
if (sp != null && !sp.hasBeenInvalidated()) {
switchPoints.add(sp);
}
}
return getMap().getSwitchPoint(name);
switchPoints.add(getMap().getSwitchPoint(name));
return switchPoints.toArray(new SwitchPoint[switchPoints.size()]);
}
private void checkSharedProtoMap() {
// Check if our map has an expected shared prototype property map. If it has, make sure that
// the prototype map has not been invalidated, and that it does match the actual map of the prototype.
if (getMap().isInvalidSharedMapFor(getProto())) {
// Change our own map to one that does not assume a shared prototype map.
setMap(getMap().makeUnsharedCopy());
}
}
/**
......@@ -2220,7 +2234,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return new GuardedInvocation(
Lookup.EMPTY_SETTER,
NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck),
getProtoSwitchPoint(name, null),
getProtoSwitchPoints(name, null),
explicitInstanceOfCheck ? null : ClassCastException.class);
}
......@@ -2366,7 +2380,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
find.getGetter(Object.class, INVALID_PROGRAM_POINT, request),
find.getProtoChainLength(),
func),
getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()),
getProtoSwitchPoints(NO_SUCH_PROPERTY_NAME, find.getOwner()),
//TODO this doesn't need a ClassCastException as guard always checks script object
null);
}
......@@ -2438,7 +2452,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
}
return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()),
NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoint(name, null),
NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoints(name, null),
explicitInstanceOfCheck ? null : ClassCastException.class);
}
......
......@@ -186,10 +186,7 @@ final class SetMethodCreator {
private SetMethod createNewPropertySetter(final SwitchPoint builtinSwitchPoint) {
final SetMethod sm = map.getFreeFieldSlot() > -1 ? createNewFieldSetter(builtinSwitchPoint) : createNewSpillPropertySetter(builtinSwitchPoint);
final PropertyListeners listeners = map.getListeners();
if (listeners != null) {
listeners.propertyAdded(sm.property);
}
map.propertyAdded(sm.property, true);
return sm;
}
......@@ -204,7 +201,7 @@ final class SetMethodCreator {
//fast type specific setter
final MethodHandle fastSetter = property.getSetter(type, newMap); //0 sobj, 1 value, slot folded for spill property already
//slow setter, that calls ScriptObject.set with appropraite type and key name
//slow setter, that calls ScriptObject.set with appropriate type and key name
MethodHandle slowSetter = ScriptObject.SET_SLOW[getAccessorTypeIndex(type)];
slowSetter = MH.insertArguments(slowSetter, 3, NashornCallSiteDescriptor.getFlags(desc));
slowSetter = MH.insertArguments(slowSetter, 1, name);
......
/*
* Copyright (c) 2015, 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 java.lang.invoke.SwitchPoint;
/**
* This class represents a property map that can be shared among multiple prototype objects, allowing all inheriting
* top-level objects to also share one property map. This is class is only used for prototype objects, the
* top-level objects use ordinary {@link PropertyMap}s with the {@link PropertyMap#sharedProtoMap} field
* set to the expected shared prototype map.
*
* <p>When an instance of this class is evolved because a property is added, removed, or modified in an object
* using it, the {@link #invalidateSwitchPoint()} method is invoked to signal to all callsites and inheriting
* objects that the assumption of a single shared prototype map is no longer valid. The property map resulting
* from the modification will no longer be an instance of this class.</p>
*/
public final class SharedPropertyMap extends PropertyMap {
private SwitchPoint switchPoint;
private static final long serialVersionUID = 2166297719721778876L;
/**
* Create a new shared property map from the given {@code map}.
* @param map property map to copy
*/
public SharedPropertyMap(final PropertyMap map) {
super(map);
this.switchPoint = new SwitchPoint();
}
@Override
public void propertyAdded(final Property property, final boolean isSelf) {
if (isSelf) {
invalidateSwitchPoint();
}
super.propertyAdded(property, isSelf);
}
@Override
public void propertyDeleted(final Property property, final boolean isSelf) {
if (isSelf) {
invalidateSwitchPoint();
}
super.propertyDeleted(property, isSelf);
}
@Override
public void propertyModified(final Property oldProperty, final Property newProperty, final boolean isSelf) {
if (isSelf) {
invalidateSwitchPoint();
}
super.propertyModified(oldProperty, newProperty, isSelf);
}
@Override
synchronized boolean isValidSharedProtoMap() {
return switchPoint != null;
}
@Override
synchronized SwitchPoint getSharedProtoSwitchPoint() {
return switchPoint;
}
/**
* Invalidate the shared prototype switch point if this is a shared prototype map.
*/
synchronized void invalidateSwitchPoint() {
if (switchPoint != null) {
assert !switchPoint.hasBeenInvalidated();
SwitchPoint.invalidateAll(new SwitchPoint[]{ switchPoint });
switchPoint = null;
}
}
}
......@@ -46,7 +46,7 @@ import jdk.nashorn.internal.runtime.linker.NashornGuards;
*
*/
public final class WithObject extends Scope {
private static final MethodHandle WITHEXPRESSIONGUARD = findOwnMH("withExpressionGuard", boolean.class, Object.class, PropertyMap.class, SwitchPoint.class);
private static final MethodHandle WITHEXPRESSIONGUARD = findOwnMH("withExpressionGuard", boolean.class, Object.class, PropertyMap.class, SwitchPoint[].class);
private static final MethodHandle WITHEXPRESSIONFILTER = findOwnMH("withFilterExpression", Object.class, Object.class);
private static final MethodHandle WITHSCOPEFILTER = findOwnMH("withFilterScope", Object.class, Object.class);
private static final MethodHandle BIND_TO_EXPRESSION_OBJ = findOwnMH("bindToExpression", Object.class, Object.class, Object.class);
......@@ -360,13 +360,24 @@ public final class WithObject extends Scope {
private MethodHandle expressionGuard(final String name, final ScriptObject owner) {
final PropertyMap map = expression.getMap();
final SwitchPoint sp = expression.getProtoSwitchPoint(name, owner);
final SwitchPoint[] sp = expression.getProtoSwitchPoints(name, owner);
return MH.insertArguments(WITHEXPRESSIONGUARD, 1, map, sp);
}
@SuppressWarnings("unused")
private static boolean withExpressionGuard(final Object receiver, final PropertyMap map, final SwitchPoint sp) {
return ((WithObject)receiver).expression.getMap() == map && (sp == null || !sp.hasBeenInvalidated());
private static boolean withExpressionGuard(final Object receiver, final PropertyMap map, final SwitchPoint[] sp) {
return ((WithObject)receiver).expression.getMap() == map && !hasBeenInvalidated(sp);
}
private static boolean hasBeenInvalidated(final SwitchPoint[] switchPoints) {
if (switchPoints != null) {
for (final SwitchPoint switchPoint : switchPoints) {
if (switchPoint.hasBeenInvalidated()) {
return true;
}
}
}
return false;
}
/**
......
......@@ -62,67 +62,146 @@ function createDeep() {
return new C();
}
function createDeeper() {
function C() {
this.i1 = 1;
this.i2 = 2;
this.i3 = 3;
return this;
}
function D() {
this.p1 = 1;
this.p2 = 2;
this.p3 = 3;
return this;
}
function E() {
this.e1 = 1;
this.e2 = 2;
this.e3 = 3;
return this;
}
D.prototype = new E();
C.prototype = new D();
return new C();
}
function createEval() {
return eval("Object.create({})");
}
function p(o) { print(o.x) }
var a, b;
function e(o) { print(o.e1) }
var a, b, c;
create();
a = create();
b = create();
c = create();
a.__proto__.x = 123;
p(a);
p(b);
p(c);
a = create();
b = create();
c = create();
b.__proto__.x = 123;
p(a);
p(b);
p(c);
a = createEmpty();
b = createEmpty();
c = createEmpty();
a.__proto__.x = 123;
p(a);
p(b);
p(c);
a = createEmpty();
b = createEmpty();
c = createEmpty();
b.__proto__.x = 123;
p(a);
p(b);
p(c);
a = createDeep();
b = createDeep();
c = createDeep();
a.__proto__.__proto__.x = 123;
p(a);
p(b);
p(c);
a = createDeep();
b = createDeep();
c = createDeep();
b.__proto__.__proto__.x = 123;
p(a);
p(b);
p(c);
a = createDeeper();
b = createDeeper();
c = createDeeper();
a.__proto__.__proto__.__proto__.x = 123;
p(a);
p(b);
p(c);
a = createDeeper();
b = createDeeper();
c = createDeeper();
b.__proto__.__proto__.__proto__.x = 123;
p(a);
p(b);
p(c);
a = createDeeper();
b = createDeeper();
c = createDeeper();
a.__proto__.__proto__ = null;
e(a);
e(b);
e(c);
a = createDeeper();
b = createDeeper();
c = createDeeper();
b.__proto__.__proto__ = null;
e(a);
e(b);
e(c);
a = createEval();
b = createEval();
c = createEval();
a.__proto__.x = 123;
p(a);
p(b);
p(c);
a = createEval();
b = createEval();
c = createEval();
b.__proto__.x = 123;
p(a);
p(b);
p(c);
123
undefined
undefined
undefined
123
undefined
123
undefined
undefined
undefined
123
undefined
123
undefined
undefined
undefined
123
undefined
123
undefined
undefined
undefined
123
undefined
undefined
1
1
1
undefined
1
123
undefined
undefined
undefined
123
undefined
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/**
* JDK-8134609: Allow constructors with same prototoype map to share the allocator map
*
* @test
* @run
* @fork
* @option -Dnashorn.debug
*/
function createProto(members) {
function P() {
for (var id in members) {
if (members.hasOwnProperty(id)) {
this[id] = members[id];
}
}
return this;
}
return new P();
}
function createSubclass(prototype, members) {
function C() {
for (var id in members) {
if (members.hasOwnProperty(id)) {
this[id] = members[id];
}
}
return this;
}
C.prototype = prototype;
return new C();
}
function assertP1(object, value) {
Assert.assertTrue(object.p1 === value);
}
// First prototype will have non-shared proto-map. Second and third will be shared.
var proto0 = createProto({p1: 0, p2: 1});
var proto1 = createProto({p1: 1, p2: 2});
var proto2 = createProto({p1: 2, p2: 3});
Assert.assertTrue(Debug.map(proto1) === Debug.map(proto2));
assertP1(proto1, 1);
assertP1(proto2, 2);
// First instantiation will have a non-shared prototype map, from the second one
// maps will be shared until a different proto map comes along.
var child0 = createSubclass(proto1, {c1: 1, c2: 2});
var child1 = createSubclass(proto2, {c1: 2, c2: 3});
var child2 = createSubclass(proto1, {c1: 3, c2: 4});
var child3 = createSubclass(proto2, {c1: 1, c2: 2});
var child4 = createSubclass(proto0, {c1: 3, c2: 2});
Assert.assertTrue(Debug.map(child1) === Debug.map(child2));
Assert.assertTrue(Debug.map(child1) === Debug.map(child3));
Assert.assertTrue(Debug.map(child3) !== Debug.map(child4));
assertP1(child1, 2);
assertP1(child2, 1);
assertP1(child3, 2);
assertP1(child4, 0);
Assert.assertTrue(delete proto2.p1);
assertP1(child3, undefined);
assertP1(child2, 1);
Assert.assertTrue(Debug.map(child1) !== Debug.map(child3));
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册