/** * Converts the given value to the given NativeObject (optionally following the prototype chains). * * @param nativeObject target NativeObject * @param value Value to convert * @param prototype use the prototype chain? */ public static Value toNativeObject( HostObject nativeObject, Value value, boolean prototype, Solver.SolverInterface solverInterface) { State state = solverInterface.getState(); boolean bad = false; Set<ObjectLabel> matches = Collections.newSet(); if (prototype) { // Make lookup using prototype chains Set<ObjectLabel> objectLabels = Collections.newSet(value.getObjectLabels()); Set<ObjectLabel> visited = Collections.newSet(); while (!objectLabels.isEmpty()) { Set<ObjectLabel> temp = Collections.newSet(); for (ObjectLabel objectLabel : objectLabels) { if (!visited.contains(objectLabel)) { visited.add(objectLabel); Value prototypeValue = state.readInternalPrototype(java.util.Collections.singleton(objectLabel)); prototypeValue = UnknownValueResolver.getRealValue(prototypeValue, state); // FIXME: Needs code review. Looks fishy to compare objects with toString(). String nativeObjectPrototype = nativeObject.toString(); String objectLabelPrototype = ""; if (objectLabel.getHostObject() != null) { objectLabelPrototype = objectLabel.getHostObject().toString(); } if (nativeObject == objectLabel.getHostObject() || nativeObjectPrototype.equals(objectLabelPrototype)) { matches.add(objectLabel); } else if (prototypeValue.getObjectLabels().isEmpty()) { bad = true; } else { temp.addAll(prototypeValue.getObjectLabels()); } } } objectLabels = temp; } } else { // Make lookup ignoring prototype chains // TODO: Verify this for (ObjectLabel objectLabel : value.getObjectLabels()) { if (objectLabel.getHostObject() == nativeObject || (objectLabel.getHostObject() != null && objectLabel.getHostObject().toString().equals(nativeObject + ".prototype"))) { matches.add(objectLabel); } else { bad = true; } } } // Message.Status status; // if (good && bad) { // status = Message.Status.MAYBE; // } else if (!good && bad) { // status = Message.Status.CERTAIN; // } else if (good && !bad) { // status = Message.Status.NONE; // } else if (!good && !bad) { // equivalent to Value of Undef / a null argument // // Considered a certain type error. // status = Message.Status.CERTAIN; // } else { // throw new AnalysisException("toNativeObject: fell through cases - should not // happen."); // } if (bad) { String message = "TypeError, argument is not of expected type: " + nativeObject; solverInterface .getMonitoring() .addMessage(solverInterface.getNode(), Message.Severity.HIGH, message); } return Value.makeObject(matches); }
/** 11.2.2, 11.2.3, 13.2.1, and 13.2.2 'new' / function call. */ @Override public void visit(CallNode n, State state) { if (n.getFunctionRegister() != AbstractNode.NO_VALUE) // old style call (where the function is given as a variable read) FunctionCalls.callFunction( new OrdinaryCallInfo(n, state) { @Override public Value getFunctionValue() { Value functionValue = state.readRegister(n.getFunctionRegister()); if (n.getLiteralConstructorKind() != null) { // these literal invocations can not be spurious in ES5 switch (n.getLiteralConstructorKind()) { case ARRAY: functionValue = Value.makeObject(new ObjectLabel(ECMAScriptObjects.ARRAY, Kind.FUNCTION)); break; case REGEXP: functionValue = Value.makeObject(new ObjectLabel(ECMAScriptObjects.REGEXP, Kind.FUNCTION)); break; default: throw new AnalysisException( "Unhandled literal constructor type: " + n.getLiteralConstructorKind()); } } return functionValue; } @Override public Set<ObjectLabel> prepareThis(State caller_state, State callee_state) { return UserFunctionCalls.determineThis( n, caller_state, callee_state, c, n.getBaseRegister()); } }, state, c); else { // getPropertyString / getPropertyRegister - like ReadPropertyNode Value baseval = state.readRegister(n.getBaseRegister()); baseval = UnknownValueResolver.getRealValue(baseval, state); Set<ObjectLabel> objlabels = baseval.getObjectLabels(); // the ReadPropertyNode has updated baseval to account for // ToObject Value propertyval; int propertyreg; if (n.isPropertyFixed()) { propertyreg = AbstractNode.NO_VALUE; propertyval = Value.makeStr(n.getPropertyString()); } else { propertyreg = n.getPropertyRegister(); propertyval = state.readRegister(propertyreg); propertyval = UnknownValueResolver.getRealValue(propertyval, state); } boolean maybe_undef = propertyval.isMaybeUndef(); boolean maybe_null = propertyval.isMaybeNull(); boolean maybe_nan = propertyval.isMaybeNaN(); propertyval = propertyval.restrictToNotNullNotUndef().restrictToNotNaN(); Str propertystr = Conversion.toString(propertyval, propertyreg, c); // read the object property value, as fixed property name or unknown property name, and // separately for "undefined"/"null"/"NaN" Map<ObjectLabel, Set<ObjectLabel>> target2this = newMap(); List<Value> nonfunctions = newList(); for (ObjectLabel objlabel : objlabels) { // find possible targets for each possible base object Set<ObjectLabel> singleton = singleton(objlabel); Value v; boolean read_undefined = false; boolean read_null = false; boolean read_nan = false; if (propertystr.isMaybeSingleStr()) { String propertyname = propertystr.getStr(); v = state.readPropertyValue(singleton, propertyname); } else if (!propertystr.isNotStr()) { v = state.readPropertyValue(singleton, propertystr); read_undefined = propertystr.isMaybeStr("undefined"); read_null = propertystr.isMaybeStr("null"); read_nan = propertystr.isMaybeStr("NaN"); } else v = Value.makeNone(); if (maybe_undef && !read_undefined) { v = UnknownValueResolver.join(v, state.readPropertyValue(singleton, "undefined"), state); } if (maybe_null && !read_null) { v = UnknownValueResolver.join(v, state.readPropertyValue(singleton, "null"), state); } if (maybe_nan && !read_nan) { v = UnknownValueResolver.join(v, state.readPropertyValue(singleton, "NaN"), state); } v = UnknownValueResolver.getRealValue(v, state); // finally, remove all the TAJS hooks, which are spurious if accessed through a dynamic // property if (!n.isPropertyFixed()) { v = JSGlobal.removeTAJSSpecificFunctions(v); } for (ObjectLabel target : v.getObjectLabels()) { if (target.getKind() == Kind.FUNCTION) { addToMapSet(target2this, target, objlabel); } else { nonfunctions.add(Value.makeObject(target)); } } if (v.isMaybePrimitive()) { nonfunctions.add(v.restrictToNotObject()); } } // do calls to each target with the corresponding values of this for (Entry<ObjectLabel, Set<ObjectLabel>> me : target2this.entrySet()) { ObjectLabel target = me.getKey(); Set<ObjectLabel> this_objs = me.getValue(); FunctionCalls.callFunction( new OrdinaryCallInfo(n, state) { @Override public Value getFunctionValue() { return Value.makeObject(target); } @Override public Set<ObjectLabel> prepareThis(State caller_state, State callee_state) { return this_objs; } }, state, c); } // also model calls to non-function values FunctionCalls.callFunction( new OrdinaryCallInfo(n, state) { @Override public Value getFunctionValue() { return Value.join(nonfunctions); } @Override public Set<ObjectLabel> prepareThis(State caller_state, State callee_state) { return Collections.emptySet(); } }, state, c); } }