1/*
2 * Copyright (c) 2010, 2013, 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 */
25
26package jdk.nashorn.internal.codegen;
27
28import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
29
30import java.util.List;
31import jdk.nashorn.internal.codegen.types.Type;
32import jdk.nashorn.internal.runtime.JSType;
33import jdk.nashorn.internal.runtime.PropertyMap;
34import jdk.nashorn.internal.runtime.ScriptObject;
35
36/**
37 * Base class for object creation code generation.
38 * @param <T> value type
39 */
40public abstract class ObjectCreator<T> implements CodeGenerator.SplitLiteralCreator {
41
42    /** List of keys & symbols to initiate in this ObjectCreator */
43    final List<MapTuple<T>> tuples;
44
45    /** Code generator */
46    final CodeGenerator codegen;
47
48    /** Property map */
49    protected PropertyMap   propertyMap;
50
51    private final boolean       isScope;
52    private final boolean       hasArguments;
53
54    /**
55     * Constructor
56     *
57     * @param codegen      the code generator
58     * @param tuples       key,symbol,value (optional) tuples
59     * @param isScope      is this object scope
60     * @param hasArguments does the created object have an "arguments" property
61     */
62    ObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples, final boolean isScope, final boolean hasArguments) {
63        this.codegen       = codegen;
64        this.tuples        = tuples;
65        this.isScope       = isScope;
66        this.hasArguments  = hasArguments;
67    }
68
69    /**
70     * Generate code for making the object.
71     * @param method Script method.
72     */
73    public void makeObject(final MethodEmitter method) {
74        createObject(method);
75        // We need to store the object in a temporary slot as populateRange expects to load the
76        // object from a slot (as it is also invoked within split methods). Note that this also
77        // helps optimistic continuations to handle the stack in case an optimistic assumption
78        // fails during initialization (see JDK-8079269).
79        final int objectSlot = method.getUsedSlotsWithLiveTemporaries();
80        final Type objectType = method.peekType();
81        method.storeTemp(objectType, objectSlot);
82        populateRange(method, objectType, objectSlot, 0, tuples.size());
83    }
84
85    /**
86     * Generate code for creating and initializing the object.
87     * @param method the method emitter
88     */
89    protected abstract void createObject(final MethodEmitter method);
90
91    /**
92     * Construct the property map appropriate for the object.
93     * @return the newly created property map
94     */
95    protected abstract PropertyMap makeMap();
96
97    /**
98     * Create a new MapCreator
99     * @param clazz type of MapCreator
100     * @return map creator instantiated by type
101     */
102    protected MapCreator<?> newMapCreator(final Class<? extends ScriptObject> clazz) {
103        return new MapCreator<>(clazz, tuples);
104    }
105
106    /**
107     * Loads the scope on the stack through the passed method emitter.
108     * @param method the method emitter to use
109     */
110    protected void loadScope(final MethodEmitter method) {
111        method.loadCompilerConstant(SCOPE);
112    }
113
114    /**
115     * Emit the correct map for the object.
116     * @param method method emitter
117     * @return the method emitter
118     */
119    protected MethodEmitter loadMap(final MethodEmitter method) {
120        codegen.loadConstant(propertyMap);
121        return method;
122    }
123
124    PropertyMap getMap() {
125        return propertyMap;
126    }
127
128    /**
129     * Is this a scope object
130     * @return true if scope
131     */
132    protected boolean isScope() {
133        return isScope;
134    }
135
136    /**
137     * Does the created object have an "arguments" property
138     * @return true if has an "arguments" property
139     */
140    protected boolean hasArguments() {
141        return hasArguments;
142    }
143
144    /**
145     * Get the class of objects created by this ObjectCreator
146     * @return class of created object
147     */
148    abstract protected Class<? extends ScriptObject> getAllocatorClass();
149
150    /**
151     * Technique for loading an initial value. Defined by anonymous subclasses in code gen.
152     *
153     * @param value Value to load.
154     * @param type the type of the value to load
155     */
156    protected abstract void loadValue(T value, Type type);
157
158    MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> tuple, final boolean pack) {
159        loadValue(tuple.value, tuple.type);
160        if (!codegen.useDualFields() || !tuple.isPrimitive()) {
161            method.convert(Type.OBJECT);
162        } else if (pack) {
163            method.pack();
164        }
165        return method;
166    }
167
168    MethodEmitter loadIndex(final MethodEmitter method, final long index) {
169        return JSType.isRepresentableAsInt(index) ? method.load((int) index) : method.load((double) index);
170    }
171}
172