提交 0e330311 编写于 作者: H hannesw

8017084: Use spill properties for large object literals

Reviewed-by: lagergren, sundar
上级 874a8be8
......@@ -25,6 +25,7 @@
package jdk.nashorn.internal.tools.nasgen;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
......@@ -164,7 +165,6 @@ public class ClassGenerator {
mi.visitCode();
mi.pushNull();
mi.putStatic(className, MAP_FIELD_NAME, MAP_DESC);
mi.loadClass(className);
mi.invokeStatic(MAP_TYPE, MAP_NEWMAP, MAP_NEWMAP_DESC);
// stack: PropertyMap
}
......@@ -236,7 +236,7 @@ public class ClassGenerator {
static void addMapField(final ClassVisitor cv) {
// add a MAP static field
final FieldVisitor fv = cv.visitField(ACC_PRIVATE | ACC_STATIC,
final FieldVisitor fv = cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL,
MAP_FIELD_NAME, MAP_DESC, null, null);
if (fv != null) {
fv.visitEnd();
......
......@@ -96,12 +96,10 @@ public interface StringConstants {
static final String MAP_TYPE = TYPE_PROPERTYMAP.getInternalName();
static final String MAP_DESC = TYPE_PROPERTYMAP.getDescriptor();
static final String MAP_NEWMAP = "newMap";
static final String MAP_NEWMAP_DESC = Type.getMethodDescriptor(TYPE_PROPERTYMAP, TYPE_CLASS);
static final String MAP_NEWMAP_DESC = Type.getMethodDescriptor(TYPE_PROPERTYMAP);
static final String MAP_DUPLICATE = "duplicate";
static final String MAP_DUPLICATE_DESC = Type.getMethodDescriptor(TYPE_PROPERTYMAP);
static final String MAP_SETFLAGS = "setFlags";
static final String LOOKUP_TYPE = TYPE_LOOKUP.getInternalName();
static final String LOOKUP_GETMETHOD = "getMethod";
static final String LOOKUP_NEWPROPERTY = "newProperty";
static final String LOOKUP_NEWPROPERTY_DESC =
Type.getMethodDescriptor(TYPE_PROPERTYMAP, TYPE_PROPERTYMAP, TYPE_STRING, Type.INT_TYPE, TYPE_METHODHANDLE, TYPE_METHODHANDLE);
......
......@@ -179,6 +179,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
private static final DebugLogger LOG = new DebugLogger("codegen", "nashorn.codegen.debug");
/** From what size should we use spill instead of fields for JavaScript objects? */
private static final int OBJECT_SPILL_THRESHOLD = 300;
/**
* Constructor.
......@@ -942,7 +944,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
* Create a new object based on the symbols and values, generate
* bootstrap code for object
*/
final FieldObjectCreator<Symbol> foc = new FieldObjectCreator<Symbol>(this, nameList, newSymbols, values, true, hasArguments) {
new FieldObjectCreator<Symbol>(this, nameList, newSymbols, values, true, hasArguments) {
@Override
protected void loadValue(final Symbol value) {
method.load(value);
......@@ -956,8 +958,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
m.loadNull();
}
}
};
foc.makeObject(method);
}.makeObject(method);
// runScript(): merge scope into global
if (isFunctionBody && function.isProgram()) {
......@@ -1320,7 +1321,6 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
return method;
}
@SuppressWarnings("rawtypes")
@Override
public boolean enterLiteralNode(final LiteralNode literalNode) {
assert literalNode.getSymbol() != null : literalNode + " has no symbol";
......@@ -1352,73 +1352,71 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
values.add(value);
}
new FieldObjectCreator<Node>(this, keys, symbols, values) {
@Override
protected void loadValue(final Node node) {
load(node);
}
if (elements.size() > OBJECT_SPILL_THRESHOLD) {
new SpillObjectCreator(this, keys, symbols, values).makeObject(method);
} else {
new FieldObjectCreator<Node>(this, keys, symbols, values) {
@Override
protected void loadValue(final Node node) {
load(node);
}
/**
* Ensure that the properties start out as object types so that
* we can do putfield initializations instead of dynamicSetIndex
* which would be the case to determine initial property type
* otherwise.
*
* Use case, it's very expensive to do a million var x = {a:obj, b:obj}
* just to have to invalidate them immediately on initialization
*
* see NASHORN-594
*/
@Override
protected MapCreator newMapCreator(final Class<?> fieldObjectClass) {
return new MapCreator(fieldObjectClass, keys, symbols) {
@Override
protected int getPropertyFlags(final Symbol symbol, final boolean isVarArg) {
return super.getPropertyFlags(symbol, isVarArg) | Property.IS_ALWAYS_OBJECT;
}
};
}
/**
* Ensure that the properties start out as object types so that
* we can do putfield initializations instead of dynamicSetIndex
* which would be the case to determine initial property type
* otherwise.
*
* Use case, it's very expensive to do a million var x = {a:obj, b:obj}
* just to have to invalidate them immediately on initialization
*
* see NASHORN-594
*/
@Override
protected MapCreator newMapCreator(final Class<?> fieldObjectClass) {
return new MapCreator(fieldObjectClass, keys, symbols) {
@Override
protected int getPropertyFlags(final Symbol symbol, final boolean hasArguments) {
return super.getPropertyFlags(symbol, hasArguments) | Property.IS_ALWAYS_OBJECT;
}
};
}
}.makeObject(method);
}.makeObject(method);
}
method.dup();
globalObjectPrototype();
method.invoke(ScriptObject.SET_PROTO);
if (!hasGettersSetters) {
method.store(objectNode.getSymbol());
return false;
}
if (hasGettersSetters) {
for (final PropertyNode propertyNode : elements) {
final FunctionNode getter = propertyNode.getGetter();
final FunctionNode setter = propertyNode.getSetter();
for (final Node element : elements) {
final PropertyNode propertyNode = (PropertyNode)element;
final Object key = propertyNode.getKey();
final FunctionNode getter = propertyNode.getGetter();
final FunctionNode setter = propertyNode.getSetter();
if (getter == null && setter == null) {
continue;
}
if (getter == null && setter == null) {
continue;
}
method.dup().loadKey(propertyNode.getKey());
method.dup().loadKey(key);
if (getter == null) {
method.loadNull();
} else {
getter.accept(this);
}
if (getter == null) {
method.loadNull();
} else {
getter.accept(this);
}
if (setter == null) {
method.loadNull();
} else {
setter.accept(this);
}
if (setter == null) {
method.loadNull();
} else {
setter.accept(this);
method.invoke(ScriptObject.SET_USER_ACCESSORS);
}
method.invoke(ScriptObject.SET_USER_ACCESSORS);
}
method.store(objectNode.getSymbol());
return false;
}
......@@ -3183,24 +3181,21 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
return;
}
final boolean isLazy = functionNode.isLazy();
new ObjectCreator(this, new ArrayList<String>(), new ArrayList<Symbol>(), false, false) {
@Override
protected void makeObject(final MethodEmitter m) {
final String className = SCRIPTFUNCTION_IMPL_OBJECT;
// Generate the object class and property map in case this function is ever used as constructor
final String className = SCRIPTFUNCTION_IMPL_OBJECT;
final int fieldCount = ObjectClassGenerator.getPaddedFieldCount(functionNode.countThisProperties());
final String allocatorClassName = Compiler.binaryName(ObjectClassGenerator.getClassName(fieldCount));
final PropertyMap allocatorMap = PropertyMap.newMap(null, 0, fieldCount, 0);
m._new(className).dup();
loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), Compiler.binaryName(getClassName()), makeMap()));
method._new(className).dup();
loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), allocatorClassName, allocatorMap));
if (isLazy || functionNode.needsParentScope()) {
m.loadCompilerConstant(SCOPE);
} else {
m.loadNull();
}
m.invoke(constructorNoLookup(className, RecompilableScriptFunctionData.class, ScriptObject.class));
}
}.makeObject(method);
if (functionNode.isLazy() || functionNode.needsParentScope()) {
method.loadCompilerConstant(SCOPE);
} else {
method.loadNull();
}
method.invoke(constructorNoLookup(className, RecompilableScriptFunctionData.class, ScriptObject.class));
}
// calls on Global class.
......
......@@ -26,15 +26,16 @@
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getPaddedFieldCount;
import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
import java.util.Iterator;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
......@@ -48,6 +49,13 @@ import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
* @see jdk.nashorn.internal.ir.Node
*/
public abstract class FieldObjectCreator<T> extends ObjectCreator {
private String fieldObjectClassName;
private Class<?> fieldObjectClass;
private int fieldCount;
private int paddedFieldCount;
private int paramCount;
/** array of corresponding values to symbols (null for no values) */
private final List<T> values;
......@@ -80,14 +88,9 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
super(codegen, keys, symbols, isScope, hasArguments);
this.values = values;
this.callSiteFlags = codegen.getCallSiteFlags();
}
/**
* Loads the scope on the stack through the passed method emitter.
* @param method the method emitter to use
*/
protected void loadScope(final MethodEmitter method) {
method.loadCompilerConstant(SCOPE);
countFields();
findClass();
}
/**
......@@ -137,6 +140,13 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
}
}
@Override
protected PropertyMap makeMap() {
assert propertyMap == null : "property map already initialized";
propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), fieldCount, paddedFieldCount);
return propertyMap;
}
/**
* Technique for loading an initial value. Defined by anonymous subclasses in code gen.
*
......@@ -173,4 +183,47 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
loadValue(value);
method.dynamicSetIndex(callSiteFlags);
}
/**
* Locate (or indirectly create) the object container class.
*/
private void findClass() {
fieldObjectClassName = isScope() ?
ObjectClassGenerator.getClassName(fieldCount, paramCount) :
ObjectClassGenerator.getClassName(paddedFieldCount);
try {
this.fieldObjectClass = Context.forStructureClass(Compiler.binaryName(fieldObjectClassName));
} catch (final ClassNotFoundException e) {
throw new AssertionError("Nashorn has encountered an internal error. Structure can not be created.");
}
}
/**
* Get the class name for the object class,
* e.g. {@code com.nashorn.oracle.scripts.JO2P0}
*
* @return script class name
*/
String getClassName() {
return fieldObjectClassName;
}
/**
* Tally the number of fields and parameters.
*/
private void countFields() {
for (final Symbol symbol : this.symbols) {
if (symbol != null) {
if (hasArguments() && symbol.isParam()) {
symbol.setFieldIndex(paramCount++);
} else {
symbol.setFieldIndex(fieldCount++);
}
}
}
paddedFieldCount = getPaddedFieldCount(fieldCount);
}
}
......@@ -175,6 +175,14 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> {
if (destType == null) {
destType = specBinaryNode.getType();
}
// Register assignments to this object in case this is used as constructor
if (binaryNode.lhs() instanceof AccessNode) {
AccessNode accessNode = (AccessNode) binaryNode.lhs();
if (accessNode.getBase().getSymbol().isThis()) {
lc.getCurrentFunction().addThisProperty(accessNode.getProperty().getName());
}
}
return specBinaryNode.setRHS(convert(specBinaryNode.rhs(), destType));
}
......
......@@ -41,10 +41,10 @@ public class MapCreator {
private final Class<?> structure;
/** key set for object map */
private final String[] keys;
final List<String> keys;
/** corresponding symbol set for object map */
private final Symbol[] symbols;
final List<Symbol> symbols;
/**
* Constructor
......@@ -54,11 +54,9 @@ public class MapCreator {
* @param symbols list of symbols for map
*/
MapCreator(final Class<?> structure, final List<String> keys, final List<Symbol> symbols) {
final int size = keys.size();
this.structure = structure;
this.keys = keys.toArray(new String[size]);
this.symbols = symbols.toArray(new Symbol[size]);
this.keys = keys;
this.symbols = symbols;
}
/**
......@@ -70,21 +68,37 @@ public class MapCreator {
*
* @return New map populated with accessor properties.
*/
PropertyMap makeMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum) {
PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum) {
final List<Property> properties = new ArrayList<>();
assert keys != null;
for (int i = 0; i < keys.length; i++) {
final String key = keys[i];
final Symbol symbol = symbols[i];
for (int i = 0, length = keys.size(); i < length; i++) {
final String key = keys.get(i);
final Symbol symbol = symbols.get(i);
if (symbol != null && !ArrayIndex.isIntArrayIndex(key)) {
properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), structure, symbol.getFieldIndex()));
}
}
return PropertyMap.newMap(structure, properties, fieldCount, fieldMaximum);
return PropertyMap.newMap(properties, fieldCount, fieldMaximum, 0);
}
PropertyMap makeSpillMap(final boolean hasArguments) {
final List<Property> properties = new ArrayList<>();
int spillIndex = 0;
assert keys != null;
for (int i = 0, length = keys.size(); i < length; i++) {
final String key = keys.get(i);
final Symbol symbol = symbols.get(i);
if (symbol != null && !ArrayIndex.isIntArrayIndex(key)) {
properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), spillIndex++));
}
}
return PropertyMap.newMap(properties, 0, 0, spillIndex);
}
/**
......
......@@ -73,11 +73,6 @@ public final class ObjectClassGenerator {
*/
static final int FIELD_PADDING = 4;
/**
* Rounding when calculating the number of fields.
*/
static final int FIELD_ROUNDING = 4;
/**
* Debug field logger
* Should we print debugging information for fields when they are generated and getters/setters are called?
......@@ -325,7 +320,6 @@ public final class ObjectClassGenerator {
final List<String> initFields = addFields(classEmitter, fieldCount);
final MethodEmitter init = newInitMethod(classEmitter);
initializeToUndefined(init, className, initFields);
init.returnVoid();
init.end();
......@@ -709,6 +703,15 @@ public final class ObjectClassGenerator {
}
}
/**
* Add padding to field count to avoid creating too many classes and have some spare fields
* @param count the field count
* @return the padded field count
*/
static int getPaddedFieldCount(final int count) {
return count / FIELD_PADDING * FIELD_PADDING + FIELD_PADDING;
}
//
// Provide generic getters and setters for undefined types. If a type is undefined, all
// and marshals the set to the correct setter depending on the type of the value being set.
......
......@@ -25,10 +25,10 @@
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import java.util.List;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.FIELD_PADDING;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.PropertyMap;
/**
......@@ -36,9 +36,6 @@ import jdk.nashorn.internal.runtime.PropertyMap;
*/
public abstract class ObjectCreator {
/** Compile unit for this ObjectCreator, see CompileUnit */
//protected final CompileUnit compileUnit;
/** List of keys to initiate in this ObjectCreator */
protected final List<String> keys;
......@@ -50,12 +47,7 @@ public abstract class ObjectCreator {
private final boolean isScope;
private final boolean hasArguments;
private int fieldCount;
private int paddedFieldCount;
private int paramCount;
private String fieldObjectClassName;
private Class<?> fieldObjectClass;
private PropertyMap propertyMap;
protected PropertyMap propertyMap;
/**
* Constructor
......@@ -72,41 +64,6 @@ public abstract class ObjectCreator {
this.symbols = symbols;
this.isScope = isScope;
this.hasArguments = hasArguments;
countFields();
findClass();
}
/**
* Tally the number of fields and parameters.
*/
private void countFields() {
for (final Symbol symbol : this.symbols) {
if (symbol != null) {
if (hasArguments() && symbol.isParam()) {
symbol.setFieldIndex(paramCount++);
} else {
symbol.setFieldIndex(fieldCount++);
}
}
}
paddedFieldCount = fieldCount + FIELD_PADDING;
}
/**
* Locate (or indirectly create) the object container class.
*/
private void findClass() {
fieldObjectClassName = isScope() ?
ObjectClassGenerator.getClassName(fieldCount, paramCount) :
ObjectClassGenerator.getClassName(paddedFieldCount);
try {
this.fieldObjectClass = Context.forStructureClass(Compiler.binaryName(fieldObjectClassName));
} catch (final ClassNotFoundException e) {
throw new AssertionError("Nashorn has encountered an internal error. Structure can not be created.");
}
}
/**
......@@ -115,6 +72,12 @@ public abstract class ObjectCreator {
*/
protected abstract void makeObject(final MethodEmitter method);
/**
* Construct the property map appropriate for the object.
* @return the newly created property map
*/
protected abstract PropertyMap makeMap();
/**
* Create a new MapCreator
* @param clazz type of MapCreator
......@@ -125,12 +88,11 @@ public abstract class ObjectCreator {
}
/**
* Construct the property map appropriate for the object.
* @return the newly created property map
* Loads the scope on the stack through the passed method emitter.
* @param method the method emitter to use
*/
protected PropertyMap makeMap() {
propertyMap = newMapCreator(fieldObjectClass).makeMap(hasArguments(), fieldCount, paddedFieldCount);
return propertyMap;
protected void loadScope(final MethodEmitter method) {
method.loadCompilerConstant(SCOPE);
}
/**
......@@ -143,16 +105,6 @@ public abstract class ObjectCreator {
return method;
}
/**
* Get the class name for the object class,
* e.g. {@code com.nashorn.oracle.scripts.JO2P0}
*
* @return script class name
*/
String getClassName() {
return fieldObjectClassName;
}
/**
* Is this a scope object
* @return true if scope
......
/*
* Copyright (c) 2010-2013, 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.codegen;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.scripts.JO;
import java.util.List;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
/**
* An object creator that uses spill properties.
*/
public class SpillObjectCreator extends ObjectCreator {
private final List<Node> values;
/**
* Constructor
*
* @param codegen code generator
* @param keys keys for fields in object
* @param symbols symbols for fields in object
* @param values list of values corresponding to keys
*/
protected SpillObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final List<Node> values) {
super(codegen, keys, symbols, false, false);
this.values = values;
makeMap();
}
@Override
protected void makeObject(final MethodEmitter method) {
assert !isScope() : "spill scope objects are not currently supported";
final int length = keys.size();
final Object[] presetValues = new Object[propertyMap.size()];
final Class clazz = JO.class;
// Compute constant values
for (int i = 0; i < length; i++) {
final String key = keys.get(i);
final Property property = propertyMap.findProperty(key);
if (property != null) {
presetValues[property.getSlot()] = LiteralNode.objectAsConstant(values.get(i));
}
}
method._new(clazz).dup();
codegen.loadConstant(propertyMap);
method.invoke(constructorNoLookup(JO.class, PropertyMap.class));
method.dup();
codegen.loadConstant(presetValues);
// Create properties with non-constant values
for (int i = 0; i < length; i++) {
final String key = keys.get(i);
final Property property = propertyMap.findProperty(key);
if (property != null && presetValues[property.getSlot()] == LiteralNode.POSTSET_MARKER) {
method.dup();
method.load(property.getSlot());
codegen.load(values.get(i)).convert(OBJECT);
method.arraystore();
presetValues[property.getSlot()] = null;
}
}
method.putField(Type.typeFor(ScriptObject.class).getInternalName(), "spill", Type.OBJECT_ARRAY.getDescriptor());
final int callSiteFlags = codegen.getCallSiteFlags();
// Assign properties with valid array index keys
for (int i = 0; i < length; i++) {
final String key = keys.get(i);
final Property property = propertyMap.findProperty(key);
final Node value = values.get(i);
if (property == null && value != null) {
method.dup();
method.load(keys.get(i));
codegen.load(value);
method.dynamicSetIndex(callSiteFlags);
}
}
}
@Override
protected PropertyMap makeMap() {
assert propertyMap == null : "property map already initialized";
propertyMap = new MapCreator(JO.class, keys, symbols) {
@Override
protected int getPropertyFlags(Symbol symbol, boolean hasArguments) {
return super.getPropertyFlags(symbol, hasArguments) | Property.IS_SPILL | Property.IS_ALWAYS_OBJECT;
}
}.makeSpillMap(false);
return propertyMap;
}
}
......@@ -131,6 +131,10 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
@Ignore
private final Compiler.Hints hints;
/** Properties of this object assigned in this function */
@Ignore
private HashSet<String> thisProperties;
/** Function flags. */
private final int flags;
......@@ -277,6 +281,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
this.declaredSymbols = functionNode.declaredSymbols;
this.kind = functionNode.kind;
this.firstToken = functionNode.firstToken;
this.thisProperties = functionNode.thisProperties;
}
@Override
......@@ -613,6 +618,25 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
return getFlag(NEEDS_PARENT_SCOPE) || isProgram();
}
/**
* Register a property assigned to the this object in this function.
* @param key the property name
*/
public void addThisProperty(final String key) {
if (thisProperties == null) {
thisProperties = new HashSet<>();
}
thisProperties.add(key);
}
/**
* Get the number of properties assigned to the this object in this function.
* @return number of properties
*/
public int countThisProperties() {
return thisProperties == null ? 0 : thisProperties.size();
}
/**
* Return the kind of this function
* @see FunctionNode.Kind
......
......@@ -49,6 +49,9 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
/** Literal value */
protected final T value;
/** Marker for values that must be computed at runtime */
public static final Object POSTSET_MARKER = new Object();
/**
* Constructor
*
......@@ -495,6 +498,30 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
return new LexerTokenLiteralNode(parent.getToken(), parent.getFinish(), value);
}
/**
* Get the constant value for an object, or {@link #POSTSET_MARKER} if the value can't be statically computed.
*
* @param object a node or value object
* @return the constant value or {@code POSTSET_MARKER}
*/
public static Object objectAsConstant(final Object object) {
if (object == null) {
return null;
} else if (object instanceof Number || object instanceof String || object instanceof Boolean) {
return object;
} else if (object instanceof LiteralNode) {
return objectAsConstant(((LiteralNode<?>)object).getValue());
} else if (object instanceof UnaryNode) {
final UnaryNode unaryNode = (UnaryNode)object;
if (unaryNode.isTokenType(TokenType.CONVERT) && unaryNode.getType().isObject()) {
return objectAsConstant(unaryNode.rhs());
}
}
return POSTSET_MARKER;
}
private static final class NullLiteralNode extends LiteralNode<Object> {
private NullLiteralNode(final long token, final int finish) {
......@@ -525,11 +552,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
* Array literal node class.
*/
public static final class ArrayLiteralNode extends LiteralNode<Node[]> {
private static class PostsetMarker {
//empty
}
private static PostsetMarker POSTSET_MARKER = new PostsetMarker();
/** Array element type. */
private Type elementType;
......@@ -740,24 +762,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
}
}
private Object objectAsConstant(final Object object) {
if (object == null) {
return null;
} else if (object instanceof Number || object instanceof String || object instanceof Boolean) {
return object;
} else if (object instanceof LiteralNode) {
return objectAsConstant(((LiteralNode<?>)object).getValue());
} else if (object instanceof UnaryNode) {
final UnaryNode unaryNode = (UnaryNode)object;
if (unaryNode.isTokenType(TokenType.CONVERT) && unaryNode.getType().isObject()) {
return objectAsConstant(unaryNode.rhs());
}
}
return POSTSET_MARKER;
}
@Override
public Node[] getArray() {
return value;
......
......@@ -514,7 +514,7 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
type("ArrayExpression");
comma();
final Node[] value = (Node[])literalNode.getValue();
final Node[] value = literalNode.getArray();
array("elements", Arrays.asList(value));
} else {
type("Literal");
......
......@@ -64,7 +64,7 @@ public final class NativeArguments extends ScriptObject {
private static final PropertyMap map$;
static {
PropertyMap map = PropertyMap.newMap(NativeArguments.class);
PropertyMap map = PropertyMap.newMap();
map = Lookup.newProperty(map, "length", Property.NOT_ENUMERABLE, G$LENGTH, S$LENGTH);
map = Lookup.newProperty(map, "callee", Property.NOT_ENUMERABLE, G$CALLEE, S$CALLEE);
map$ = map;
......
......@@ -54,7 +54,7 @@ public final class NativeStrictArguments extends ScriptObject {
private static final PropertyMap map$;
static {
PropertyMap map = PropertyMap.newMap(NativeStrictArguments.class);
PropertyMap map = PropertyMap.newMap();
map = Lookup.newProperty(map, "length", Property.NOT_ENUMERABLE, G$LENGTH, S$LENGTH);
// In strict mode, the caller and callee properties should throw TypeError
// Need to add properties directly to map since slots are assigned speculatively by newUserAccessors.
......
......@@ -52,7 +52,7 @@ public class PrototypeObject extends ScriptObject {
private static final MethodHandle SET_CONSTRUCTOR = findOwnMH("setConstructor", void.class, Object.class, Object.class);
static {
PropertyMap map = PropertyMap.newMap(PrototypeObject.class);
PropertyMap map = PropertyMap.newMap();
map = Lookup.newProperty(map, "constructor", Property.NOT_ENUMERABLE, GET_CONSTRUCTOR, SET_CONSTRUCTOR);
map$ = map;
}
......
......@@ -149,7 +149,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
}
static {
PropertyMap map = PropertyMap.newMap(ScriptFunctionImpl.class);
PropertyMap map = PropertyMap.newMap();
map = Lookup.newProperty(map, "prototype", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, G$PROTOTYPE, S$PROTOTYPE);
map = Lookup.newProperty(map, "length", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null);
map = Lookup.newProperty(map, "name", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null);
......@@ -201,7 +201,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
// Instance of this class is used as global anonymous function which
// serves as Function.prototype object.
private static class AnonymousFunction extends ScriptFunctionImpl {
private static final PropertyMap nasgenmap$$ = PropertyMap.newMap(AnonymousFunction.class);
private static final PropertyMap nasgenmap$$ = PropertyMap.newMap();
AnonymousFunction() {
super("", GlobalFunctions.ANONYMOUS, nasgenmap$$, null);
......
......@@ -2009,7 +2009,7 @@ loop:
}
if (!redefinitionOk) {
throw error(AbstractParser.message("property.redefinition", key.toString()), property.getToken());
throw error(AbstractParser.message("property.redefinition", key), property.getToken());
}
PropertyNode newProperty = existingProperty;
......
......@@ -140,8 +140,8 @@ public class AccessorProperty extends Property {
this.primitiveGetter = bindTo(property.primitiveGetter, delegate);
this.primitiveSetter = bindTo(property.primitiveSetter, delegate);
this.objectGetter = bindTo(property.objectGetter, delegate);
this.objectSetter = bindTo(property.objectSetter, delegate);
this.objectGetter = bindTo(property.ensureObjectGetter(), delegate);
this.objectSetter = bindTo(property.ensureObjectSetter(), delegate);
setCurrentType(property.getCurrentType());
}
......@@ -331,12 +331,26 @@ public class AccessorProperty extends Property {
}
}
@Override
public MethodHandle getGetter(final Class<?> type) {
// Spill getters and setters are lazily initialized, see JDK-8011630
private MethodHandle ensureObjectGetter() {
if (isSpill() && objectGetter == null) {
objectGetter = getSpillGetter();
}
return objectGetter;
}
private MethodHandle ensureObjectSetter() {
if (isSpill() && objectSetter == null) {
objectSetter = getSpillSetter();
}
return objectSetter;
}
@Override
public MethodHandle getGetter(final Class<?> type) {
final int i = getAccessorTypeIndex(type);
ensureObjectGetter();
if (getters[i] == null) {
getters[i] = debug(
createGetter(currentType, type, primitiveGetter, objectGetter),
......@@ -372,9 +386,7 @@ public class AccessorProperty extends Property {
}
private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) {
if (isSpill() && objectSetter == null) {
objectSetter = getSpillSetter();
}
ensureObjectSetter();
MethodHandle mh = createSetter(forType, type, primitiveSetter, objectSetter);
mh = debug(mh, currentType, type, "set");
return mh;
......
......@@ -91,14 +91,16 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener {
/**
* Constructor.
*
* @param properties A {@link PropertyHashMap} with initial contents.
* @param fieldCount Number of fields in use.
* @param properties A {@link PropertyHashMap} with initial contents.
* @param fieldCount Number of fields in use.
* @param fieldMaximum Number of fields available.
* @param spillLength Number of spill slots used.
*/
private PropertyMap(final PropertyHashMap properties, final int fieldCount, final int fieldMaximum) {
private PropertyMap(final PropertyHashMap properties, final int fieldCount, final int fieldMaximum, final int spillLength) {
this.properties = properties;
this.fieldCount = fieldCount;
this.fieldMaximum = fieldMaximum;
this.spillLength = spillLength;
if (Context.DEBUG) {
count++;
......@@ -111,7 +113,7 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener {
* @param properties A {@link PropertyHashMap} with initial contents.
*/
private PropertyMap(final PropertyHashMap properties) {
this(properties, 0, 0);
this(properties, 0, 0, 0);
}
/**
......@@ -159,42 +161,23 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener {
/**
* Public property map allocator.
*
* @param structure Class the map's {@link AccessorProperty}s apply to.
* @param properties Collection of initial properties.
* @param fieldCount Number of fields in use.
* @param properties Collection of initial properties.
* @param fieldCount Number of fields in use.
* @param fieldMaximum Number of fields available.
*
* @param spillLength Number of used spill slots.
* @return New {@link PropertyMap}.
*/
public static PropertyMap newMap(final Class<?> structure, final Collection<Property> properties, final int fieldCount, final int fieldMaximum) {
// Reduce the number of empty maps in the context.
if (structure == JO.class) {
return EMPTY_MAP;
}
public static PropertyMap newMap(final Collection<Property> properties, final int fieldCount, final int fieldMaximum, final int spillLength) {
PropertyHashMap newProperties = EMPTY_HASHMAP.immutableAdd(properties);
return new PropertyMap(newProperties, fieldCount, fieldMaximum);
}
/**
* Public property map factory allocator
*
* @param structure Class the map's {@link AccessorProperty}s apply to.
*
* @return New {@link PropertyMap}.
*/
public static PropertyMap newMap(final Class<?> structure) {
return newMap(structure, null, 0, 0);
return new PropertyMap(newProperties, fieldCount, fieldMaximum, spillLength);
}
/**
* Return a sharable empty map.
*
* @param context the context
* @return New empty {@link PropertyMap}.
*/
public static PropertyMap newEmptyMap(final Context context) {
public static PropertyMap newMap() {
return new PropertyMap(EMPTY_HASHMAP);
}
......
......@@ -170,7 +170,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
this.arrayData = ArrayData.EMPTY_ARRAY;
this.setMap(map == null ? PropertyMap.newMap(getClass()) : map);
this.setMap(map == null ? PropertyMap.newMap() : map);
}
/**
......@@ -188,7 +188,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
this.arrayData = ArrayData.EMPTY_ARRAY;
this.setMap(map == null ? PropertyMap.newMap(getClass()) : map);
this.setMap(map == null ? PropertyMap.newMap() : map);
this.proto = proto;
if (proto != null) {
......
......@@ -33,7 +33,7 @@ import jdk.nashorn.internal.runtime.ScriptObject;
*/
public class JO extends ScriptObject {
private static final PropertyMap map$ = PropertyMap.newMap(JO.class);
private static final PropertyMap map$ = PropertyMap.newMap();
/**
* Returns the initial property map to be used.
......
此差异已折叠。
703
5624
17575
111
getting X
X
setting X
17577
111
aaa
hhh
yyy
X
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册