ConstructorGenerator.java revision 2:da1e581c933b
1/*
2 * Copyright (c) 2010, 2012, 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.tools.nasgen;
27
28import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
29import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
30import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
31import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
32import static jdk.nashorn.internal.tools.nasgen.StringConstants.CONSTRUCTOR_SUFFIX;
33import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
34import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
35import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DESC;
36import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DUPLICATE;
37import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DUPLICATE_DESC;
38import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_FIELD_NAME;
39import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_TYPE;
40import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
41import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPE;
42import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR;
43import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC;
44import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE;
45import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_INIT_DESC3;
46import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_INIT_DESC4;
47import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE;
48import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY;
49import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC;
50import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE;
51import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_INIT_DESC;
52import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_TYPE;
53
54import java.io.FileOutputStream;
55import java.io.IOException;
56import java.util.List;
57import jdk.internal.org.objectweb.asm.Handle;
58
59/**
60 * This class generates constructor class for a @ClassInfo annotated class.
61 *
62 */
63public class ConstructorGenerator extends ClassGenerator {
64    private final ScriptClassInfo scriptClassInfo;
65    private final String className;
66    private final MemberInfo constructor;
67    private final int memberCount;
68    private final List<MemberInfo> specs;
69
70    ConstructorGenerator(final ScriptClassInfo sci) {
71        this.scriptClassInfo = sci;
72
73        this.className = scriptClassInfo.getConstructorClassName();
74        this.constructor = scriptClassInfo.getConstructor();
75        this.memberCount = scriptClassInfo.getConstructorMemberCount();
76        this.specs = scriptClassInfo.getSpecializedConstructors();
77    }
78
79    byte[] getClassBytes() {
80        // new class extensing from ScriptObject
81        final String superClass = (constructor != null)? SCRIPTFUNCTIONIMPL_TYPE : SCRIPTOBJECT_TYPE;
82        cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, className, null, superClass, null);
83        if (memberCount > 0) {
84            // add fields
85            emitFields();
86            // add <clinit>
87            emitStaticInitializer();
88        }
89        // add <init>
90        emitConstructor();
91
92        if (constructor == null) {
93            emitGetClassName(scriptClassInfo.getName());
94        }
95
96        cw.visitEnd();
97        return cw.toByteArray();
98    }
99
100    // --Internals only below this point
101    private void emitFields() {
102        // Introduce "Function" type instance fields for each
103        // constructor @Function in script class and introduce instance
104        // fields for each constructor @Property in the script class.
105        for (MemberInfo memInfo : scriptClassInfo.getMembers()) {
106            if (memInfo.isConstructorFunction()) {
107                addFunctionField(memInfo.getJavaName());
108                memInfo = (MemberInfo)memInfo.clone();
109                memInfo.setJavaDesc(OBJECT_DESC);
110                memInfo.setJavaAccess(ACC_PUBLIC);
111                addGetter(className, memInfo);
112                addSetter(className, memInfo);
113            } else if (memInfo.isConstructorProperty()) {
114                if (memInfo.isStaticFinal()) {
115                    addGetter(scriptClassInfo.getJavaName(), memInfo);
116                } else {
117                    addField(memInfo.getJavaName(), memInfo.getJavaDesc());
118                    memInfo = (MemberInfo)memInfo.clone();
119                    memInfo.setJavaAccess(ACC_PUBLIC);
120                    addGetter(className, memInfo);
121                    addSetter(className, memInfo);
122                }
123            }
124        }
125
126        addMapField();
127    }
128
129    private void emitStaticInitializer() {
130        final MethodGenerator mi = makeStaticInitializer();
131        emitStaticInitPrefix(mi, className);
132
133        for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
134            if (memInfo.isConstructorFunction() || memInfo.isConstructorProperty()) {
135                linkerAddGetterSetter(mi, className, memInfo);
136            } else if (memInfo.isConstructorGetter()) {
137                final MemberInfo setter = scriptClassInfo.findSetter(memInfo);
138                linkerAddGetterSetter(mi, className, memInfo, setter);
139            }
140        }
141        emitStaticInitSuffix(mi, className);
142    }
143
144    private void emitConstructor() {
145        final MethodGenerator mi = makeConstructor();
146        mi.visitCode();
147        callSuper(mi);
148
149        if (memberCount > 0) {
150            // initialize Function type fields
151            initFunctionFields(mi);
152            // initialize data fields
153            initDataFields(mi);
154        }
155
156        if (constructor != null) {
157            final int arity = constructor.getArity();
158            if (arity != MemberInfo.DEFAULT_ARITY) {
159                mi.loadThis();
160                mi.push(arity);
161                mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETARITY,
162                        SCRIPTFUNCTION_SETARITY_DESC);
163            }
164        }
165        mi.returnVoid();
166        mi.computeMaxs();
167        mi.visitEnd();
168    }
169
170    private void loadMap(final MethodGenerator mi) {
171        if (memberCount > 0) {
172            mi.getStatic(className, MAP_FIELD_NAME, MAP_DESC);
173            // make sure we use duplicated PropertyMap so that original map
174            // stays intact and so can be used for many globals in same context
175            mi.invokeVirtual(MAP_TYPE, MAP_DUPLICATE, MAP_DUPLICATE_DESC);
176        }
177    }
178
179    private void callSuper(final MethodGenerator mi) {
180        String superClass, superDesc;
181        mi.loadThis();
182        if (constructor == null) {
183            // call ScriptObject.<init>
184            superClass = SCRIPTOBJECT_TYPE;
185            superDesc = (memberCount > 0) ? SCRIPTOBJECT_INIT_DESC : DEFAULT_INIT_DESC;
186            loadMap(mi);
187        } else {
188            // call Function.<init>
189            superClass = SCRIPTFUNCTIONIMPL_TYPE;
190            superDesc = (memberCount > 0) ? SCRIPTFUNCTIONIMPL_INIT_DESC4 : SCRIPTFUNCTIONIMPL_INIT_DESC3;
191            mi.loadLiteral(constructor.getName());
192            mi.visitLdcInsn(new Handle(H_INVOKESTATIC, scriptClassInfo.getJavaName(), constructor.getJavaName(), constructor.getJavaDesc()));
193            loadMap(mi);
194            mi.memberInfoArray(scriptClassInfo.getJavaName(), specs); //pushes null if specs empty
195        }
196
197        mi.invokeSpecial(superClass, INIT, superDesc);
198    }
199
200    private void initFunctionFields(final MethodGenerator mi) {
201        for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
202            if (!memInfo.isConstructorFunction()) {
203                continue;
204            }
205            mi.loadThis();
206            newFunction(mi, scriptClassInfo.getJavaName(), memInfo, scriptClassInfo.findSpecializations(memInfo.getJavaName()));
207            mi.putField(className, memInfo.getJavaName(), OBJECT_DESC);
208        }
209    }
210
211    private void initDataFields(final MethodGenerator mi) {
212         for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
213            if (!memInfo.isConstructorProperty() || memInfo.isFinal()) {
214                continue;
215            }
216            final Object value = memInfo.getValue();
217            if (value != null) {
218                mi.loadThis();
219                mi.loadLiteral(value);
220                mi.putField(className, memInfo.getJavaName(), memInfo.getJavaDesc());
221            } else if (!memInfo.getInitClass().isEmpty()) {
222                final String clazz = memInfo.getInitClass();
223                mi.loadThis();
224                mi.newObject(clazz);
225                mi.dup();
226                mi.invokeSpecial(clazz, INIT, DEFAULT_INIT_DESC);
227                mi.putField(className, memInfo.getJavaName(), memInfo.getJavaDesc());
228            }
229        }
230
231        if (constructor != null) {
232            mi.loadThis();
233            final String protoName = scriptClassInfo.getPrototypeClassName();
234            mi.newObject(protoName);
235            mi.dup();
236            mi.invokeSpecial(protoName, INIT, DEFAULT_INIT_DESC);
237            mi.dup();
238            mi.loadThis();
239            mi.invokeStatic(PROTOTYPEOBJECT_TYPE, PROTOTYPEOBJECT_SETCONSTRUCTOR,
240                    PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC);
241            mi.putField(SCRIPTFUNCTION_TYPE, PROTOTYPE, OBJECT_DESC);
242        }
243    }
244
245    /**
246     * Entry point for ConstructorGenerator run separately as an application. Will display
247     * usage. Takes one argument, a class name.
248     * @param args args vector
249     * @throws IOException if class can't be read
250     */
251    public static void main(final String[] args) throws IOException {
252        if (args.length != 1) {
253            System.err.println("Usage: " + ConstructorGenerator.class.getName() + " <class>");
254            System.exit(1);
255        }
256
257        final String className = args[0].replace('.', '/');
258        final ScriptClassInfo sci = getScriptClassInfo(className + ".class");
259        if (sci == null) {
260            System.err.println("No @ScriptClass in " + className);
261            System.exit(2);
262            throw new IOException(); // get rid of warning for sci.verify() below - may be null
263        }
264
265        try {
266            sci.verify();
267        } catch (final Exception e) {
268            System.err.println(e.getMessage());
269            System.exit(3);
270        }
271        final ConstructorGenerator gen = new ConstructorGenerator(sci);
272        try (FileOutputStream fos = new FileOutputStream(className + CONSTRUCTOR_SUFFIX + ".class")) {
273            fos.write(gen.getClassBytes());
274        }
275    }
276}
277