PrototypeGenerator.java revision 6:5a1b0714df0e
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_PUBLIC;
29import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
30import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
31import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
32import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
33import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DESC;
34import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DUPLICATE;
35import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DUPLICATE_DESC;
36import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_FIELD_NAME;
37import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_TYPE;
38import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
39import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE;
40import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPE_SUFFIX;
41import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_INIT_DESC;
42
43import java.io.FileOutputStream;
44import java.io.IOException;
45
46/**
47 * This class generates prototype class for a @ClassInfo annotated class.
48 *
49 */
50public class PrototypeGenerator extends ClassGenerator {
51    private final ScriptClassInfo scriptClassInfo;
52    private final String className;
53    private final int memberCount;
54
55    PrototypeGenerator(final ScriptClassInfo sci) {
56        this.scriptClassInfo = sci;
57        this.className = scriptClassInfo.getPrototypeClassName();
58        this.memberCount = scriptClassInfo.getPrototypeMemberCount();
59    }
60
61    byte[] getClassBytes() {
62        // new class extensing from ScriptObject
63        cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, className, null, PROTOTYPEOBJECT_TYPE, null);
64        if (memberCount > 0) {
65            // add fields
66            emitFields();
67            // add <clinit>
68            emitStaticInitializer();
69        }
70        // add <init>
71        emitConstructor();
72
73        // add getClassName()
74        emitGetClassName(scriptClassInfo.getName());
75
76        cw.visitEnd();
77        return cw.toByteArray();
78    }
79
80    // --Internals only below this point
81    private void emitFields() {
82        // introduce "Function" type instance fields for each
83        // prototype @Function in script class info
84        for (MemberInfo memInfo : scriptClassInfo.getMembers()) {
85            if (memInfo.isPrototypeFunction()) {
86                addFunctionField(memInfo.getJavaName());
87                memInfo = (MemberInfo)memInfo.clone();
88                memInfo.setJavaDesc(OBJECT_DESC);
89                addGetter(className, memInfo);
90                addSetter(className, memInfo);
91            } else if (memInfo.isPrototypeProperty()) {
92                if (memInfo.isStaticFinal()) {
93                    addGetter(scriptClassInfo.getJavaName(), memInfo);
94                } else {
95                    addField(memInfo.getJavaName(), memInfo.getJavaDesc());
96                    memInfo = (MemberInfo)memInfo.clone();
97                    memInfo.setJavaAccess(ACC_PUBLIC);
98                    addGetter(className, memInfo);
99                    addSetter(className, memInfo);
100                }
101            }
102        }
103
104        addMapField();
105    }
106
107    private void emitStaticInitializer() {
108        final MethodGenerator mi = makeStaticInitializer();
109        emitStaticInitPrefix(mi, className);
110        for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
111            if (memInfo.isPrototypeFunction() || memInfo.isPrototypeProperty()) {
112                linkerAddGetterSetter(mi, className, memInfo);
113            } else if (memInfo.isPrototypeGetter()) {
114                final MemberInfo setter = scriptClassInfo.findSetter(memInfo);
115                linkerAddGetterSetter(mi, className, memInfo, setter);
116            }
117        }
118        emitStaticInitSuffix(mi, className);
119    }
120
121    private void emitConstructor() {
122        final MethodGenerator mi = makeConstructor();
123        mi.visitCode();
124        mi.loadThis();
125        if (memberCount > 0) {
126            // call "super(map$)"
127            mi.getStatic(className, MAP_FIELD_NAME, MAP_DESC);
128            // make sure we use duplicated PropertyMap so that original map
129            // stays intact and so can be used for many globals in same context
130            mi.invokeVirtual(MAP_TYPE, MAP_DUPLICATE, MAP_DUPLICATE_DESC);
131            mi.invokeSpecial(PROTOTYPEOBJECT_TYPE, INIT, SCRIPTOBJECT_INIT_DESC);
132            // initialize Function type fields
133            initFunctionFields(mi);
134        } else {
135            // call "super()"
136            mi.invokeSpecial(PROTOTYPEOBJECT_TYPE, INIT, DEFAULT_INIT_DESC);
137        }
138        mi.returnVoid();
139        mi.computeMaxs();
140        mi.visitEnd();
141    }
142
143    private void initFunctionFields(final MethodGenerator mi) {
144        for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
145            if (! memInfo.isPrototypeFunction()) {
146                continue;
147            }
148            mi.loadThis();
149            newFunction(mi, scriptClassInfo.getJavaName(), memInfo, scriptClassInfo.findSpecializations(memInfo.getJavaName()));
150            mi.putField(className, memInfo.getJavaName(), OBJECT_DESC);
151        }
152    }
153
154    /**
155     * External entry point for PrototypeGenerator if called from the command line
156     *
157     * @param args arguments, takes 1 argument which is the class to process
158     * @throws IOException if class cannot be read
159     */
160    public static void main(final String[] args) throws IOException {
161        if (args.length != 1) {
162            System.err.println("Usage: " + ConstructorGenerator.class.getName() + " <class>");
163            System.exit(1);
164        }
165
166        final String className = args[0].replace('.', '/');
167        final ScriptClassInfo sci = getScriptClassInfo(className + ".class");
168        if (sci == null) {
169            System.err.println("No @ScriptClass in " + className);
170            System.exit(2);
171            throw new AssertionError(); //guard against warning that sci is null below
172        }
173        try {
174            sci.verify();
175        } catch (final Exception e) {
176            System.err.println(e.getMessage());
177            System.exit(3);
178        }
179        final PrototypeGenerator gen = new PrototypeGenerator(sci);
180        try (FileOutputStream fos = new FileOutputStream(className + PROTOTYPE_SUFFIX + ".class")) {
181            fos.write(gen.getClassBytes());
182        }
183    }
184}
185