Path: blob/master/sourcetools/j9constantpool/com/ibm/oti/VMCPTool/Main.java
6004 views
/*******************************************************************************1* Copyright (c) 2004, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/21package com.ibm.oti.VMCPTool;2223import java.io.File;24import java.io.FileNotFoundException;25import java.io.FileReader;26import java.io.FileWriter;27import java.io.IOException;28import java.io.PrintWriter;29import java.io.StringWriter;30import java.util.HashMap;31import java.util.Map;32import java.util.Set;33import java.util.TreeMap;3435import javax.xml.parsers.DocumentBuilder;36import javax.xml.parsers.DocumentBuilderFactory;3738import org.w3c.dom.Document;39import org.w3c.dom.Element;40import org.w3c.dom.Node;41import org.w3c.dom.NodeList;4243@SuppressWarnings("nls")44public class Main implements Constants {4546static final class JCLRuntimeFlag {47final String flagName;48final int value;49int useCount;5051public JCLRuntimeFlag(String flagName, int value) {52this.flagName = flagName;53this.value = value;54this.useCount = 0;55}5657public String cDefine() {58StringBuilder buf = new StringBuilder();5960buf.append("#define JCL_RTFLAG_");6162for (int i = 0, length = flagName.length(); i < length; ++i) {63char c = flagName.charAt(i);64if (Character.isUpperCase(c)) {65buf.append("_");66}67buf.append(Character.toUpperCase(c));68}6970buf.append(" 0x").append(Integer.toHexString(value));7172return buf.toString();73}74}7576private static final String[] endianMacros = {77"#ifdef J9VM_ENV_LITTLE_ENDIAN",78"",79"/* U_16 U_8 U_8 */",80"#define WORD_BYTE_BYTE(a, b, c) ( ((U_32)a) | ((U_32)b << 16) | ((U_32)c << 24) )",81"/* U_8 U_8 U_8 U_8 */",82"#define BYTE_BYTE_BYTE_BYTE(a, b, c, d) ( ((U_32)a) | ((U_32)b << 8) | ((U_32)c << 16) | ((U_32)d << 24) )",83"/* U_8 U_8 U_16 */",84"#define BYTE_BYTE_WORD(a, b, c) ( ((U_32)a) | ((U_32)b << 8) | ((U_32)c << 16) )",85"/* U_16 U_16 */",86"#define WORD_WORD(a, b) ( ((U_32)a ) | ((U_32)b << 16 ) )",87"",88"#else /* J9VM_ENV_LITTLE_ENDIAN */",89"",90"/* U_16 U_8 U_8 */",91"#define WORD_BYTE_BYTE(a, b, c) ( ((U_32)a << 16) | ((U_32)b << 8) | ((U_32)c) )",92"/* U_8 U_8 U_8 U_8 */",93"#define BYTE_BYTE_BYTE_BYTE(a, b, c, d) ( ((U_32)a << 24) | ((U_32)b << 16) | ((U_32)c << 8) | ((U_32)d) )",94"/* U_8 U_8 U_16 */",95"#define BYTE_BYTE_WORD(a, b, c) ( ((U_32)a << 24) | ((U_32)b << 16) | ((U_32)c) )",96"/* U_16 U_16 */",97"#define WORD_WORD(a, b) ( ((U_32)a << 16) | ((U_32)b) )",98"",99"#endif /* J9VM_ENV_LITTLE_ENDIAN */"100};101102private static final String[] openDefinition = {103"/* Autogenerated file */",104"",105"#include \"j9.h\"",106"#include \"j9consts.h\""107};108109private static final String[] openHeader = {110"/* Autogenerated header */",111"",112"#ifndef J9VM_CONSTANT_POOL_H",113"#define J9VM_CONSTANT_POOL_H",114"",115"/* @ddr_namespace: map_to_type=J9VmconstantpoolConstants */"116};117118private static final String[] classMacros = {119"#define J9VMCONSTANTPOOL_CLASSREF_AT(vm, index) ((J9RAMClassRef*)(&(vm)->jclConstantPool[(index)]))",120"#define J9VMCONSTANTPOOL_CLASS_AT(vm, index) (J9VMCONSTANTPOOL_CLASSREF_AT(vm, index)->value == NULL \\",121"\t? (vm)->internalVMFunctions->resolveKnownClass(vm, index) \\",122"\t: J9VMCONSTANTPOOL_CLASSREF_AT(vm, index)->value)"123};124125private static final String[] fieldMacros = {126"#define J9VMCONSTANTPOOL_AT(vm, index, kind) ((kind*)&(vm)->jclConstantPool[index])",127"#define J9VMCONSTANTPOOL_FIELDREF_AT(vm, index) J9VMCONSTANTPOOL_AT(vm, index, J9RAMFieldRef)",128"#define J9VMCONSTANTPOOL_FIELD_OFFSET(vm, index) (J9JAVAVM_OBJECT_HEADER_SIZE(vm) + J9VMCONSTANTPOOL_FIELDREF_AT(vm, index)->valueOffset)",129"",130"#if !defined(J9VM_ENV_LITTLE_ENDIAN) && !defined(J9VM_ENV_DATA64)",131"#define J9VMCONSTANTPOOL_ADDRESS_OFFSET(vm, index) J9VMCONSTANTPOOL_FIELD_OFFSET(vm, index) + sizeof(UDATA)",132"#else",133"#define J9VMCONSTANTPOOL_ADDRESS_OFFSET(vm, index) J9VMCONSTANTPOOL_FIELD_OFFSET(vm, index)",134"#endif"135};136137private static final String[] staticFieldMacros = {138"#define J9VMCONSTANTPOOL_STATICFIELDREF_AT(vm, index) J9VMCONSTANTPOOL_AT(vm, index, J9RAMStaticFieldRef)",139"#define J9VMCONSTANTPOOL_STATICFIELD_ADDRESS(vm, index) (J9RAMSTATICFIELDREF_VALUEADDRESS(J9VMCONSTANTPOOL_STATICFIELDREF_AT(vm, index)))",140};141142private static final String[] staticMethodMacros = {143"#define J9VMCONSTANTPOOL_STATICMETHODREF_AT(vm, index) J9VMCONSTANTPOOL_AT(vm, index, J9RAMStaticMethodRef)",144"#define J9VMCONSTANTPOOL_STATICMETHOD_AT(vm, index) (J9VMCONSTANTPOOL_STATICMETHODREF_AT(vm, index)->method)"145};146147private static final String[] virtualMethodMacros = {148"#define J9VMCONSTANTPOOL_VIRTUALMETHODREF_AT(vm, index) J9VMCONSTANTPOOL_AT(vm, index, J9RAMVirtualMethodRef)",149"#define J9VMCONSTANTPOOL_VIRTUALMETHOD_AT(vm, index) (J9VMCONSTANTPOOL_VIRTUALMETHODREF_AT(vm, index)->methodIndexAndArgCount)"150};151152private static final String[] specialMethodMacros = {153"#define J9VMCONSTANTPOOL_SPECIALMETHODREF_AT(vm, index) J9VMCONSTANTPOOL_AT(vm, index, J9RAMSpecialMethodRef)",154"#define J9VMCONSTANTPOOL_SPECIALMETHOD_AT(vm, index) (J9VMCONSTANTPOOL_SPECIALMETHODREF_AT(vm, index)->method)"155};156157private static final String[] interfaceMethodMacros = {158"#define J9VMCONSTANTPOOL_INTERFACEMETHODREF_AT(vm, index) J9VMCONSTANTPOOL_AT(vm, index, J9RAMInterfaceMethodRef)",159"#define J9VMCONSTANTPOOL_INTERFACEMETHOD_AT(vm, index) (J9VMCONSTANTPOOL_INTERFACEMETHODREF_AT(vm, index)->methodIndexAndArgCount)"160};161162private static final String[] closeHeader = {163"#endif /* J9VM_CONSTANT_POOL_H */"164};165166private static final String optionBuildSpecId = "-buildSpecId";167private static final String optionHelp = "-help";168private static final String optionVersion = "-version";169private static final String optionRootDir = "-rootDir";170private static final String optionConfigDir = "-configDir";171private static final String optionOutputDir = "-outputDir";172private static final String optionCmakeCache = "-cmakeCache";173private static final String optionVerbose = "-verbose";174private static final String constantPool = "vmconstantpool.xml";175176private static String buildSpecId;177private static Integer version;178private static String configDirectory;179private static String rootDirectory = ".";180private static String outputDirectory;181private static String cmakeCache;182private static boolean verbose;183184private static IFlagInfo flagInfo;185private static TreeMap<String, JCLRuntimeFlag> runtimeFlagDefs;186187private static boolean parseOptions(String[] args) {188boolean isValid = true;189190try {191for (int i = 0; i < args.length; ++i) {192String arg = args[i];193if (optionHelp.equalsIgnoreCase(arg)) {194return false;195} else if (optionVersion.equalsIgnoreCase(arg)) {196String versionValue = args[++i];197try {198version = Integer.valueOf(versionValue);199} catch (NumberFormatException e) {200System.err.printf("ERROR: argument for '%s' must be numeric (%s)\n", optionVersion, versionValue);201isValid = false;202}203} else if (optionRootDir.equalsIgnoreCase(arg)) {204rootDirectory = args[++i];205} else if (optionConfigDir.equalsIgnoreCase(arg)) {206configDirectory = args[++i];207} else if (optionBuildSpecId.equalsIgnoreCase(arg)) {208buildSpecId = args[++i];209} else if (optionOutputDir.equalsIgnoreCase(arg)) {210outputDirectory = args[++i];211} else if (optionCmakeCache.equalsIgnoreCase(arg)) {212cmakeCache = args[++i];213} else if (optionVerbose.equalsIgnoreCase(arg)) {214verbose = true;215} else {216System.err.printf("Unrecognized option '%s'\n", arg);217return false;218}219}220} catch (ArrayIndexOutOfBoundsException e) {221return false;222}223224if (version == null) {225System.err.printf("ERROR: required argument '%s' not given\n", optionVersion);226isValid = false;227}228229if (rootDirectory == null) {230System.err.printf("ERROR: required argument '%s' not given\n", optionRootDir);231isValid = false;232}233234if (cmakeCache == null) {235if (configDirectory == null) {236System.err.printf("ERROR: required argument '%s' not given\n", optionConfigDir);237isValid = false;238}239if (buildSpecId == null) {240System.err.printf("ERROR: required argument '%s' not given\n", optionBuildSpecId);241isValid = false;242}243}244245return isValid;246}247248private static void printHelp() {249System.err.println(Main.class.getName() + ":");250System.err.println();251System.err.println("Usage:");252253String commonOptStr = "\t" + optionRootDir + " <directory> [" + optionOutputDir + " <directory>] ";254String trailingOptStr = optionVersion + " <version>";255256System.err.println(commonOptStr + optionConfigDir + " <directory> " + optionBuildSpecId + "<specId> " + trailingOptStr);257System.err.println(commonOptStr + optionCmakeCache + " <cacheFile> " + trailingOptStr);258}259260public static void main(String[] args) throws Throwable {261if (!parseOptions(args)) {262printHelp();263System.exit(1);264}265266if (cmakeCache != null) {267if (configDirectory != null) {268System.err.println("Ignoring option " + optionConfigDir);269}270if (buildSpecId != null) {271System.err.println("Ignoring option " + optionBuildSpecId);272}273flagInfo = new CmakeFlagInfo(cmakeCache);274} else {275flagInfo = new UmaFlagInfo(configDirectory, buildSpecId);276}277278ConstantPool pool = parseConstantPool();279280pool.removeNonApplicableItems(version.intValue(), flagInfo.getAllSetFlags());281282writeHeader(pool);283writeDefinition(pool, version.intValue(), flagInfo.getAllSetFlags());284}285286private static File getOutputFile(String directory, String fileName) throws FileNotFoundException {287File dir;288// If -outputDir was not set on commandline, default to outputting in the root directory289if (outputDirectory == null) {290dir = new File(rootDirectory, directory);291} else {292// Note: if -outputDir was specified we output all files in that directory (not in any sub directories)293// so we ignore the directory argument294dir = new File(outputDirectory);295}296297File file = new File(dir, fileName);298return file;299}300301private static void printOn(PrintWriter out, String[] lines) {302for (int i = 0; i < lines.length; ++i) {303out.println(lines[i]);304}305}306307private static void writeDefinition(ConstantPool pool, int version, Set<String> flags) throws Throwable {308System.out.println("Generating JCL constant pool definitions for Java " + version);309StringWriter buffer = new StringWriter();310PrintWriter out = new PrintWriter(buffer);311printOn(out, openDefinition);312out.println();313printOn(out, endianMacros);314out.println();315316pool.writeForClassLibrary(version, flags, out);317318out.flush();319out.close();320321writeToDisk(getOutputFile("jcl", "j9vmconstantpool.c"), buffer.toString());322}323324private static void writeHeader(ConstantPool pool) throws Throwable {325System.out.print("Generating header file ");326StringWriter buffer = new StringWriter();327PrintWriter out = new PrintWriter(buffer);328printOn(out, openHeader);329out.println();330331out.println("/* Runtime flag definitions */");332for (JCLRuntimeFlag flag : runtimeFlagDefs.values()) {333out.println(flag.cDefine());334}335out.println();336337printOn(out, classMacros);338out.println();339printOn(out, fieldMacros);340out.println();341printOn(out, staticFieldMacros);342out.println();343printOn(out, staticMethodMacros);344out.println();345printOn(out, virtualMethodMacros);346out.println();347printOn(out, specialMethodMacros);348out.println();349printOn(out, interfaceMethodMacros);350out.println();351pool.writeMacros(out);352out.println();353out.println("#define J9VM_VMCONSTANTPOOL_SIZE " + pool.constantPoolSize());354out.println();355printOn(out, closeHeader);356357out.flush();358out.close();359writeToDisk(getOutputFile("oti", "j9vmconstantpool.h"), buffer.toString());360}361362private static ConstantPool parseConstantPool() throws IOException {363NodeList nodes;364365try {366File dir = new File(rootDirectory, "oti");367File file = new File(dir, constantPool);368System.out.println("Reading constant pool from " + file.getPath());369DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();370Document document = builder.parse(file.toURI().toURL().toExternalForm());371nodes = document.getDocumentElement().getChildNodes();372} catch (Exception e) {373e.printStackTrace();374System.exit(-1);375return null; // unreachable376}377378Map<String, ClassRef> classes = new HashMap<>();379final int nodeCount = nodes.getLength();380381// Find classref elements.382for (int i = 0; i < nodeCount; ++i) {383Node node = nodes.item(i);384if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(CLASSREF)) {385Element element = (Element) node;386if (element.hasAttribute("name")) {387classes.put(element.getAttribute("name"), new ClassRef(element));388} else {389System.err.println("Missing name for " + CLASSREF + " element");390System.exit(-1);391}392}393}394395// Build constant pool396ConstantPool pool = new ConstantPool();397// TreeMap keeps flags ordered by name.398runtimeFlagDefs = new TreeMap<>();399400int lastFlagValue = 0x1;401JCLRuntimeFlag defaultFlag = new JCLRuntimeFlag("default", lastFlagValue);402runtimeFlagDefs.put("default", defaultFlag);403404for (int i = 0; i < nodeCount; ++i) {405Node node = nodes.item(i);406if (node.getNodeType() == Node.ELEMENT_NODE) {407PrimaryItem cpItem = cpItem((Element) node, classes);408409/* Extract and validate flags */410Node flagsNode = node.getAttributes().getNamedItem("flags");411if (null == flagsNode) {412defaultFlag.useCount += 1;413} else {414/* non-null flags= attribute */415String flagName = flagsNode.getTextContent();416417/* validate flags used in constantpool.xml */418if (!flagInfo.isFlagValid(flagName)) {419System.err.println("Invalid flag used ->" + flagName);420System.exit(-1);421}422423if (flagName.startsWith("!")) {424// skip flag w/ starting !425flagName = flagName.substring(1);426}427/* Find or create the flag object */428JCLRuntimeFlag flag = runtimeFlagDefs.get(flagName);429if (null == flag) {430lastFlagValue <<= 1;431flag = new JCLRuntimeFlag(flagName, lastFlagValue);432runtimeFlagDefs.put(flagName, flag);433}434435/* increment the useCount */436flag.useCount += 1;437}438439pool.add(cpItem);440}441}442443System.out.println("Found " + runtimeFlagDefs.size() + " flags used, declaring runtime constants.");444445for (JCLRuntimeFlag flag : runtimeFlagDefs.values()) {446System.out.println("\t" + flag.cDefine() + " (useCount=" + flag.useCount + ")");447}448449return pool;450}451452private static PrimaryItem cpItem(Element e, Map<String, ClassRef> classes) {453String type = e.getNodeName();454if (CLASSREF.equals(type)) {455return classes.get(e.getAttribute("name"));456} else if (FIELDREF.equals(type)) {457return new FieldRef(e, classes);458} else if (STATICFIELDREF.equals(type)) {459return new StaticFieldRef(e, classes);460} else if (STATICMETHODREF.equals(type)) {461return new StaticMethodRef(e, classes);462} else if (VIRTUALMETHODREF.equals(type)) {463return new VirtualMethodRef(e, classes);464} else if (SPECIALMETHODREF.equals(type)) {465return new SpecialMethodRef(e, classes);466} else if (INTERFACEMETHODREF.equals(type)) {467return new InterfaceMethodRef(e, classes);468} else {469System.err.println("Unrecognized node type: " + type);470System.exit(-1);471return null; // unreachable472}473}474475public static JCLRuntimeFlag getRuntimeFlag(String key) {476return runtimeFlagDefs.get(key);477}478479/**480* Checks if a file with given name exists on disk. Returns true if it does481* not exist. If the file exists, compares it with generated buffer and482* returns true if they are equal.483*484* @param fileName485* @param desiredContent486* @return boolean TRUE|FALSE487*/488private static boolean differentFromCopyOnDisk(String fileName, String desiredContent) {489File fileOnDisk = new File(fileName);490boolean returnValue = true;491492if (fileOnDisk.exists()) {493StringBuilder fileBuffer = new StringBuilder();494try {495try (FileReader fr = new FileReader(fileOnDisk)) {496char charArray[] = new char[1024];497498int numRead = -1;499while ((numRead = fr.read(charArray)) != -1) {500fileBuffer.append(charArray, 0, numRead);501}502503if (desiredContent.equals(fileBuffer.toString())) {504returnValue = false;505}506}507} catch (IOException e) {508// ignore509}510}511512return returnValue;513}514515/**516* Compares the given buffer and file and if they are different it writes517* the buffer into a new file.518*519* @param file520* @param desiredContent521* @return522* @throws IOException523*/524private static void writeToDisk(File file, String desiredContent) throws IOException {525if (differentFromCopyOnDisk(file.getPath(), desiredContent)) {526System.out.println("** Writing " + file.getPath());527file.delete();528529try (FileWriter fw = new FileWriter(file.getPath())) {530fw.write(desiredContent);531}532} else if (verbose) {533System.out.println("** Skipped writing [same as on file system]: " + file.getPath());534}535}536537}538539540