/ API / JSValueRef.cpp
JSValueRef.cpp
  1  /*
  2   * Copyright (C) 2006-2020 Apple Inc. All rights reserved.
  3   *
  4   * Redistribution and use in source and binary forms, with or without
  5   * modification, are permitted provided that the following conditions
  6   * are met:
  7   * 1. Redistributions of source code must retain the above copyright
  8   *    notice, this list of conditions and the following disclaimer.
  9   * 2. Redistributions in binary form must reproduce the above copyright
 10   *    notice, this list of conditions and the following disclaimer in the
 11   *    documentation and/or other materials provided with the distribution.
 12   *
 13   * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 14   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 15   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 17   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 18   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 19   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 20   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 21   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 22   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 23   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 24   */
 25  
 26  #include "config.h"
 27  #include "JSValueRef.h"
 28  
 29  #include "APICast.h"
 30  #include "APIUtils.h"
 31  #include "DateInstance.h"
 32  #include "JSAPIWrapperObject.h"
 33  #include "JSCInlines.h"
 34  #include "JSCallbackObject.h"
 35  #include "JSONObject.h"
 36  #include "LiteralParser.h"
 37  #include "Protect.h"
 38  #include <wtf/Assertions.h>
 39  #include <wtf/text/WTFString.h>
 40  
 41  #if PLATFORM(MAC)
 42  #include <mach-o/dyld.h>
 43  #endif
 44  
 45  #if ENABLE(REMOTE_INSPECTOR)
 46  #include "JSGlobalObjectInspectorController.h"
 47  #endif
 48  
 49  using namespace JSC;
 50  
 51  ::JSType JSValueGetType(JSContextRef ctx, JSValueRef value)
 52  {
 53      if (!ctx) {
 54          ASSERT_NOT_REACHED();
 55          return kJSTypeUndefined;
 56      }
 57  #if !CPU(ADDRESS64)
 58      JSGlobalObject* globalObject = toJS(ctx);
 59      JSLockHolder locker(globalObject);
 60      JSValue jsValue = toJS(globalObject, value);
 61  #else
 62      JSValue jsValue = toJS(value);
 63  #endif
 64  
 65      if (jsValue.isUndefined())
 66          return kJSTypeUndefined;
 67      if (!jsValue || jsValue.isNull())
 68          return kJSTypeNull;
 69      if (jsValue.isBoolean())
 70          return kJSTypeBoolean;
 71      if (jsValue.isNumber())
 72          return kJSTypeNumber;
 73      if (jsValue.isString())
 74          return kJSTypeString;
 75      if (jsValue.isSymbol())
 76          return kJSTypeSymbol;
 77      ASSERT(jsValue.isObject());
 78      return kJSTypeObject;
 79  }
 80  
 81  bool JSValueIsUndefined(JSContextRef ctx, JSValueRef value)
 82  {
 83      if (!ctx) {
 84          ASSERT_NOT_REACHED();
 85          return false;
 86      }
 87  #if !CPU(ADDRESS64)
 88      JSGlobalObject* globalObject = toJS(ctx);
 89      JSLockHolder locker(globalObject);
 90      return toJS(globalObject, value).isUndefined();
 91  #else
 92      return toJS(value).isUndefined();
 93  #endif
 94  }
 95  
 96  bool JSValueIsNull(JSContextRef ctx, JSValueRef value)
 97  {
 98      if (!ctx) {
 99          ASSERT_NOT_REACHED();
100          return false;
101      }
102  
103  #if !CPU(ADDRESS64)
104      JSGlobalObject* globalObject = toJS(ctx);
105      JSLockHolder locker(globalObject);
106      return toJS(globalObject, value).isNull();
107  #else
108      return !value || toJS(value).isNull();
109  #endif
110  }
111  
112  bool JSValueIsBoolean(JSContextRef ctx, JSValueRef value)
113  {
114      if (!ctx) {
115          ASSERT_NOT_REACHED();
116          return false;
117      }
118  #if !CPU(ADDRESS64)
119      JSGlobalObject* globalObject = toJS(ctx);
120      JSLockHolder locker(globalObject);
121      return toJS(globalObject, value).isBoolean();
122  #else
123      return toJS(value).isBoolean();
124  #endif
125  }
126  
127  bool JSValueIsNumber(JSContextRef ctx, JSValueRef value)
128  {
129      if (!ctx) {
130          ASSERT_NOT_REACHED();
131          return false;
132      }
133  #if !CPU(ADDRESS64)
134      JSGlobalObject* globalObject = toJS(ctx);
135      JSLockHolder locker(globalObject);
136      return toJS(globalObject, value).isNumber();
137  #else
138      return toJS(value).isNumber();
139  #endif
140  }
141  
142  bool JSValueIsString(JSContextRef ctx, JSValueRef value)
143  {
144      if (!ctx) {
145          ASSERT_NOT_REACHED();
146          return false;
147      }
148  #if !CPU(ADDRESS64)
149      JSGlobalObject* globalObject = toJS(ctx);
150      JSLockHolder locker(globalObject);
151      return toJS(globalObject, value).isString();
152  #else
153      return value && toJS(value).isString();
154  #endif
155  }
156  
157  bool JSValueIsObject(JSContextRef ctx, JSValueRef value)
158  {
159      if (!ctx) {
160          ASSERT_NOT_REACHED();
161          return false;
162      }
163  #if !CPU(ADDRESS64)
164      JSGlobalObject* globalObject = toJS(ctx);
165      JSLockHolder locker(globalObject);
166      return toJS(globalObject, value).isObject();
167  #else
168      return value && toJS(value).isObject();
169  #endif
170  }
171  
172  bool JSValueIsSymbol(JSContextRef ctx, JSValueRef value)
173  {
174      if (!ctx) {
175          ASSERT_NOT_REACHED();
176          return false;
177      }
178  #if !CPU(ADDRESS64)
179      JSGlobalObject* globalObject = toJS(ctx);
180      JSLockHolder locker(globalObject);
181      return toJS(globalObject, value).isSymbol();
182  #else
183      return value && toJS(value).isSymbol();
184  #endif
185  }
186  
187  bool JSValueIsArray(JSContextRef ctx, JSValueRef value)
188  {
189      if (!ctx) {
190          ASSERT_NOT_REACHED();
191          return false;
192      }
193      JSGlobalObject* globalObject = toJS(ctx);
194      VM& vm = globalObject->vm();
195      JSLockHolder locker(globalObject);
196  
197      return toJS(globalObject, value).inherits<JSArray>(vm);
198  }
199  
200  bool JSValueIsDate(JSContextRef ctx, JSValueRef value)
201  {
202      if (!ctx) {
203          ASSERT_NOT_REACHED();
204          return false;
205      }
206      JSGlobalObject* globalObject = toJS(ctx);
207      VM& vm = globalObject->vm();
208      JSLockHolder locker(globalObject);
209  
210      return toJS(globalObject, value).inherits<DateInstance>(vm);
211  }
212  
213  bool JSValueIsObjectOfClass(JSContextRef ctx, JSValueRef value, JSClassRef jsClass)
214  {
215      if (!ctx || !jsClass) {
216          ASSERT_NOT_REACHED();
217          return false;
218      }
219      JSGlobalObject* globalObject = toJS(ctx);
220      VM& vm = globalObject->vm();
221      JSLockHolder locker(globalObject);
222  
223      JSValue jsValue = toJS(globalObject, value);
224      
225      if (JSObject* o = jsValue.getObject()) {
226          if (o->inherits<JSProxy>(vm))
227              o = jsCast<JSProxy*>(o)->target();
228  
229          if (o->inherits<JSCallbackObject<JSGlobalObject>>(vm))
230              return jsCast<JSCallbackObject<JSGlobalObject>*>(o)->inherits(jsClass);
231          if (o->inherits<JSCallbackObject<JSNonFinalObject>>(vm))
232              return jsCast<JSCallbackObject<JSNonFinalObject>*>(o)->inherits(jsClass);
233  #if JSC_OBJC_API_ENABLED
234          if (o->inherits<JSCallbackObject<JSAPIWrapperObject>>(vm))
235              return jsCast<JSCallbackObject<JSAPIWrapperObject>*>(o)->inherits(jsClass);
236  #endif
237      }
238      return false;
239  }
240  
241  bool JSValueIsEqual(JSContextRef ctx, JSValueRef a, JSValueRef b, JSValueRef* exception)
242  {
243      if (!ctx) {
244          ASSERT_NOT_REACHED();
245          return false;
246      }
247      JSGlobalObject* globalObject = toJS(ctx);
248      VM& vm = globalObject->vm();
249      JSLockHolder locker(vm);
250      auto scope = DECLARE_CATCH_SCOPE(vm);
251  
252      JSValue jsA = toJS(globalObject, a);
253      JSValue jsB = toJS(globalObject, b);
254  
255      bool result = JSValue::equal(globalObject, jsA, jsB); // false if an exception is thrown
256      if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
257          return false;
258      
259      return result;
260  }
261  
262  bool JSValueIsStrictEqual(JSContextRef ctx, JSValueRef a, JSValueRef b)
263  {
264      if (!ctx) {
265          ASSERT_NOT_REACHED();
266          return false;
267      }
268      JSGlobalObject* globalObject = toJS(ctx);
269      JSLockHolder locker(globalObject);
270  
271      JSValue jsA = toJS(globalObject, a);
272      JSValue jsB = toJS(globalObject, b);
273  
274      return JSValue::strictEqual(globalObject, jsA, jsB);
275  }
276  
277  bool JSValueIsInstanceOfConstructor(JSContextRef ctx, JSValueRef value, JSObjectRef constructor, JSValueRef* exception)
278  {
279      if (!ctx) {
280          ASSERT_NOT_REACHED();
281          return false;
282      }
283      JSGlobalObject* globalObject = toJS(ctx);
284      VM& vm = globalObject->vm();
285      JSLockHolder locker(vm);
286      auto scope = DECLARE_CATCH_SCOPE(vm);
287  
288      JSValue jsValue = toJS(globalObject, value);
289  
290      JSObject* jsConstructor = toJS(constructor);
291      if (!jsConstructor->structure(vm)->typeInfo().implementsHasInstance())
292          return false;
293      bool result = jsConstructor->hasInstance(globalObject, jsValue); // false if an exception is thrown
294      if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
295          return false;
296      return result;
297  }
298  
299  JSValueRef JSValueMakeUndefined(JSContextRef ctx)
300  {
301      if (!ctx) {
302          ASSERT_NOT_REACHED();
303          return nullptr;
304      }
305  #if !CPU(ADDRESS64)
306      JSGlobalObject* globalObject = toJS(ctx);
307      JSLockHolder locker(globalObject);
308      return toRef(globalObject, jsUndefined());
309  #else
310      return toRef(jsUndefined());
311  #endif
312  }
313  
314  JSValueRef JSValueMakeNull(JSContextRef ctx)
315  {
316      if (!ctx) {
317          ASSERT_NOT_REACHED();
318          return nullptr;
319      }
320  #if !CPU(ADDRESS64)
321      JSGlobalObject* globalObject = toJS(ctx);
322      JSLockHolder locker(globalObject);
323      return toRef(globalObject, jsNull());
324  #else
325      return toRef(jsNull());
326  #endif
327  }
328  
329  JSValueRef JSValueMakeBoolean(JSContextRef ctx, bool value)
330  {
331      if (!ctx) {
332          ASSERT_NOT_REACHED();
333          return nullptr;
334      }
335  #if !CPU(ADDRESS64)
336      JSGlobalObject* globalObject = toJS(ctx);
337      JSLockHolder locker(globalObject);
338      return toRef(globalObject, jsBoolean(value));
339  #else
340      return toRef(jsBoolean(value));
341  #endif
342  }
343  
344  JSValueRef JSValueMakeNumber(JSContextRef ctx, double value)
345  {
346      if (!ctx) {
347          ASSERT_NOT_REACHED();
348          return nullptr;
349      }
350  #if !CPU(ADDRESS64)
351      JSGlobalObject* globalObject = toJS(ctx);
352      JSLockHolder locker(globalObject);
353      return toRef(globalObject, jsNumber(purifyNaN(value)));
354  #else
355      return toRef(jsNumber(purifyNaN(value)));
356  #endif
357  }
358  
359  JSValueRef JSValueMakeSymbol(JSContextRef ctx, JSStringRef description)
360  {
361      if (!ctx) {
362          ASSERT_NOT_REACHED();
363          return nullptr;
364      }
365      JSGlobalObject* globalObject = toJS(ctx);
366      VM& vm = globalObject->vm();
367      JSLockHolder locker(globalObject);
368  
369      if (!description)
370          return toRef(globalObject, Symbol::create(vm));
371      return toRef(globalObject, Symbol::createWithDescription(vm, description->string()));
372  }
373  
374  JSValueRef JSValueMakeString(JSContextRef ctx, JSStringRef string)
375  {
376      if (!ctx) {
377          ASSERT_NOT_REACHED();
378          return nullptr;
379      }
380      JSGlobalObject* globalObject = toJS(ctx);
381      VM& vm = globalObject->vm();
382      JSLockHolder locker(vm);
383  
384      return toRef(globalObject, jsString(vm, string ? string->string() : String()));
385  }
386  
387  JSValueRef JSValueMakeFromJSONString(JSContextRef ctx, JSStringRef string)
388  {
389      if (!ctx) {
390          ASSERT_NOT_REACHED();
391          return nullptr;
392      }
393      JSGlobalObject* globalObject = toJS(ctx);
394      JSLockHolder locker(globalObject);
395      String str = string->string();
396      unsigned length = str.length();
397      if (!length || str.is8Bit()) {
398          LiteralParser<LChar> parser(globalObject, str.characters8(), length, StrictJSON);
399          return toRef(globalObject, parser.tryLiteralParse());
400      }
401      LiteralParser<UChar> parser(globalObject, str.characters16(), length, StrictJSON);
402      return toRef(globalObject, parser.tryLiteralParse());
403  }
404  
405  JSStringRef JSValueCreateJSONString(JSContextRef ctx, JSValueRef apiValue, unsigned indent, JSValueRef* exception)
406  {
407      if (!ctx) {
408          ASSERT_NOT_REACHED();
409          return nullptr;
410      }
411      JSGlobalObject* globalObject = toJS(ctx);
412      VM& vm = globalObject->vm();
413      JSLockHolder locker(vm);
414      auto scope = DECLARE_CATCH_SCOPE(vm);
415  
416      JSValue value = toJS(globalObject, apiValue);
417      String result = JSONStringify(globalObject, value, indent);
418      if (exception)
419          *exception = nullptr;
420      if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
421          return nullptr;
422      return OpaqueJSString::tryCreate(result).leakRef();
423  }
424  
425  bool JSValueToBoolean(JSContextRef ctx, JSValueRef value)
426  {
427      if (!ctx) {
428          ASSERT_NOT_REACHED();
429          return false;
430      }
431      JSGlobalObject* globalObject = toJS(ctx);
432      JSLockHolder locker(globalObject);
433  
434      JSValue jsValue = toJS(globalObject, value);
435      return jsValue.toBoolean(globalObject);
436  }
437  
438  double JSValueToNumber(JSContextRef ctx, JSValueRef value, JSValueRef* exception)
439  {
440      if (!ctx) {
441          ASSERT_NOT_REACHED();
442          return PNaN;
443      }
444      JSGlobalObject* globalObject = toJS(ctx);
445      VM& vm = globalObject->vm();
446      JSLockHolder locker(vm);
447      auto scope = DECLARE_CATCH_SCOPE(vm);
448  
449      JSValue jsValue = toJS(globalObject, value);
450  
451      double number = jsValue.toNumber(globalObject);
452      if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
453          number = PNaN;
454      return number;
455  }
456  
457  JSStringRef JSValueToStringCopy(JSContextRef ctx, JSValueRef value, JSValueRef* exception)
458  {
459      if (!ctx) {
460          ASSERT_NOT_REACHED();
461          return nullptr;
462      }
463      JSGlobalObject* globalObject = toJS(ctx);
464      VM& vm = globalObject->vm();
465      JSLockHolder locker(vm);
466      auto scope = DECLARE_CATCH_SCOPE(vm);
467  
468      JSValue jsValue = toJS(globalObject, value);
469      
470      auto stringRef(OpaqueJSString::tryCreate(jsValue.toWTFString(globalObject)));
471      if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
472          stringRef = nullptr;
473      return stringRef.leakRef();
474  }
475  
476  JSObjectRef JSValueToObject(JSContextRef ctx, JSValueRef value, JSValueRef* exception)
477  {
478      if (!ctx) {
479          ASSERT_NOT_REACHED();
480          return nullptr;
481      }
482      JSGlobalObject* globalObject = toJS(ctx);
483      VM& vm = globalObject->vm();
484      JSLockHolder locker(vm);
485      auto scope = DECLARE_CATCH_SCOPE(vm);
486  
487      JSValue jsValue = toJS(globalObject, value);
488      
489      JSObjectRef objectRef = toRef(jsValue.toObject(globalObject));
490      if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
491          objectRef = nullptr;
492      return objectRef;
493  }
494  
495  void JSValueProtect(JSContextRef ctx, JSValueRef value)
496  {
497      if (!ctx) {
498          ASSERT_NOT_REACHED();
499          return;
500      }
501      JSGlobalObject* globalObject = toJS(ctx);
502      JSLockHolder locker(globalObject);
503  
504      JSValue jsValue = toJSForGC(globalObject, value);
505      gcProtect(jsValue);
506  }
507  
508  void JSValueUnprotect(JSContextRef ctx, JSValueRef value)
509  {
510      JSGlobalObject* globalObject = toJS(ctx);
511      JSLockHolder locker(globalObject);
512  
513      JSValue jsValue = toJSForGC(globalObject, value);
514      gcUnprotect(jsValue);
515  }