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