// // Copyright (C) 2006 United States Government as represented by the // Administrator of the National Aeronautics and Space Administration // (NASA). All Rights Reserved. // // This software is distributed under the NASA Open Source Agreement // (NOSA), version 1.3. The NOSA has been approved by the Open Source // Initiative. See the file NOSA-1.3-JPF at the top of the distribution // directory tree for the complete NOSA document. // // THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY // KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT // LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO // SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR // A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT // THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT // DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. // package gov.nasa.jpf.util.json; import java.util.HashMap; import java.util.Set; import cmu.conditional.One; import de.fosd.typechef.featureexpr.FeatureExpr; import de.fosd.typechef.featureexpr.FeatureExprFactory; import gov.nasa.jpf.JPF; import gov.nasa.jpf.JPFException; import gov.nasa.jpf.util.JPFLogger; import gov.nasa.jpf.util.ObjectConverter; import gov.nasa.jpf.vm.ChoiceGenerator; import gov.nasa.jpf.vm.ClassInfo; import gov.nasa.jpf.vm.ClinitRequired; import gov.nasa.jpf.vm.ElementInfo; import gov.nasa.jpf.vm.FieldInfo; import gov.nasa.jpf.vm.Fields; import gov.nasa.jpf.vm.MJIEnv; import gov.nasa.jpf.vm.ThreadInfo; /** * Object parsed from JSON document. * @author Ivan Mushketik */ public class JSONObject{ private static final JPFLogger logger = JPF.getLogger("gov.nasa.jpf.util.json.JSONObject"); private HashMap<String, Value> keyValues = new HashMap<String, Value>(); private HashMap<String, CGCall> cgCalls = new HashMap<String, CGCall>(); void addValue(String key, Value value) { if (keyValues.containsKey(key)) { throw new JPFException("Attempt to add two nodes with the same key in JSON object"); } keyValues.put(key, value); } /** * Get value read from JSON document with specified key. * @param key - value's key. * @return read value. */ public Value getValue(String key) { return keyValues.get(key); } public String[] getValuesKeys() { Set<String> valuesKeys = keyValues.keySet(); String[] result = new String[keyValues.size()]; valuesKeys.toArray(result); return result; } public void addCGCall(String key, CGCall cgCall) { if (cgCalls.containsKey(key)) { throw new JPFException("Attempt to add two CG with the same key in JSON object"); } cgCalls.put(key, cgCall); } public CGCall getCGCall(String key) { return cgCalls.get(key); } public String[] getCGCallsKeys() { Set<String> cgKeys = cgCalls.keySet(); String[] result = new String[cgKeys.size()]; cgKeys.toArray(result); return result; } /** * check if all required ClassInfos for this object have been initialized so * that the caller can decide if it has to re-execute before proceeding * * NOTE - this currently does not support concrete field types that are subtypes * of the respective field types */ public boolean requiresClinitExecution (ClassInfo ci, ThreadInfo ti){ while (ci != null){ if (ci.pushRequiredClinits(null, ti)){ return true; } for (FieldInfo fi : ci.getDeclaredInstanceFields()) { ClassInfo ciField = fi.getTypeClassInfo(); if (requiresClinitExecution(ciField, ti)){ return true; } } ci = ci.getSuperClass(); } return false; } //--- the fillers // NOTE - (pcm) before calling this method you have to make sure all required // types are initialized public int fillObject (FeatureExpr ctx, MJIEnv env, ClassInfo ci, ChoiceGenerator<?>[] cgs, String prefix) throws ClinitRequired { int newObjRef = env.newObject(ctx, ci); ElementInfo ei = env.getHeap().getModifiable(newObjRef); // Fill all fields for this class until it has a super class while (ci != null) { FieldInfo[] fields = ci.getDeclaredInstanceFields(); for (FieldInfo fi : fields) { String fieldName = fi.getName(); Value val = getValue(fieldName); CGCall cgCall = getCGCall(fieldName); // If a value was defined in JSON document if (val != null) { fillFromValue(ctx, fi, ei, val, env, cgs, prefix); } else if (cgCall != null) { // Value of this field should be taken from CG String cgId = prefix + fieldName; ChoiceGenerator<?> cg = getCGByID(cgs, cgId); assert cg != null : "Expected CG with id " + cgId; Object cgResult = cg.getNextChoice(); if (!fi.isReference()) { convertPrimititve(ei, fi, cgResult); } else { int newFieldRef = ObjectConverter.JPFObjectFromJavaObject(ctx, env, cgResult); ei.setReferenceField(ctx, fi, new One<>(newFieldRef)); } } else { logger.warning("Value for field ", fi.getFullName(), " isn't specified"); } } ci = ci.getSuperClass(); } return newObjRef; } private void fillFromValue(FeatureExpr ctx, FieldInfo fi, ElementInfo ei, Value val, MJIEnv env, ChoiceGenerator<?>[] cgs, String prefix) { String fieldName = fi.getName(); // Handle primitive types if (!fi.isReference()) { fillPrimitive(ei, fi, val); } else { if (isArrayType(fi.getType())) { int newArrRef = createArray(ctx, env, fi.getTypeClassInfo(), val, cgs, prefix + fieldName); ei.setReferenceField(ctx, fi, new One<>(newArrRef)); } else { Creator creator = CreatorsFactory.getCreator(fi.getType()); if (creator != null) { int newSubObjRef = creator.create(ctx, env, fi.getType(), val); ei.setReferenceField(ctx, fi, new One<>(newSubObjRef)); } else { // Not a special case. Fill it recursively ClassInfo ciField = fi.getTypeClassInfo(); if (ciField.pushRequiredClinits(ctx, env.getThreadInfo())){ throw new ClinitRequired(ciField); } JSONObject jsonObj = val.getObject(); int fieldRef = MJIEnv.NULL; if (jsonObj != null) { fieldRef = jsonObj.fillObject(ctx, env, ciField, cgs, prefix + fieldName); } ei.setReferenceField(ctx, fi.getName(), new One<>(fieldRef)); } } } } private static void fillPrimitive(ElementInfo ei, FieldInfo fi, Value val) { String primitiveName = fi.getType(); FeatureExpr ctx = FeatureExprFactory.True(); if (primitiveName.equals("boolean")) { ei.setBooleanField(ctx, fi, new One<>(val.getBoolean())); } else if (primitiveName.equals("byte")) { ei.setByteField(ctx, fi, new One<>(val.getDouble().byteValue())); } else if (primitiveName.equals("short")) { ei.setShortField(ctx, fi, new One<>(val.getDouble().shortValue())); } else { if (primitiveName.equals("int")) { ei.setIntField(ctx, fi, new One<>(val.getDouble().intValue())); } else if (primitiveName.equals("long")) { ei.setLongField(ctx, fi, new One<>(val.getDouble().longValue())); } else if (primitiveName.equals("float")) { ei.setFloatField(ctx, fi, new One<>(val.getDouble().floatValue())); } else if (primitiveName.equals("double")) { ei.setDoubleField(ctx, fi, new One<>(val.getDouble())); } } } public int createArray(FeatureExpr ctx, MJIEnv env, ClassInfo ciArray, Value value, ChoiceGenerator<?>[] cgs, String prefix) { Value vals[] = value.getArray(); ClassInfo ciElement = ciArray.getComponentClassInfo(); String arrayElementType = ciElement.getName(); int arrayRef; // Handle arrays of primitive types if (arrayElementType.equals("boolean")) { arrayRef = env.newBooleanArray(vals.length); ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef); // Conditional<Boolean>[] bools = arrayEI.asBooleanArray(); for (int i = 0; i < vals.length; i++) { arrayEI.setBooleanElement(ctx, i, new One<>(vals[i].getBoolean())); // bools[i] = vals[i].getBoolean(); } } else if (arrayElementType.equals("byte")) { arrayRef = env.newByteArray(vals.length); ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef); // byte bytes[] = arrayEI.asByteArray(); // for (int i = 0; i < vals.length; i++) { // bytes[i] = vals[i].getDouble().byteValue(); // } for (int i = 0; i < vals.length; i++) { arrayEI.setByteElement(ctx, i, new One<>(vals[i].getDouble().byteValue())); } } else if (arrayElementType.equals("short")) { arrayRef = env.newShortArray(vals.length); ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef); // short shorts[] = arrayEI.asShortArray(); for (int i = 0; i < vals.length; i++) { arrayEI.setShortElement(ctx, i, new One<>(vals[i].getDouble().shortValue())); // shorts[i] = vals[i].getDouble().shortValue(); } } else if (arrayElementType.equals("int")) { arrayRef = env.newIntArray(vals.length); ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef); // Conditional<Integer>[] array = arrayEI.asIntArray(); for (int i = 0; i < vals.length; i++) { arrayEI.setIntElement(ctx, i, new One<>(vals[i].getDouble().intValue())); // ints[i] = vals[i].getDouble().intValue(); } } else if (arrayElementType.equals("long")) { arrayRef = env.newLongArray(vals.length); ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef); // long[] longs = arrayEI.asLongArray(); for (int i = 0; i < vals.length; i++) { arrayEI.setLongElement(ctx, i, new One<>(vals[i].getDouble().longValue())); // longs[i] = vals[i].getDouble().longValue(); } } else if (arrayElementType.equals("float")) { arrayRef = env.newFloatArray(vals.length); ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef); // Conditional<Float>[] floats = arrayEI.asFloatArray(); for (int i = 0; i < vals.length; i++) { arrayEI.setFloatElement(ctx, i, new One<>(vals[i].getDouble().floatValue())); // floats[i] = vals[i].getDouble().floatValue(); } } else if (arrayElementType.equals("double")) { arrayRef = env.newDoubleArray(vals.length); ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef); for (int i = 0; i < vals.length; i++) { arrayEI.setDoubleElement(ctx, i, new One<>(vals[i].getDouble())); } // double[] doubles = arrayEI.asDoubleArray(); // for (int i = 0; i < vals.length; i++) { // doubles[i] = vals[i].getDouble(); // } } else { // Not an array of primitive types arrayRef = env.newObjectArray(arrayElementType, vals.length); ElementInfo arrayEI = env.getModifiableElementInfo(arrayRef); Fields fields = arrayEI.getFields(); Creator creator = CreatorsFactory.getCreator(arrayElementType); for (int i = 0; i < vals.length; i++) { int newObjRef; if (creator != null) { newObjRef = creator.create(ctx, env, arrayElementType, vals[i]); } else{ if (isArrayType(arrayElementType)) { newObjRef = createArray(ctx, env, ciElement, vals[i], cgs, prefix + "[" + i); } else { JSONObject jsonObj = vals[i].getObject(); if (jsonObj != null) { newObjRef = jsonObj.fillObject(ctx, env, ciElement, cgs, prefix + "[" + i); } else { newObjRef = MJIEnv.NULL; } } } fields.setReferenceValue(FeatureExprFactory.True(), i, new One<>(newObjRef)); } } return arrayRef; } private boolean isArrayType(String typeName) { return typeName.lastIndexOf('[') >= 0; } /** * This is method is used to set field of primitive type from CG result object * @param ei - ElementInfo to set field in * @param fi - FieldInfo of a field we want to set * @param cgResult - result of CG call */ private void convertPrimititve(ElementInfo ei, FieldInfo fi, Object cgResult) { String primitiveName = fi.getType(); FeatureExpr ctx = FeatureExprFactory.True(); if (primitiveName.equals("boolean") && cgResult instanceof Boolean) { ei.setBooleanField(ctx, fi, new One<>((Boolean) cgResult)); } else if (cgResult instanceof Number) { Number number = (Number) cgResult; if (primitiveName.equals("byte")) { ei.setByteField(ctx, fi, new One<>(number.byteValue())); } else if (primitiveName.equals("short")) { ei.setShortField(ctx, fi, new One<>(number.shortValue())); } else { if (primitiveName.equals("int")) { ei.setIntField(ctx, fi, new One<>(number.intValue())); } else if (primitiveName.equals("long")) { ei.setLongField(ctx, fi, new One<>(number.longValue())); } else if (primitiveName.equals("float")) { ei.setFloatField(ctx, fi, new One<>(number.floatValue())); } else if (primitiveName.equals("double")) { ei.setDoubleField(ctx, fi, new One<>(number.doubleValue())); } } } else if (cgResult instanceof Character) { Character c = (Character) cgResult; ei.setCharField(FeatureExprFactory.True(), fi, new One<>(c)); } else { throw new JPFException("Can't convert " + cgResult.getClass().getCanonicalName() + " to " + primitiveName); } } /** * Get CG from current state CG list by it's ID * @param cgs - array of CG from current state * @param id - id of the CG that we search for * @return - CG with a specified id or null if no id with such name found */ private ChoiceGenerator<?> getCGByID(ChoiceGenerator<?>[] cgs, String id) { if (cgs == null) { return null; } for (int i = 0; i < cgs.length; i++) { if (cgs[i].getId().equals(id)) { return cgs[i]; } } return null; } }