ConstructorGenerator.java revision 1574:1597de0e19e3
14Srgrimes/*
24Srgrimes * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
34Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44Srgrimes *
54Srgrimes * This code is free software; you can redistribute it and/or modify it
64Srgrimes * under the terms of the GNU General Public License version 2 only, as
74Srgrimes * published by the Free Software Foundation.  Oracle designates this
84Srgrimes * particular file as subject to the "Classpath" exception as provided
94Srgrimes * by Oracle in the LICENSE file that accompanied this code.
104Srgrimes *
114Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT
124Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
134Srgrimes * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
144Srgrimes * version 2 for more details (a copy is included in the LICENSE file that
154Srgrimes * accompanied this code).
164Srgrimes *
174Srgrimes * You should have received a copy of the GNU General Public License version
184Srgrimes * 2 along with this work; if not, write to the Free Software Foundation,
194Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
204Srgrimes *
214Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
224Srgrimes * or visit www.oracle.com if you need additional information or have any
234Srgrimes * questions.
244Srgrimes */
254Srgrimes
264Srgrimespackage jdk.nashorn.internal.tools.nasgen;
274Srgrimes
284Srgrimesimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
294Srgrimesimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
304Srgrimesimport static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
314Srgrimesimport static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
324Srgrimesimport static jdk.nashorn.internal.tools.nasgen.StringConstants.CONSTRUCTOR_SUFFIX;
334Srgrimesimport static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
34593Srgrimesimport static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
3550477Speterimport static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
364Srgrimesimport static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC;
374Srgrimesimport static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME;
3832929Seivindimport static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR;
3913290Speterimport static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC;
4013225Swollmanimport static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE;
412056Swollmanimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_INIT_DESC3;
422056Swollmanimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_INIT_DESC4;
4345720Speterimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY;
4467365Sjhbimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC;
4511865Sphkimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETDOCUMENTATION;
4633281Sbdeimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETDOCUMENTATION_DESC;
4745720Speterimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETPROTOTYPE;
4811865Sphkimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETPROTOTYPE_DESC;
492056Swollmanimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE;
5071287Sjakeimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_INIT_DESC;
5145720Speterimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_TYPE;
5245720Speter
5322093Sbdeimport java.io.FileOutputStream;
544478Sbdeimport java.io.IOException;
5522093Sbdeimport java.util.List;
564478Sbdeimport jdk.internal.org.objectweb.asm.Handle;
573816Swollman
5831255Sbde/**
5925083Sjdp * This class generates constructor class for a @ScriptClass annotated class.
6031255Sbde *
6130805Sbde */
6230805Sbdepublic class ConstructorGenerator extends ClassGenerator {
6326309Speter    private final ScriptClassInfo scriptClassInfo;
642056Swollman    private final String className;
6530805Sbde    private final MemberInfo constructor;
6631255Sbde    private final int memberCount;
674478Sbde    private final List<MemberInfo> specs;
6831255Sbde
6945720Speter    ConstructorGenerator(final ScriptClassInfo sci) {
702056Swollman        this.scriptClassInfo = sci;
7130805Sbde
723816Swollman        this.className = scriptClassInfo.getConstructorClassName();
7331255Sbde        this.constructor = scriptClassInfo.getConstructor();
742056Swollman        this.memberCount = scriptClassInfo.getConstructorMemberCount();
7526373Sdfr        this.specs = scriptClassInfo.getSpecializedConstructors();
762056Swollman    }
7731255Sbde
7860008Swollman    byte[] getClassBytes() {
794Srgrimes        // new class extending from ScriptObject
804Srgrimes        final String superClass = (constructor != null)? SCRIPTFUNCTION_TYPE : SCRIPTOBJECT_TYPE;
814Srgrimes        cw.visit(V1_7, ACC_FINAL, className, null, superClass, null);
824Srgrimes        if (memberCount > 0) {
834Srgrimes            // add fields
8419653Sbde            emitFields();
8519653Sbde            // add <clinit>
8619653Sbde            emitStaticInitializer();
8719653Sbde        }
8849081Scracauer        // add <init>
8919653Sbde        emitConstructor();
904Srgrimes
914Srgrimes        if (constructor == null) {
925351Sbde            emitGetClassName(scriptClassInfo.getName());
934Srgrimes        }
944Srgrimes
955351Sbde        cw.visitEnd();
9635215Sbde        return cw.toByteArray();
9735215Sbde    }
9835215Sbde
995351Sbde    // --Internals only below this point
1005351Sbde    private void emitFields() {
1014Srgrimes        // Introduce "Function" type instance fields for each
1024Srgrimes        // constructor @Function in script class and introduce instance
1034Srgrimes        // fields for each constructor @Property in the script class.
1044Srgrimes        for (MemberInfo memInfo : scriptClassInfo.getMembers()) {
1054Srgrimes            if (memInfo.isConstructorFunction()) {
1064Srgrimes                addFunctionField(memInfo.getJavaName());
1074Srgrimes                memInfo = (MemberInfo)memInfo.clone();
1084Srgrimes                memInfo.setJavaDesc(OBJECT_DESC);
1094Srgrimes                memInfo.setJavaAccess(ACC_PUBLIC);
1105351Sbde                addGetter(className, memInfo);
1114Srgrimes                addSetter(className, memInfo);
1124Srgrimes            } else if (memInfo.isConstructorProperty()) {
1134Srgrimes                if (memInfo.isStaticFinal()) {
1144Srgrimes                    addGetter(scriptClassInfo.getJavaName(), memInfo);
1154Srgrimes                } else {
1164Srgrimes                    addField(memInfo.getJavaName(), memInfo.getJavaDesc());
1174Srgrimes                    memInfo = (MemberInfo)memInfo.clone();
1184Srgrimes                    memInfo.setJavaAccess(ACC_PUBLIC);
1194Srgrimes                    addGetter(className, memInfo);
1204Srgrimes                    addSetter(className, memInfo);
1214Srgrimes                }
1224Srgrimes            }
12345720Speter        }
12445720Speter
12550181Speter        addMapField();
12645720Speter    }
12745720Speter
12845100Sdt    private void emitStaticInitializer() {
12933281Sbde        final MethodGenerator mi = makeStaticInitializer();
13033281Sbde        emitStaticInitPrefix(mi, className, memberCount);
13145100Sdt
1324Srgrimes        for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
1335351Sbde            if (memInfo.isConstructorFunction() || memInfo.isConstructorProperty()) {
13411865Sphk                linkerAddGetterSetter(mi, className, memInfo);
13511865Sphk            } else if (memInfo.isConstructorGetter()) {
13611865Sphk                final MemberInfo setter = scriptClassInfo.findSetter(memInfo);
13711865Sphk                linkerAddGetterSetter(mi, scriptClassInfo.getJavaName(), memInfo, setter);
13811865Sphk            }
13941591Sarchie        }
14066698Sjhb        emitStaticInitSuffix(mi, className);
14141591Sarchie    }
14241797Sbde
14341591Sarchie    private void emitConstructor() {
14441591Sarchie        final MethodGenerator mi = makeConstructor();
14541591Sarchie        mi.visitCode();
14626812Speter        callSuper(mi);
1474Srgrimes
1484Srgrimes        if (memberCount > 0) {
1494Srgrimes            // initialize Function type fields
15047226Speter            initFunctionFields(mi);
1514Srgrimes            // initialize data fields
15227567Sfsmp            initDataFields(mi);
1534Srgrimes        }
1544Srgrimes
1554Srgrimes        if (constructor != null) {
1565351Sbde            initPrototype(mi);
1574Srgrimes            final int arity = constructor.getArity();
15825164Speter            if (arity != MemberInfo.DEFAULT_ARITY) {
15935302Sbde                mi.loadThis();
16035302Sbde                mi.push(arity);
16135302Sbde                mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETARITY,
16246548Sbde                        SCRIPTFUNCTION_SETARITY_DESC);
16335302Sbde            }
16435302Sbde            final String doc = constructor.getDocumentation();
16535302Sbde            if (doc != null) {
16635302Sbde                mi.loadThis();
16735302Sbde                mi.loadLiteral(doc);
16835302Sbde                mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETDOCUMENTATION,
16935302Sbde                        SCRIPTFUNCTION_SETDOCUMENTATION_DESC);
17035302Sbde            }
17135302Sbde        }
17235302Sbde        mi.returnVoid();
17335302Sbde        mi.computeMaxs();
1744Srgrimes        mi.visitEnd();
1754Srgrimes    }
1764478Sbde
17735302Sbde    private void loadMap(final MethodGenerator mi) {
17835302Sbde        if (memberCount > 0) {
17935302Sbde            mi.getStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC);
18046548Sbde        }
18135302Sbde    }
18235302Sbde
18335302Sbde    private void callSuper(final MethodGenerator mi) {
18435302Sbde        String superClass, superDesc;
18535302Sbde        mi.loadThis();
1864Srgrimes        if (constructor == null) {
18727567Sfsmp            // call ScriptObject.<init>
1884Srgrimes            superClass = SCRIPTOBJECT_TYPE;
1894Srgrimes            superDesc = (memberCount > 0) ? SCRIPTOBJECT_INIT_DESC : DEFAULT_INIT_DESC;
19050181Speter            loadMap(mi);
19150181Speter        } else {
19250181Speter            // call Function.<init>
19350181Speter            superClass = SCRIPTFUNCTION_TYPE;
19450181Speter            superDesc = (memberCount > 0) ? SCRIPTFUNCTION_INIT_DESC4 : SCRIPTFUNCTION_INIT_DESC3;
19550181Speter            mi.loadLiteral(constructor.getName());
19650181Speter            mi.visitLdcInsn(new Handle(H_INVOKESTATIC, scriptClassInfo.getJavaName(), constructor.getJavaName(), constructor.getJavaDesc()));
19750181Speter            loadMap(mi);
19850181Speter            mi.memberInfoArray(scriptClassInfo.getJavaName(), specs); //pushes null if specs empty
19950181Speter        }
20050181Speter
20150181Speter        mi.invokeSpecial(superClass, INIT, superDesc);
20250181Speter    }
20350181Speter
20450181Speter    private void initFunctionFields(final MethodGenerator mi) {
2054Srgrimes        assert memberCount > 0;
2064Srgrimes        for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
2074Srgrimes            if (!memInfo.isConstructorFunction()) {
2084Srgrimes                continue;
2094Srgrimes            }
2104Srgrimes            mi.loadThis();
21145720Speter            newFunction(mi, scriptClassInfo.getJavaName(), memInfo, scriptClassInfo.findSpecializations(memInfo.getJavaName()));
21245720Speter            mi.putField(className, memInfo.getJavaName(), OBJECT_DESC);
2134Srgrimes        }
21446555Speter    }
21527567Sfsmp
21647226Speter    private void initDataFields(final MethodGenerator mi) {
21747226Speter        assert memberCount > 0;
21845720Speter        for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
21927567Sfsmp           if (!memInfo.isConstructorProperty() || memInfo.isFinal()) {
22027567Sfsmp               continue;
22127567Sfsmp           }
2224Srgrimes           final Object value = memInfo.getValue();
2234Srgrimes           if (value != null) {
2244Srgrimes               mi.loadThis();
2254Srgrimes               mi.loadLiteral(value);
2264Srgrimes               mi.putField(className, memInfo.getJavaName(), memInfo.getJavaDesc());
2274Srgrimes           } else if (!memInfo.getInitClass().isEmpty()) {
2284Srgrimes               final String clazz = memInfo.getInitClass();
2294Srgrimes               mi.loadThis();
2304Srgrimes               mi.newObject(clazz);
2314Srgrimes               mi.dup();
2324Srgrimes               mi.invokeSpecial(clazz, INIT, DEFAULT_INIT_DESC);
2334Srgrimes               mi.putField(className, memInfo.getJavaName(), memInfo.getJavaDesc());
2344Srgrimes           }
23547226Speter        }
23647226Speter    }
23747226Speter
2384Srgrimes    private void initPrototype(final MethodGenerator mi) {
2394Srgrimes        assert constructor != null;
2404Srgrimes        mi.loadThis();
2414Srgrimes        final String protoName = scriptClassInfo.getPrototypeClassName();
2424Srgrimes        mi.newObject(protoName);
2434Srgrimes        mi.dup();
24445720Speter        mi.invokeSpecial(protoName, INIT, DEFAULT_INIT_DESC);
24547226Speter        mi.dup();
24612929Sdg        mi.loadThis();
24712929Sdg        mi.invokeStatic(PROTOTYPEOBJECT_TYPE, PROTOTYPEOBJECT_SETCONSTRUCTOR,
2484Srgrimes                PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC);
24965557Sjasone        mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETPROTOTYPE, SCRIPTFUNCTION_SETPROTOTYPE_DESC);
25065557Sjasone    }
25165557Sjasone
25265557Sjasone    /**
25365557Sjasone     * Entry point for ConstructorGenerator run separately as an application. Will display
25465557Sjasone     * usage. Takes one argument, a class name.
2554Srgrimes     * @param args args vector
25645720Speter     * @throws IOException if class can't be read
2574Srgrimes     */
2584Srgrimes    public static void main(final String[] args) throws IOException {
2594Srgrimes        if (args.length != 1) {
2604Srgrimes            System.err.println("Usage: " + ConstructorGenerator.class.getName() + " <class>");
2614Srgrimes            System.exit(1);
2624Srgrimes        }
2634Srgrimes
26427567Sfsmp        final String className = args[0].replace('.', '/');
26527567Sfsmp        final ScriptClassInfo sci = getScriptClassInfo(className + ".class");
2664Srgrimes        if (sci == null) {
2674Srgrimes            System.err.println("No @ScriptClass in " + className);
2684Srgrimes            System.exit(2);
26945720Speter            throw new IOException(); // get rid of warning for sci.verify() below - may be null
27045720Speter        }
2714Srgrimes
27241591Sarchie        try {
2735351Sbde            sci.verify();
2745351Sbde        } catch (final Exception e) {
27541591Sarchie            System.err.println(e.getMessage());
2765351Sbde            System.exit(3);
2774Srgrimes        }
2784Srgrimes        final ConstructorGenerator gen = new ConstructorGenerator(sci);
2794Srgrimes        try (FileOutputStream fos = new FileOutputStream(className + CONSTRUCTOR_SUFFIX + ".class")) {
2804Srgrimes            fos.write(gen.getClassBytes());
2814Srgrimes        }
2824Srgrimes    }
2834Srgrimes}
2844Srgrimes