ConstructorGenerator.java revision 77:bca3a64a4a82
1192904Sbms/*
2192904Sbms * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3192904Sbms * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4192904Sbms *
5192904Sbms * This code is free software; you can redistribute it and/or modify it
6192904Sbms * under the terms of the GNU General Public License version 2 only, as
7192904Sbms * published by the Free Software Foundation.  Oracle designates this
8192904Sbms * particular file as subject to the "Classpath" exception as provided
9192904Sbms * by Oracle in the LICENSE file that accompanied this code.
10192904Sbms *
11192904Sbms * This code is distributed in the hope that it will be useful, but WITHOUT
12192904Sbms * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13192904Sbms * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14192904Sbms * version 2 for more details (a copy is included in the LICENSE file that
15192904Sbms * accompanied this code).
16192904Sbms *
17192904Sbms * You should have received a copy of the GNU General Public License version
18192904Sbms * 2 along with this work; if not, write to the Free Software Foundation,
19192904Sbms * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20192904Sbms *
21192904Sbms * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22192904Sbms * or visit www.oracle.com if you need additional information or have any
23192904Sbms * questions.
24192904Sbms */
25192904Sbms
26192904Sbmspackage jdk.nashorn.internal.tools.nasgen;
27192904Sbms
28192904Sbmsimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
29192904Sbmsimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
30249252Saeimport static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
31192904Sbmsimport static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
32192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.CONSTRUCTOR_SUFFIX;
33192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
34192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
35192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DESC;
36192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DUPLICATE;
37192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DUPLICATE_DESC;
38192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_FIELD_NAME;
39192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_TYPE;
40192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
41192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPE;
42192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR;
43192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC;
44192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE;
45192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_INIT_DESC3;
46192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_INIT_DESC4;
47192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE;
48192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY;
49192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC;
50192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE;
51192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_INIT_DESC;
52192904Sbmsimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_TYPE;
53192904Sbms
54192904Sbmsimport java.io.FileOutputStream;
55233648Seadlerimport java.io.IOException;
56192904Sbmsimport java.util.List;
57192904Sbmsimport jdk.internal.org.objectweb.asm.Handle;
58192904Sbms
59192904Sbms/**
60192904Sbms * This class generates constructor class for a @ClassInfo annotated class.
61192904Sbms *
62192904Sbms */
63192904Sbmspublic class ConstructorGenerator extends ClassGenerator {
64192904Sbms    private final ScriptClassInfo scriptClassInfo;
65192904Sbms    private final String className;
66192904Sbms    private final MemberInfo constructor;
67192904Sbms    private final int memberCount;
68192904Sbms    private final List<MemberInfo> specs;
69192904Sbms
70192904Sbms    ConstructorGenerator(final ScriptClassInfo sci) {
71192904Sbms        this.scriptClassInfo = sci;
72192904Sbms
73192904Sbms        this.className = scriptClassInfo.getConstructorClassName();
74192904Sbms        this.constructor = scriptClassInfo.getConstructor();
75192904Sbms        this.memberCount = scriptClassInfo.getConstructorMemberCount();
76192904Sbms        this.specs = scriptClassInfo.getSpecializedConstructors();
77192904Sbms    }
78192904Sbms
79192904Sbms    byte[] getClassBytes() {
80192904Sbms        // new class extensing from ScriptObject
81192904Sbms        final String superClass = (constructor != null)? SCRIPTFUNCTIONIMPL_TYPE : SCRIPTOBJECT_TYPE;
82192904Sbms        cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, className, null, superClass, null);
83192904Sbms        if (memberCount > 0) {
84192904Sbms            // add fields
85192904Sbms            emitFields();
86192904Sbms            // add <clinit>
87192904Sbms            emitStaticInitializer();
88192904Sbms        }
89192904Sbms        // add <init>
90192904Sbms        emitConstructor();
91192904Sbms
92249253Sjoel        if (constructor == null) {
93249253Sjoel            emitGetClassName(scriptClassInfo.getName());
94249252Sae        }
95192904Sbms
96192904Sbms        cw.visitEnd();
97249253Sjoel        return cw.toByteArray();
98192904Sbms    }
99192904Sbms
100192904Sbms    // --Internals only below this point
101192904Sbms    private void emitFields() {
102192904Sbms        // 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, scriptClassInfo.getJavaName(), 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