/*
 * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.svm.hosted.c.query;

import static com.oracle.svm.core.util.VMError.shouldNotReachHere;
import static com.oracle.svm.hosted.c.query.QueryParserUtil.parseHexToLong;
import static com.oracle.svm.hosted.c.query.QueryParserUtil.parseSigned;
import static com.oracle.svm.hosted.c.query.QueryParserUtil.unsignedExtendToSize;

import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.oracle.svm.hosted.c.NativeLibraries;
import com.oracle.svm.hosted.c.info.AccessorInfo;
import com.oracle.svm.hosted.c.info.ConstantInfo;
import com.oracle.svm.hosted.c.info.ElementInfo;
import com.oracle.svm.hosted.c.info.EnumConstantInfo;
import com.oracle.svm.hosted.c.info.NativeCodeInfo;
import com.oracle.svm.hosted.c.info.PointerToInfo;
import com.oracle.svm.hosted.c.info.PropertyInfo;
import com.oracle.svm.hosted.c.info.RawStructureInfo;
import com.oracle.svm.hosted.c.info.SizableInfo;
import com.oracle.svm.hosted.c.info.StructBitfieldInfo;
import com.oracle.svm.hosted.c.info.StructFieldInfo;
import com.oracle.svm.hosted.c.info.StructInfo;
import com.oracle.svm.hosted.c.info.SizableInfo.ElementKind;
import com.oracle.svm.hosted.c.info.SizableInfo.SignednessValue;
import com.oracle.svm.hosted.c.util.FileUtils;

import jdk.vm.ci.meta.JavaKind;

Parses query result described in QueryResultFormat.
/** * Parses query result described in {@link QueryResultFormat}. */
public final class QueryResultParser extends NativeInfoTreeVisitor { private final Map<String, String> idToResult; private QueryResultParser(NativeLibraries nativeLibs) { super(nativeLibs); this.idToResult = new HashMap<>(); } public static List<String> parse(NativeLibraries nativeLibs, NativeCodeInfo nativeCodeInfo, InputStream source) { QueryResultParser parser = new QueryResultParser(nativeLibs); List<String> lines = FileUtils.readAllLines(source); for (String line : lines) { String[] keyValuePair = line.split(QueryResultFormat.DELIMINATOR); assert keyValuePair.length == 2; parser.idToResult.put(keyValuePair[0], keyValuePair[1]); } nativeCodeInfo.accept(parser); return lines; } @Override protected void visitConstantInfo(ConstantInfo constantInfo) { switch (constantInfo.getKind()) { case INTEGER: parseIntegerProperty(constantInfo.getSizeInfo()); parseSignedness(constantInfo.getSignednessInfo()); parseIntegerConstantValue(constantInfo.getValueInfo()); /* * From the point of view of the C compiler, plain #define constants have the type * int and therefore size 4. But sometimes we want to access such values as short or * byte to avoid casts. Check the actual value of the constant, and if it fits the * declared type of the constant, then change the actual size to the declared size. */ JavaKind returnKind = AccessorInfo.getReturnType(constantInfo.getAnnotatedElement()).getJavaKind(); if (returnKind == JavaKind.Object) { returnKind = nativeLibs.getTarget().wordJavaKind; } int declaredSize = getSizeInBytes(returnKind); int actualSize = constantInfo.getSizeInfo().getProperty(); if (declaredSize != actualSize) { long value = (long) constantInfo.getValueInfo().getProperty(); if (value >= returnKind.getMinValue() && value <= returnKind.getMaxValue()) { constantInfo.getSizeInfo().setProperty(declaredSize); } } break; case POINTER: parseIntegerProperty(constantInfo.getSizeInfo()); parseIntegerConstantValue(constantInfo.getValueInfo()); break; case FLOAT: parseIntegerProperty(constantInfo.getSizeInfo()); parseFloatValue(constantInfo.getValueInfo()); break; case STRING: parseStringValue(constantInfo.getValueInfo()); break; case BYTEARRAY: parseByteArrayValue(constantInfo.getValueInfo()); break; default: throw shouldNotReachHere(); } } @Override public void visitStructInfo(StructInfo structInfo) { if (!structInfo.isIncomplete()) { parseIntegerProperty(structInfo.getSizeInfo()); } processChildren(structInfo); } @Override protected void visitRawStructureInfo(RawStructureInfo info) { /* Nothing to do, do not visit children. */ } @Override public void visitStructFieldInfo(StructFieldInfo fieldInfo) { parseIntegerProperty(fieldInfo.getSizeInfo()); parseIntegerProperty(fieldInfo.getOffsetInfo()); if (fieldInfo.getKind() == ElementKind.INTEGER) { parseSignedness(fieldInfo.getSignednessInfo()); } } @Override public void visitStructBitfieldInfo(StructBitfieldInfo bitfieldInfo) { parseIntegerProperty(bitfieldInfo.getByteOffsetInfo()); parseIntegerProperty(bitfieldInfo.getStartBitInfo()); parseIntegerProperty(bitfieldInfo.getEndBitInfo()); parseSignedness(bitfieldInfo.getSignednessInfo()); } @Override public void visitPointerToInfo(PointerToInfo pointerToInfo) { parseIntegerProperty(pointerToInfo.getSizeInfo()); if (pointerToInfo.getKind() == ElementKind.INTEGER) { parseSignedness(pointerToInfo.getSignednessInfo()); } } @Override protected void visitEnumConstantInfo(EnumConstantInfo constantInfo) { assert constantInfo.getKind() == ElementKind.INTEGER; parseIntegerProperty(constantInfo.getSizeInfo()); parseSignedness(constantInfo.getSignednessInfo()); parseIntegerConstantValue(constantInfo.getValueInfo()); } private void parseSignedness(PropertyInfo<SignednessValue> info) { info.setProperty(SignednessValue.valueOf(stringLiteral(info))); } private void parseIntegerConstantValue(PropertyInfo<Object> info) { boolean isUnsigned = ((SizableInfo) info.getParent()).isUnsigned(); int size = ((SizableInfo) info.getParent()).getSizeInfo().getProperty(); String hex = idToResult.get(info.getUniqueID()); int hexSize = hex.length() / 2; if (hexSize < size) { hex = unsignedExtendToSize(size, hex); } if (isUnsigned) { parseHexToLong(info, hex); } else { parseSigned(info, hex); } } private void parseFloatValue(PropertyInfo<Object> info) { String str = idToResult.get(info.getUniqueID()); double value = Double.parseDouble(str); info.setProperty(value); } private void parseStringValue(PropertyInfo<Object> info) { info.setProperty(stringLiteral(info)); } private String stringLiteral(ElementInfo info) { String str = idToResult.get(info.getUniqueID()); if (str.startsWith(QueryResultFormat.STRING_MARKER) && str.endsWith(QueryResultFormat.STRING_MARKER)) { return str.substring(QueryResultFormat.STRING_MARKER.length(), str.length() - QueryResultFormat.STRING_MARKER.length()); } else { addError("String constant not deliminated correctly", info); return ""; } } private void parseByteArrayValue(PropertyInfo<Object> info) { info.setProperty(byteArrayLiteral(info)); } private byte[] byteArrayLiteral(ElementInfo info) { String str = stringLiteral(info); if (!str.isEmpty()) { return str.getBytes(Charset.forName("UTF-8")); } else { return new byte[0]; } } private void parseIntegerProperty(PropertyInfo<Integer> info) { int value = Integer.parseInt(idToResult.get(info.getUniqueID())); info.setProperty(value); } }