ConstructorGenerator.java revision 754:4d54c3d19e88
1258945Sroberto/*
2258945Sroberto * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3258945Sroberto * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4258945Sroberto *
5258945Sroberto * This code is free software; you can redistribute it and/or modify it
6258945Sroberto * under the terms of the GNU General Public License version 2 only, as
7258945Sroberto * published by the Free Software Foundation.  Oracle designates this
8258945Sroberto * particular file as subject to the "Classpath" exception as provided
9258945Sroberto * by Oracle in the LICENSE file that accompanied this code.
10258945Sroberto *
11258945Sroberto * This code is distributed in the hope that it will be useful, but WITHOUT
12258945Sroberto * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13258945Sroberto * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14258945Sroberto * version 2 for more details (a copy is included in the LICENSE file that
15258945Sroberto * accompanied this code).
16258945Sroberto *
17258945Sroberto * You should have received a copy of the GNU General Public License version
18258945Sroberto * 2 along with this work; if not, write to the Free Software Foundation,
19258945Sroberto * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20258945Sroberto *
21258945Sroberto * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22258945Sroberto * or visit www.oracle.com if you need additional information or have any
23258945Sroberto * questions.
24258945Sroberto */
25258945Sroberto
26258945Srobertopackage jdk.nashorn.internal.tools.nasgen;
27258945Sroberto
28258945Srobertoimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
29258945Srobertoimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
30258945Srobertoimport static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
31258945Srobertoimport static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
32258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.CONSTRUCTOR_SUFFIX;
33258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
34258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
35258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC;
36258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME;
37258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
38258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR;
39289764Sglebiusimport static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC;
40258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE;
41258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_INIT_DESC3;
42258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_INIT_DESC4;
43258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE;
44258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY;
45258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC;
46258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETPROTOTYPE;
47258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETPROTOTYPE_DESC;
48258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE;
49258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_INIT_DESC;
50258945Srobertoimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_TYPE;
51258945Sroberto
52258945Srobertoimport java.io.FileOutputStream;
53258945Srobertoimport java.io.IOException;
54258945Srobertoimport java.util.List;
55258945Srobertoimport jdk.internal.org.objectweb.asm.Handle;
56258945Sroberto
57258945Sroberto/**
58258945Sroberto * This class generates constructor class for a @ClassInfo annotated class.
59258945Sroberto *
60258945Sroberto */
61258945Srobertopublic class ConstructorGenerator extends ClassGenerator {
62258945Sroberto    private final ScriptClassInfo scriptClassInfo;
63258945Sroberto    private final String className;
64258945Sroberto    private final MemberInfo constructor;
65258945Sroberto    private final int memberCount;
66258945Sroberto    private final List<MemberInfo> specs;
67258945Sroberto
68258945Sroberto    ConstructorGenerator(final ScriptClassInfo sci) {
69258945Sroberto        this.scriptClassInfo = sci;
70258945Sroberto
71258945Sroberto        this.className = scriptClassInfo.getConstructorClassName();
72258945Sroberto        this.constructor = scriptClassInfo.getConstructor();
73258945Sroberto        this.memberCount = scriptClassInfo.getConstructorMemberCount();
74258945Sroberto        this.specs = scriptClassInfo.getSpecializedConstructors();
75258945Sroberto    }
76258945Sroberto
77258945Sroberto    byte[] getClassBytes() {
78258945Sroberto        // new class extensing from ScriptObject
79258945Sroberto        final String superClass = (constructor != null)? SCRIPTFUNCTIONIMPL_TYPE : SCRIPTOBJECT_TYPE;
80258945Sroberto        cw.visit(V1_7, ACC_FINAL, className, null, superClass, null);
81258945Sroberto        if (memberCount > 0) {
82258945Sroberto            // add fields
83258945Sroberto            emitFields();
84258945Sroberto            // add <clinit>
85258945Sroberto            emitStaticInitializer();
86258945Sroberto        }
87258945Sroberto        // add <init>
88258945Sroberto        emitConstructor();
89258945Sroberto
90258945Sroberto        if (constructor == null) {
91258945Sroberto            emitGetClassName(scriptClassInfo.getName());
92258945Sroberto        }
93258945Sroberto
94258945Sroberto        cw.visitEnd();
95258945Sroberto        return cw.toByteArray();
96280849Scy    }
97258945Sroberto
98258945Sroberto    // --Internals only below this point
99258945Sroberto    private void emitFields() {
100258945Sroberto        // Introduce "Function" type instance fields for each
101258945Sroberto        // constructor @Function in script class and introduce instance
102258945Sroberto        // fields for each constructor @Property in the script class.
103258945Sroberto        for (MemberInfo memInfo : scriptClassInfo.getMembers()) {
104258945Sroberto            if (memInfo.isConstructorFunction()) {
105280849Scy                addFunctionField(memInfo.getJavaName());
106258945Sroberto                memInfo = (MemberInfo)memInfo.clone();
107258945Sroberto                memInfo.setJavaDesc(OBJECT_DESC);
108258945Sroberto                memInfo.setJavaAccess(ACC_PUBLIC);
109258945Sroberto                addGetter(className, memInfo);
110258945Sroberto                addSetter(className, memInfo);
111258945Sroberto            } else if (memInfo.isConstructorProperty()) {
112258945Sroberto                if (memInfo.isStaticFinal()) {
113258945Sroberto                    addGetter(scriptClassInfo.getJavaName(), memInfo);
114258945Sroberto                } else {
115258945Sroberto                    addField(memInfo.getJavaName(), memInfo.getJavaDesc());
116258945Sroberto                    memInfo = (MemberInfo)memInfo.clone();
117258945Sroberto                    memInfo.setJavaAccess(ACC_PUBLIC);
118258945Sroberto                    addGetter(className, memInfo);
119258945Sroberto                    addSetter(className, memInfo);
120258945Sroberto                }
121258945Sroberto            }
122258945Sroberto        }
123258945Sroberto
124258945Sroberto        addMapField();
125258945Sroberto    }
126258945Sroberto
127258945Sroberto    private void emitStaticInitializer() {
128258945Sroberto        final MethodGenerator mi = makeStaticInitializer();
129258945Sroberto        emitStaticInitPrefix(mi, className, memberCount);
130258945Sroberto
131258945Sroberto        for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
132258945Sroberto            if (memInfo.isConstructorFunction() || memInfo.isConstructorProperty()) {
133258945Sroberto                linkerAddGetterSetter(mi, className, memInfo);
134258945Sroberto            } else if (memInfo.isConstructorGetter()) {
135258945Sroberto                final MemberInfo setter = scriptClassInfo.findSetter(memInfo);
136258945Sroberto                linkerAddGetterSetter(mi, scriptClassInfo.getJavaName(), memInfo, setter);
137258945Sroberto            }
138258945Sroberto        }
139258945Sroberto        emitStaticInitSuffix(mi, className);
140258945Sroberto    }
141258945Sroberto
142258945Sroberto    private void emitConstructor() {
143258945Sroberto        final MethodGenerator mi = makeConstructor();
144258945Sroberto        mi.visitCode();
145258945Sroberto        callSuper(mi);
146258945Sroberto
147258945Sroberto        if (memberCount > 0) {
148258945Sroberto            // initialize Function type fields
149258945Sroberto            initFunctionFields(mi);
150258945Sroberto            // initialize data fields
151258945Sroberto            initDataFields(mi);
152258945Sroberto        }
153258945Sroberto
154258945Sroberto        if (constructor != null) {
155258945Sroberto            final int arity = constructor.getArity();
156258945Sroberto            if (arity != MemberInfo.DEFAULT_ARITY) {
157258945Sroberto                mi.loadThis();
158258945Sroberto                mi.push(arity);
159258945Sroberto                mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETARITY,
160258945Sroberto                        SCRIPTFUNCTION_SETARITY_DESC);
161258945Sroberto            }
162258945Sroberto        }
163258945Sroberto        mi.returnVoid();
164258945Sroberto        mi.computeMaxs();
165258945Sroberto        mi.visitEnd();
166258945Sroberto    }
167258945Sroberto
168258945Sroberto    private void loadMap(final MethodGenerator mi) {
169258945Sroberto        if (memberCount > 0) {
170258945Sroberto            mi.getStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC);
171258945Sroberto        }
172258945Sroberto    }
173258945Sroberto
174258945Sroberto    private void callSuper(final MethodGenerator mi) {
175258945Sroberto        String superClass, superDesc;
176258945Sroberto        mi.loadThis();
177258945Sroberto        if (constructor == null) {
178258945Sroberto            // call ScriptObject.<init>
179258945Sroberto            superClass = SCRIPTOBJECT_TYPE;
180258945Sroberto            superDesc = (memberCount > 0) ? SCRIPTOBJECT_INIT_DESC : DEFAULT_INIT_DESC;
181258945Sroberto            loadMap(mi);
182280849Scy        } else {
183258945Sroberto            // call Function.<init>
184258945Sroberto            superClass = SCRIPTFUNCTIONIMPL_TYPE;
185258945Sroberto            superDesc = (memberCount > 0) ? SCRIPTFUNCTIONIMPL_INIT_DESC4 : SCRIPTFUNCTIONIMPL_INIT_DESC3;
186258945Sroberto            mi.loadLiteral(constructor.getName());
187258945Sroberto            mi.visitLdcInsn(new Handle(H_INVOKESTATIC, scriptClassInfo.getJavaName(), constructor.getJavaName(), constructor.getJavaDesc()));
188258945Sroberto            loadMap(mi);
189280849Scy            mi.memberInfoArray(scriptClassInfo.getJavaName(), specs); //pushes null if specs empty
190258945Sroberto        }
191258945Sroberto
192280849Scy        mi.invokeSpecial(superClass, INIT, superDesc);
193258945Sroberto    }
194280849Scy
195280849Scy    private void initFunctionFields(final MethodGenerator mi) {
196280849Scy        for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
197258945Sroberto            if (!memInfo.isConstructorFunction()) {
198258945Sroberto                continue;
199258945Sroberto            }
200258945Sroberto            mi.loadThis();
201258945Sroberto            newFunction(mi, scriptClassInfo.getJavaName(), memInfo, scriptClassInfo.findSpecializations(memInfo.getJavaName()));
202258945Sroberto            mi.putField(className, memInfo.getJavaName(), OBJECT_DESC);
203258945Sroberto        }
204258945Sroberto    }
205258945Sroberto
206258945Sroberto    private void initDataFields(final MethodGenerator mi) {
207258945Sroberto         for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
208258945Sroberto            if (!memInfo.isConstructorProperty() || memInfo.isFinal()) {
209258945Sroberto                continue;
210258945Sroberto            }
211258945Sroberto            final Object value = memInfo.getValue();
212258945Sroberto            if (value != null) {
213258945Sroberto                mi.loadThis();
214258945Sroberto                mi.loadLiteral(value);
215258945Sroberto                mi.putField(className, memInfo.getJavaName(), memInfo.getJavaDesc());
216258945Sroberto            } else if (!memInfo.getInitClass().isEmpty()) {
217258945Sroberto                final String clazz = memInfo.getInitClass();
218258945Sroberto                mi.loadThis();
219258945Sroberto                mi.newObject(clazz);
220258945Sroberto                mi.dup();
221258945Sroberto                mi.invokeSpecial(clazz, INIT, DEFAULT_INIT_DESC);
222258945Sroberto                mi.putField(className, memInfo.getJavaName(), memInfo.getJavaDesc());
223258945Sroberto            }
224258945Sroberto        }
225258945Sroberto
226258945Sroberto        if (constructor != null) {
227258945Sroberto            mi.loadThis();
228258945Sroberto            final String protoName = scriptClassInfo.getPrototypeClassName();
229258945Sroberto            mi.newObject(protoName);
230258945Sroberto            mi.dup();
231258945Sroberto            mi.invokeSpecial(protoName, INIT, DEFAULT_INIT_DESC);
232258945Sroberto            mi.dup();
233258945Sroberto            mi.loadThis();
234258945Sroberto            mi.invokeStatic(PROTOTYPEOBJECT_TYPE, PROTOTYPEOBJECT_SETCONSTRUCTOR,
235258945Sroberto                    PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC);
236258945Sroberto            mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETPROTOTYPE, SCRIPTFUNCTION_SETPROTOTYPE_DESC);
237258945Sroberto        }
238258945Sroberto    }
239258945Sroberto
240258945Sroberto    /**
241258945Sroberto     * Entry point for ConstructorGenerator run separately as an application. Will display
242258945Sroberto     * usage. Takes one argument, a class name.
243258945Sroberto     * @param args args vector
244258945Sroberto     * @throws IOException if class can't be read
245258945Sroberto     */
246    public static void main(final String[] args) throws IOException {
247        if (args.length != 1) {
248            System.err.println("Usage: " + ConstructorGenerator.class.getName() + " <class>");
249            System.exit(1);
250        }
251
252        final String className = args[0].replace('.', '/');
253        final ScriptClassInfo sci = getScriptClassInfo(className + ".class");
254        if (sci == null) {
255            System.err.println("No @ScriptClass in " + className);
256            System.exit(2);
257            throw new IOException(); // get rid of warning for sci.verify() below - may be null
258        }
259
260        try {
261            sci.verify();
262        } catch (final Exception e) {
263            System.err.println(e.getMessage());
264            System.exit(3);
265        }
266        final ConstructorGenerator gen = new ConstructorGenerator(sci);
267        try (FileOutputStream fos = new FileOutputStream(className + CONSTRUCTOR_SUFFIX + ".class")) {
268            fos.write(gen.getClassBytes());
269        }
270    }
271}
272