1 | /* |
2 | * Copyright (C) 2006-2019 Apple Inc. All rights reserved. |
3 | * Copyright (C) 2008 Kelvin W Sherlock ([email protected]) |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * 1. Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions and the following disclaimer. |
10 | * 2. Redistributions in binary form must reproduce the above copyright |
11 | * notice, this list of conditions and the following disclaimer in the |
12 | * documentation and/or other materials provided with the distribution. |
13 | * |
14 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
15 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
17 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
18 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | */ |
26 | |
27 | #include "config.h" |
28 | #include "JSObjectRef.h" |
29 | #include "JSObjectRefPrivate.h" |
30 | |
31 | #include "APICast.h" |
32 | #include "APIUtils.h" |
33 | #include "DateConstructor.h" |
34 | #include "ErrorConstructor.h" |
35 | #include "Exception.h" |
36 | #include "FunctionConstructor.h" |
37 | #include "Identifier.h" |
38 | #include "InitializeThreading.h" |
39 | #include "JSAPIWrapperObject.h" |
40 | #include "JSArray.h" |
41 | #include "JSCInlines.h" |
42 | #include "JSCallbackConstructor.h" |
43 | #include "JSCallbackFunction.h" |
44 | #include "JSCallbackObject.h" |
45 | #include "JSClassRef.h" |
46 | #include "JSFunction.h" |
47 | #include "JSGlobalObject.h" |
48 | #include "JSObject.h" |
49 | #include "JSPromise.h" |
50 | #include "JSPromiseDeferred.h" |
51 | #include "JSRetainPtr.h" |
52 | #include "JSString.h" |
53 | #include "JSValueRef.h" |
54 | #include "ObjectConstructor.h" |
55 | #include "ObjectPrototype.h" |
56 | #include "PropertyNameArray.h" |
57 | #include "ProxyObject.h" |
58 | #include "RegExpConstructor.h" |
59 | |
60 | #if ENABLE(REMOTE_INSPECTOR) |
61 | #include "JSGlobalObjectInspectorController.h" |
62 | #endif |
63 | |
64 | using namespace JSC; |
65 | |
66 | JSClassRef JSClassCreate(const JSClassDefinition* definition) |
67 | { |
68 | initializeThreading(); |
69 | auto jsClass = (definition->attributes & kJSClassAttributeNoAutomaticPrototype) |
70 | ? OpaqueJSClass::createNoAutomaticPrototype(definition) |
71 | : OpaqueJSClass::create(definition); |
72 | |
73 | return &jsClass.leakRef(); |
74 | } |
75 | |
76 | JSClassRef JSClassRetain(JSClassRef jsClass) |
77 | { |
78 | jsClass->ref(); |
79 | return jsClass; |
80 | } |
81 | |
82 | void JSClassRelease(JSClassRef jsClass) |
83 | { |
84 | jsClass->deref(); |
85 | } |
86 | |
87 | JSObjectRef JSObjectMake(JSContextRef ctx, JSClassRef jsClass, void* data) |
88 | { |
89 | if (!ctx) { |
90 | ASSERT_NOT_REACHED(); |
91 | return 0; |
92 | } |
93 | ExecState* exec = toJS(ctx); |
94 | VM& vm = exec->vm(); |
95 | JSLockHolder locker(vm); |
96 | |
97 | if (!jsClass) |
98 | return toRef(constructEmptyObject(exec)); |
99 | |
100 | JSCallbackObject<JSDestructibleObject>* object = JSCallbackObject<JSDestructibleObject>::create(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->callbackObjectStructure(), jsClass, data); |
101 | if (JSObject* prototype = jsClass->prototype(exec)) |
102 | object->setPrototypeDirect(vm, prototype); |
103 | |
104 | return toRef(object); |
105 | } |
106 | |
107 | JSObjectRef JSObjectMakeFunctionWithCallback(JSContextRef ctx, JSStringRef name, JSObjectCallAsFunctionCallback callAsFunction) |
108 | { |
109 | if (!ctx) { |
110 | ASSERT_NOT_REACHED(); |
111 | return 0; |
112 | } |
113 | ExecState* exec = toJS(ctx); |
114 | VM& vm = exec->vm(); |
115 | JSLockHolder locker(vm); |
116 | return toRef(JSCallbackFunction::create(vm, exec->lexicalGlobalObject(), callAsFunction, name ? name->string() : "anonymous"_s )); |
117 | } |
118 | |
119 | JSObjectRef JSObjectMakeConstructor(JSContextRef ctx, JSClassRef jsClass, JSObjectCallAsConstructorCallback callAsConstructor) |
120 | { |
121 | if (!ctx) { |
122 | ASSERT_NOT_REACHED(); |
123 | return 0; |
124 | } |
125 | ExecState* exec = toJS(ctx); |
126 | VM& vm = exec->vm(); |
127 | JSLockHolder locker(vm); |
128 | |
129 | JSValue jsPrototype = jsClass ? jsClass->prototype(exec) : 0; |
130 | if (!jsPrototype) |
131 | jsPrototype = exec->lexicalGlobalObject()->objectPrototype(); |
132 | |
133 | JSCallbackConstructor* constructor = JSCallbackConstructor::create(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->callbackConstructorStructure(), jsClass, callAsConstructor); |
134 | constructor->putDirect(vm, vm.propertyNames->prototype, jsPrototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); |
135 | return toRef(constructor); |
136 | } |
137 | |
138 | JSObjectRef JSObjectMakeFunction(JSContextRef ctx, JSStringRef name, unsigned parameterCount, const JSStringRef parameterNames[], JSStringRef body, JSStringRef sourceURL, int startingLineNumber, JSValueRef* exception) |
139 | { |
140 | if (!ctx) { |
141 | ASSERT_NOT_REACHED(); |
142 | return 0; |
143 | } |
144 | ExecState* exec = toJS(ctx); |
145 | VM& vm = exec->vm(); |
146 | JSLockHolder locker(vm); |
147 | auto scope = DECLARE_CATCH_SCOPE(vm); |
148 | |
149 | startingLineNumber = std::max(1, startingLineNumber); |
150 | Identifier nameID = name ? name->identifier(&vm) : Identifier::fromString(exec, "anonymous" ); |
151 | |
152 | MarkedArgumentBuffer args; |
153 | for (unsigned i = 0; i < parameterCount; i++) |
154 | args.append(jsString(exec, parameterNames[i]->string())); |
155 | args.append(jsString(exec, body->string())); |
156 | if (UNLIKELY(args.hasOverflowed())) { |
157 | auto throwScope = DECLARE_THROW_SCOPE(vm); |
158 | throwOutOfMemoryError(exec, throwScope); |
159 | handleExceptionIfNeeded(scope, exec, exception); |
160 | return 0; |
161 | } |
162 | |
163 | auto sourceURLString = sourceURL ? sourceURL->string() : String(); |
164 | JSObject* result = constructFunction(exec, exec->lexicalGlobalObject(), args, nameID, SourceOrigin { sourceURLString }, sourceURLString, TextPosition(OrdinalNumber::fromOneBasedInt(startingLineNumber), OrdinalNumber())); |
165 | if (handleExceptionIfNeeded(scope, exec, exception) == ExceptionStatus::DidThrow) |
166 | result = 0; |
167 | return toRef(result); |
168 | } |
169 | |
170 | JSObjectRef JSObjectMakeArray(JSContextRef ctx, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) |
171 | { |
172 | if (!ctx) { |
173 | ASSERT_NOT_REACHED(); |
174 | return 0; |
175 | } |
176 | ExecState* exec = toJS(ctx); |
177 | VM& vm = exec->vm(); |
178 | JSLockHolder locker(vm); |
179 | auto scope = DECLARE_CATCH_SCOPE(vm); |
180 | |
181 | JSObject* result; |
182 | if (argumentCount) { |
183 | MarkedArgumentBuffer argList; |
184 | for (size_t i = 0; i < argumentCount; ++i) |
185 | argList.append(toJS(exec, arguments[i])); |
186 | if (UNLIKELY(argList.hasOverflowed())) { |
187 | auto throwScope = DECLARE_THROW_SCOPE(vm); |
188 | throwOutOfMemoryError(exec, throwScope); |
189 | handleExceptionIfNeeded(scope, exec, exception); |
190 | return 0; |
191 | } |
192 | |
193 | result = constructArray(exec, static_cast<ArrayAllocationProfile*>(0), argList); |
194 | } else |
195 | result = constructEmptyArray(exec, 0); |
196 | |
197 | if (handleExceptionIfNeeded(scope, exec, exception) == ExceptionStatus::DidThrow) |
198 | result = 0; |
199 | |
200 | return toRef(result); |
201 | } |
202 | |
203 | JSObjectRef JSObjectMakeDate(JSContextRef ctx, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) |
204 | { |
205 | if (!ctx) { |
206 | ASSERT_NOT_REACHED(); |
207 | return 0; |
208 | } |
209 | ExecState* exec = toJS(ctx); |
210 | VM& vm = exec->vm(); |
211 | JSLockHolder locker(vm); |
212 | auto scope = DECLARE_CATCH_SCOPE(vm); |
213 | |
214 | MarkedArgumentBuffer argList; |
215 | for (size_t i = 0; i < argumentCount; ++i) |
216 | argList.append(toJS(exec, arguments[i])); |
217 | if (UNLIKELY(argList.hasOverflowed())) { |
218 | auto throwScope = DECLARE_THROW_SCOPE(vm); |
219 | throwOutOfMemoryError(exec, throwScope); |
220 | handleExceptionIfNeeded(scope, exec, exception); |
221 | return 0; |
222 | } |
223 | |
224 | JSObject* result = constructDate(exec, exec->lexicalGlobalObject(), JSValue(), argList); |
225 | if (handleExceptionIfNeeded(scope, exec, exception) == ExceptionStatus::DidThrow) |
226 | result = 0; |
227 | |
228 | return toRef(result); |
229 | } |
230 | |
231 | JSObjectRef JSObjectMakeError(JSContextRef ctx, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) |
232 | { |
233 | if (!ctx) { |
234 | ASSERT_NOT_REACHED(); |
235 | return 0; |
236 | } |
237 | ExecState* exec = toJS(ctx); |
238 | VM& vm = exec->vm(); |
239 | JSLockHolder locker(vm); |
240 | auto scope = DECLARE_CATCH_SCOPE(vm); |
241 | |
242 | JSValue message = argumentCount ? toJS(exec, arguments[0]) : jsUndefined(); |
243 | Structure* errorStructure = exec->lexicalGlobalObject()->errorStructure(); |
244 | JSObject* result = ErrorInstance::create(exec, errorStructure, message); |
245 | |
246 | if (handleExceptionIfNeeded(scope, exec, exception) == ExceptionStatus::DidThrow) |
247 | result = 0; |
248 | |
249 | return toRef(result); |
250 | } |
251 | |
252 | JSObjectRef JSObjectMakeRegExp(JSContextRef ctx, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) |
253 | { |
254 | if (!ctx) { |
255 | ASSERT_NOT_REACHED(); |
256 | return 0; |
257 | } |
258 | ExecState* exec = toJS(ctx); |
259 | VM& vm = exec->vm(); |
260 | JSLockHolder locker(vm); |
261 | auto scope = DECLARE_CATCH_SCOPE(vm); |
262 | |
263 | MarkedArgumentBuffer argList; |
264 | for (size_t i = 0; i < argumentCount; ++i) |
265 | argList.append(toJS(exec, arguments[i])); |
266 | if (UNLIKELY(argList.hasOverflowed())) { |
267 | auto throwScope = DECLARE_THROW_SCOPE(vm); |
268 | throwOutOfMemoryError(exec, throwScope); |
269 | handleExceptionIfNeeded(scope, exec, exception); |
270 | return 0; |
271 | } |
272 | |
273 | JSObject* result = constructRegExp(exec, exec->lexicalGlobalObject(), argList); |
274 | if (handleExceptionIfNeeded(scope, exec, exception) == ExceptionStatus::DidThrow) |
275 | result = 0; |
276 | |
277 | return toRef(result); |
278 | } |
279 | |
280 | JSObjectRef JSObjectMakeDeferredPromise(JSContextRef ctx, JSObjectRef* resolve, JSObjectRef* reject, JSValueRef* exception) |
281 | { |
282 | if (!ctx) { |
283 | ASSERT_NOT_REACHED(); |
284 | return nullptr; |
285 | } |
286 | |
287 | ExecState* exec = toJS(ctx); |
288 | VM& vm = exec->vm(); |
289 | JSLockHolder locker(exec); |
290 | auto scope = DECLARE_CATCH_SCOPE(vm); |
291 | |
292 | auto* globalObject = exec->lexicalGlobalObject(); |
293 | JSPromiseDeferred::DeferredData data = JSPromiseDeferred::createDeferredData(exec, globalObject, globalObject->promiseConstructor()); |
294 | if (handleExceptionIfNeeded(scope, exec, exception) == ExceptionStatus::DidThrow) |
295 | return nullptr; |
296 | |
297 | if (resolve) |
298 | *resolve = toRef(data.resolve); |
299 | if (reject) |
300 | *reject = toRef(data.reject); |
301 | return toRef(data.promise); |
302 | } |
303 | |
304 | JSValueRef JSObjectGetPrototype(JSContextRef ctx, JSObjectRef object) |
305 | { |
306 | if (!ctx) { |
307 | ASSERT_NOT_REACHED(); |
308 | return 0; |
309 | } |
310 | ExecState* exec = toJS(ctx); |
311 | JSLockHolder locker(exec); |
312 | |
313 | JSObject* jsObject = toJS(object); |
314 | return toRef(exec, jsObject->getPrototypeDirect(exec->vm())); |
315 | } |
316 | |
317 | void JSObjectSetPrototype(JSContextRef ctx, JSObjectRef object, JSValueRef value) |
318 | { |
319 | if (!ctx) { |
320 | ASSERT_NOT_REACHED(); |
321 | return; |
322 | } |
323 | ExecState* exec = toJS(ctx); |
324 | VM& vm = exec->vm(); |
325 | JSLockHolder locker(vm); |
326 | auto scope = DECLARE_CATCH_SCOPE(vm); |
327 | |
328 | JSObject* jsObject = toJS(object); |
329 | JSValue jsValue = toJS(exec, value); |
330 | jsObject->setPrototype(vm, exec, jsValue.isObject() ? jsValue : jsNull()); |
331 | handleExceptionIfNeeded(scope, exec, nullptr); |
332 | } |
333 | |
334 | bool JSObjectHasProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName) |
335 | { |
336 | if (!ctx) { |
337 | ASSERT_NOT_REACHED(); |
338 | return false; |
339 | } |
340 | ExecState* exec = toJS(ctx); |
341 | VM& vm = exec->vm(); |
342 | JSLockHolder locker(vm); |
343 | |
344 | JSObject* jsObject = toJS(object); |
345 | |
346 | return jsObject->hasProperty(exec, propertyName->identifier(&vm)); |
347 | } |
348 | |
349 | JSValueRef JSObjectGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) |
350 | { |
351 | if (!ctx) { |
352 | ASSERT_NOT_REACHED(); |
353 | return 0; |
354 | } |
355 | ExecState* exec = toJS(ctx); |
356 | VM& vm = exec->vm(); |
357 | JSLockHolder locker(vm); |
358 | auto scope = DECLARE_CATCH_SCOPE(vm); |
359 | |
360 | JSObject* jsObject = toJS(object); |
361 | |
362 | JSValue jsValue = jsObject->get(exec, propertyName->identifier(&vm)); |
363 | handleExceptionIfNeeded(scope, exec, exception); |
364 | return toRef(exec, jsValue); |
365 | } |
366 | |
367 | void JSObjectSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSPropertyAttributes attributes, JSValueRef* exception) |
368 | { |
369 | if (!ctx) { |
370 | ASSERT_NOT_REACHED(); |
371 | return; |
372 | } |
373 | ExecState* exec = toJS(ctx); |
374 | VM& vm = exec->vm(); |
375 | JSLockHolder locker(vm); |
376 | auto scope = DECLARE_CATCH_SCOPE(vm); |
377 | |
378 | JSObject* jsObject = toJS(object); |
379 | Identifier name(propertyName->identifier(&vm)); |
380 | JSValue jsValue = toJS(exec, value); |
381 | |
382 | bool doesNotHaveProperty = attributes && !jsObject->hasProperty(exec, name); |
383 | if (LIKELY(!scope.exception())) { |
384 | if (doesNotHaveProperty) { |
385 | PropertyDescriptor desc(jsValue, attributes); |
386 | jsObject->methodTable(vm)->defineOwnProperty(jsObject, exec, name, desc, false); |
387 | } else { |
388 | PutPropertySlot slot(jsObject); |
389 | jsObject->methodTable(vm)->put(jsObject, exec, name, jsValue, slot); |
390 | } |
391 | } |
392 | handleExceptionIfNeeded(scope, exec, exception); |
393 | } |
394 | |
395 | bool JSObjectHasPropertyForKey(JSContextRef ctx, JSObjectRef object, JSValueRef key, JSValueRef* exception) |
396 | { |
397 | if (!ctx) { |
398 | ASSERT_NOT_REACHED(); |
399 | return false; |
400 | } |
401 | ExecState* exec = toJS(ctx); |
402 | VM& vm = exec->vm(); |
403 | JSLockHolder locker(vm); |
404 | auto scope = DECLARE_CATCH_SCOPE(vm); |
405 | |
406 | JSObject* jsObject = toJS(object); |
407 | Identifier ident = toJS(exec, key).toPropertyKey(exec); |
408 | if (handleExceptionIfNeeded(scope, exec, exception) == ExceptionStatus::DidThrow) |
409 | return false; |
410 | |
411 | bool result = jsObject->hasProperty(exec, ident); |
412 | handleExceptionIfNeeded(scope, exec, exception); |
413 | return result; |
414 | } |
415 | |
416 | JSValueRef JSObjectGetPropertyForKey(JSContextRef ctx, JSObjectRef object, JSValueRef key, JSValueRef* exception) |
417 | { |
418 | if (!ctx) { |
419 | ASSERT_NOT_REACHED(); |
420 | return nullptr; |
421 | } |
422 | ExecState* exec = toJS(ctx); |
423 | VM& vm = exec->vm(); |
424 | JSLockHolder locker(vm); |
425 | auto scope = DECLARE_CATCH_SCOPE(vm); |
426 | |
427 | JSObject* jsObject = toJS(object); |
428 | Identifier ident = toJS(exec, key).toPropertyKey(exec); |
429 | if (handleExceptionIfNeeded(scope, exec, exception) == ExceptionStatus::DidThrow) |
430 | return nullptr; |
431 | |
432 | JSValue jsValue = jsObject->get(exec, ident); |
433 | handleExceptionIfNeeded(scope, exec, exception); |
434 | return toRef(exec, jsValue); |
435 | } |
436 | |
437 | void JSObjectSetPropertyForKey(JSContextRef ctx, JSObjectRef object, JSValueRef key, JSValueRef value, JSPropertyAttributes attributes, JSValueRef* exception) |
438 | { |
439 | if (!ctx) { |
440 | ASSERT_NOT_REACHED(); |
441 | return; |
442 | } |
443 | ExecState* exec = toJS(ctx); |
444 | VM& vm = exec->vm(); |
445 | JSLockHolder locker(vm); |
446 | auto scope = DECLARE_CATCH_SCOPE(vm); |
447 | |
448 | JSObject* jsObject = toJS(object); |
449 | JSValue jsValue = toJS(exec, value); |
450 | |
451 | Identifier ident = toJS(exec, key).toPropertyKey(exec); |
452 | if (handleExceptionIfNeeded(scope, exec, exception) == ExceptionStatus::DidThrow) |
453 | return; |
454 | |
455 | bool doesNotHaveProperty = attributes && !jsObject->hasProperty(exec, ident); |
456 | if (LIKELY(!scope.exception())) { |
457 | if (doesNotHaveProperty) { |
458 | PropertyDescriptor desc(jsValue, attributes); |
459 | jsObject->methodTable(vm)->defineOwnProperty(jsObject, exec, ident, desc, false); |
460 | } else { |
461 | PutPropertySlot slot(jsObject); |
462 | jsObject->methodTable(vm)->put(jsObject, exec, ident, jsValue, slot); |
463 | } |
464 | } |
465 | handleExceptionIfNeeded(scope, exec, exception); |
466 | } |
467 | |
468 | bool JSObjectDeletePropertyForKey(JSContextRef ctx, JSObjectRef object, JSValueRef key, JSValueRef* exception) |
469 | { |
470 | if (!ctx) { |
471 | ASSERT_NOT_REACHED(); |
472 | return false; |
473 | } |
474 | ExecState* exec = toJS(ctx); |
475 | VM& vm = exec->vm(); |
476 | JSLockHolder locker(vm); |
477 | auto scope = DECLARE_CATCH_SCOPE(vm); |
478 | |
479 | JSObject* jsObject = toJS(object); |
480 | Identifier ident = toJS(exec, key).toPropertyKey(exec); |
481 | if (handleExceptionIfNeeded(scope, exec, exception) == ExceptionStatus::DidThrow) |
482 | return false; |
483 | |
484 | bool result = jsObject->methodTable(vm)->deleteProperty(jsObject, exec, ident); |
485 | handleExceptionIfNeeded(scope, exec, exception); |
486 | return result; |
487 | } |
488 | |
489 | JSValueRef JSObjectGetPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsigned propertyIndex, JSValueRef* exception) |
490 | { |
491 | if (!ctx) { |
492 | ASSERT_NOT_REACHED(); |
493 | return 0; |
494 | } |
495 | ExecState* exec = toJS(ctx); |
496 | VM& vm = exec->vm(); |
497 | JSLockHolder locker(vm); |
498 | auto scope = DECLARE_CATCH_SCOPE(vm); |
499 | |
500 | JSObject* jsObject = toJS(object); |
501 | |
502 | JSValue jsValue = jsObject->get(exec, propertyIndex); |
503 | handleExceptionIfNeeded(scope, exec, exception); |
504 | return toRef(exec, jsValue); |
505 | } |
506 | |
507 | |
508 | void JSObjectSetPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsigned propertyIndex, JSValueRef value, JSValueRef* exception) |
509 | { |
510 | if (!ctx) { |
511 | ASSERT_NOT_REACHED(); |
512 | return; |
513 | } |
514 | ExecState* exec = toJS(ctx); |
515 | VM& vm = exec->vm(); |
516 | JSLockHolder locker(vm); |
517 | auto scope = DECLARE_CATCH_SCOPE(vm); |
518 | |
519 | JSObject* jsObject = toJS(object); |
520 | JSValue jsValue = toJS(exec, value); |
521 | |
522 | jsObject->methodTable(vm)->putByIndex(jsObject, exec, propertyIndex, jsValue, false); |
523 | handleExceptionIfNeeded(scope, exec, exception); |
524 | } |
525 | |
526 | bool JSObjectDeleteProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) |
527 | { |
528 | if (!ctx) { |
529 | ASSERT_NOT_REACHED(); |
530 | return false; |
531 | } |
532 | ExecState* exec = toJS(ctx); |
533 | VM& vm = exec->vm(); |
534 | JSLockHolder locker(vm); |
535 | auto scope = DECLARE_CATCH_SCOPE(vm); |
536 | |
537 | JSObject* jsObject = toJS(object); |
538 | |
539 | bool result = jsObject->methodTable(vm)->deleteProperty(jsObject, exec, propertyName->identifier(&vm)); |
540 | handleExceptionIfNeeded(scope, exec, exception); |
541 | return result; |
542 | } |
543 | |
544 | // API objects have private properties, which may get accessed during destruction. This |
545 | // helper lets us get the ClassInfo of an API object from a function that may get called |
546 | // during destruction. |
547 | static const ClassInfo* classInfoPrivate(JSObject* jsObject) |
548 | { |
549 | VM& vm = *jsObject->vm(); |
550 | |
551 | if (vm.currentlyDestructingCallbackObject != jsObject) |
552 | return jsObject->classInfo(vm); |
553 | |
554 | return vm.currentlyDestructingCallbackObjectClassInfo; |
555 | } |
556 | |
557 | void* JSObjectGetPrivate(JSObjectRef object) |
558 | { |
559 | JSObject* jsObject = uncheckedToJS(object); |
560 | VM& vm = *jsObject->vm(); |
561 | |
562 | const ClassInfo* classInfo = classInfoPrivate(jsObject); |
563 | |
564 | // Get wrapped object if proxied |
565 | if (classInfo->isSubClassOf(JSProxy::info())) { |
566 | jsObject = static_cast<JSProxy*>(jsObject)->target(); |
567 | classInfo = jsObject->classInfo(vm); |
568 | } |
569 | |
570 | if (classInfo->isSubClassOf(JSCallbackObject<JSGlobalObject>::info())) |
571 | return static_cast<JSCallbackObject<JSGlobalObject>*>(jsObject)->getPrivate(); |
572 | if (classInfo->isSubClassOf(JSCallbackObject<JSDestructibleObject>::info())) |
573 | return static_cast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->getPrivate(); |
574 | #if JSC_OBJC_API_ENABLED |
575 | if (classInfo->isSubClassOf(JSCallbackObject<JSAPIWrapperObject>::info())) |
576 | return static_cast<JSCallbackObject<JSAPIWrapperObject>*>(jsObject)->getPrivate(); |
577 | #endif |
578 | |
579 | return 0; |
580 | } |
581 | |
582 | bool JSObjectSetPrivate(JSObjectRef object, void* data) |
583 | { |
584 | JSObject* jsObject = uncheckedToJS(object); |
585 | VM& vm = *jsObject->vm(); |
586 | |
587 | const ClassInfo* classInfo = classInfoPrivate(jsObject); |
588 | |
589 | // Get wrapped object if proxied |
590 | if (classInfo->isSubClassOf(JSProxy::info())) { |
591 | jsObject = static_cast<JSProxy*>(jsObject)->target(); |
592 | classInfo = jsObject->classInfo(vm); |
593 | } |
594 | |
595 | if (classInfo->isSubClassOf(JSCallbackObject<JSGlobalObject>::info())) { |
596 | static_cast<JSCallbackObject<JSGlobalObject>*>(jsObject)->setPrivate(data); |
597 | return true; |
598 | } |
599 | if (classInfo->isSubClassOf(JSCallbackObject<JSDestructibleObject>::info())) { |
600 | static_cast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->setPrivate(data); |
601 | return true; |
602 | } |
603 | #if JSC_OBJC_API_ENABLED |
604 | if (classInfo->isSubClassOf(JSCallbackObject<JSAPIWrapperObject>::info())) { |
605 | static_cast<JSCallbackObject<JSAPIWrapperObject>*>(jsObject)->setPrivate(data); |
606 | return true; |
607 | } |
608 | #endif |
609 | |
610 | return false; |
611 | } |
612 | |
613 | JSValueRef JSObjectGetPrivateProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName) |
614 | { |
615 | ExecState* exec = toJS(ctx); |
616 | VM& vm = exec->vm(); |
617 | JSLockHolder locker(vm); |
618 | JSObject* jsObject = toJS(object); |
619 | JSValue result; |
620 | Identifier name(propertyName->identifier(&vm)); |
621 | |
622 | |
623 | // Get wrapped object if proxied |
624 | if (jsObject->inherits<JSProxy>(vm)) |
625 | jsObject = jsCast<JSProxy*>(jsObject)->target(); |
626 | |
627 | if (jsObject->inherits<JSCallbackObject<JSGlobalObject>>(vm)) |
628 | result = jsCast<JSCallbackObject<JSGlobalObject>*>(jsObject)->getPrivateProperty(name); |
629 | else if (jsObject->inherits<JSCallbackObject<JSDestructibleObject>>(vm)) |
630 | result = jsCast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->getPrivateProperty(name); |
631 | #if JSC_OBJC_API_ENABLED |
632 | else if (jsObject->inherits<JSCallbackObject<JSAPIWrapperObject>>(vm)) |
633 | result = jsCast<JSCallbackObject<JSAPIWrapperObject>*>(jsObject)->getPrivateProperty(name); |
634 | #endif |
635 | return toRef(exec, result); |
636 | } |
637 | |
638 | bool JSObjectSetPrivateProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value) |
639 | { |
640 | ExecState* exec = toJS(ctx); |
641 | VM& vm = exec->vm(); |
642 | JSLockHolder locker(vm); |
643 | JSObject* jsObject = toJS(object); |
644 | JSValue jsValue = value ? toJS(exec, value) : JSValue(); |
645 | Identifier name(propertyName->identifier(&vm)); |
646 | |
647 | // Get wrapped object if proxied |
648 | if (jsObject->inherits<JSProxy>(vm)) |
649 | jsObject = jsCast<JSProxy*>(jsObject)->target(); |
650 | |
651 | if (jsObject->inherits<JSCallbackObject<JSGlobalObject>>(vm)) { |
652 | jsCast<JSCallbackObject<JSGlobalObject>*>(jsObject)->setPrivateProperty(vm, name, jsValue); |
653 | return true; |
654 | } |
655 | if (jsObject->inherits<JSCallbackObject<JSDestructibleObject>>(vm)) { |
656 | jsCast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->setPrivateProperty(vm, name, jsValue); |
657 | return true; |
658 | } |
659 | #if JSC_OBJC_API_ENABLED |
660 | if (jsObject->inherits<JSCallbackObject<JSAPIWrapperObject>>(vm)) { |
661 | jsCast<JSCallbackObject<JSAPIWrapperObject>*>(jsObject)->setPrivateProperty(vm, name, jsValue); |
662 | return true; |
663 | } |
664 | #endif |
665 | return false; |
666 | } |
667 | |
668 | bool JSObjectDeletePrivateProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName) |
669 | { |
670 | ExecState* exec = toJS(ctx); |
671 | VM& vm = exec->vm(); |
672 | JSLockHolder locker(vm); |
673 | JSObject* jsObject = toJS(object); |
674 | Identifier name(propertyName->identifier(&vm)); |
675 | |
676 | // Get wrapped object if proxied |
677 | if (jsObject->inherits<JSProxy>(vm)) |
678 | jsObject = jsCast<JSProxy*>(jsObject)->target(); |
679 | |
680 | if (jsObject->inherits<JSCallbackObject<JSGlobalObject>>(vm)) { |
681 | jsCast<JSCallbackObject<JSGlobalObject>*>(jsObject)->deletePrivateProperty(name); |
682 | return true; |
683 | } |
684 | if (jsObject->inherits<JSCallbackObject<JSDestructibleObject>>(vm)) { |
685 | jsCast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->deletePrivateProperty(name); |
686 | return true; |
687 | } |
688 | #if JSC_OBJC_API_ENABLED |
689 | if (jsObject->inherits<JSCallbackObject<JSAPIWrapperObject>>(vm)) { |
690 | jsCast<JSCallbackObject<JSAPIWrapperObject>*>(jsObject)->deletePrivateProperty(name); |
691 | return true; |
692 | } |
693 | #endif |
694 | return false; |
695 | } |
696 | |
697 | bool JSObjectIsFunction(JSContextRef ctx, JSObjectRef object) |
698 | { |
699 | if (!object) |
700 | return false; |
701 | ExecState* exec = toJS(ctx); |
702 | VM& vm = exec->vm(); |
703 | JSLockHolder locker(vm); |
704 | CallData callData; |
705 | JSCell* cell = toJS(object); |
706 | return cell->methodTable(vm)->getCallData(cell, callData) != CallType::None; |
707 | } |
708 | |
709 | JSValueRef JSObjectCallAsFunction(JSContextRef ctx, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) |
710 | { |
711 | ExecState* exec = toJS(ctx); |
712 | VM& vm = exec->vm(); |
713 | JSLockHolder locker(vm); |
714 | auto scope = DECLARE_CATCH_SCOPE(vm); |
715 | |
716 | if (!object) |
717 | return 0; |
718 | |
719 | JSObject* jsObject = toJS(object); |
720 | JSObject* jsThisObject = toJS(thisObject); |
721 | |
722 | if (!jsThisObject) |
723 | jsThisObject = exec->globalThisValue(); |
724 | |
725 | MarkedArgumentBuffer argList; |
726 | for (size_t i = 0; i < argumentCount; i++) |
727 | argList.append(toJS(exec, arguments[i])); |
728 | if (UNLIKELY(argList.hasOverflowed())) { |
729 | auto throwScope = DECLARE_THROW_SCOPE(vm); |
730 | throwOutOfMemoryError(exec, throwScope); |
731 | handleExceptionIfNeeded(scope, exec, exception); |
732 | return 0; |
733 | } |
734 | |
735 | CallData callData; |
736 | CallType callType = jsObject->methodTable(vm)->getCallData(jsObject, callData); |
737 | if (callType == CallType::None) |
738 | return 0; |
739 | |
740 | JSValueRef result = toRef(exec, profiledCall(exec, ProfilingReason::API, jsObject, callType, callData, jsThisObject, argList)); |
741 | if (handleExceptionIfNeeded(scope, exec, exception) == ExceptionStatus::DidThrow) |
742 | result = 0; |
743 | return result; |
744 | } |
745 | |
746 | bool JSObjectIsConstructor(JSContextRef ctx, JSObjectRef object) |
747 | { |
748 | ExecState* exec = toJS(ctx); |
749 | VM& vm = exec->vm(); |
750 | JSLockHolder locker(vm); |
751 | if (!object) |
752 | return false; |
753 | return toJS(object)->isConstructor(vm); |
754 | } |
755 | |
756 | JSObjectRef JSObjectCallAsConstructor(JSContextRef ctx, JSObjectRef object, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) |
757 | { |
758 | ExecState* exec = toJS(ctx); |
759 | VM& vm = exec->vm(); |
760 | JSLockHolder locker(vm); |
761 | auto scope = DECLARE_CATCH_SCOPE(vm); |
762 | |
763 | if (!object) |
764 | return 0; |
765 | |
766 | JSObject* jsObject = toJS(object); |
767 | |
768 | ConstructData constructData; |
769 | ConstructType constructType = jsObject->methodTable(vm)->getConstructData(jsObject, constructData); |
770 | if (constructType == ConstructType::None) |
771 | return 0; |
772 | |
773 | MarkedArgumentBuffer argList; |
774 | for (size_t i = 0; i < argumentCount; i++) |
775 | argList.append(toJS(exec, arguments[i])); |
776 | if (UNLIKELY(argList.hasOverflowed())) { |
777 | auto throwScope = DECLARE_THROW_SCOPE(vm); |
778 | throwOutOfMemoryError(exec, throwScope); |
779 | handleExceptionIfNeeded(scope, exec, exception); |
780 | return 0; |
781 | } |
782 | |
783 | JSObjectRef result = toRef(profiledConstruct(exec, ProfilingReason::API, jsObject, constructType, constructData, argList)); |
784 | if (handleExceptionIfNeeded(scope, exec, exception) == ExceptionStatus::DidThrow) |
785 | result = 0; |
786 | return result; |
787 | } |
788 | |
789 | struct OpaqueJSPropertyNameArray { |
790 | WTF_MAKE_FAST_ALLOCATED; |
791 | public: |
792 | // FIXME: Why not inherit from RefCounted? |
793 | OpaqueJSPropertyNameArray(VM* vm) |
794 | : refCount(0) |
795 | , vm(vm) |
796 | { |
797 | } |
798 | |
799 | unsigned refCount; |
800 | VM* vm; |
801 | Vector<Ref<OpaqueJSString>> array; |
802 | }; |
803 | |
804 | JSPropertyNameArrayRef JSObjectCopyPropertyNames(JSContextRef ctx, JSObjectRef object) |
805 | { |
806 | if (!ctx) { |
807 | ASSERT_NOT_REACHED(); |
808 | return 0; |
809 | } |
810 | ExecState* exec = toJS(ctx); |
811 | JSLockHolder locker(exec); |
812 | |
813 | VM* vm = &exec->vm(); |
814 | |
815 | JSObject* jsObject = toJS(object); |
816 | JSPropertyNameArrayRef propertyNames = new OpaqueJSPropertyNameArray(vm); |
817 | PropertyNameArray array(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude); |
818 | jsObject->methodTable(*vm)->getPropertyNames(jsObject, exec, array, EnumerationMode()); |
819 | |
820 | size_t size = array.size(); |
821 | propertyNames->array.reserveInitialCapacity(size); |
822 | for (size_t i = 0; i < size; ++i) |
823 | propertyNames->array.uncheckedAppend(OpaqueJSString::tryCreate(array[i].string()).releaseNonNull()); |
824 | |
825 | return JSPropertyNameArrayRetain(propertyNames); |
826 | } |
827 | |
828 | JSPropertyNameArrayRef JSPropertyNameArrayRetain(JSPropertyNameArrayRef array) |
829 | { |
830 | ++array->refCount; |
831 | return array; |
832 | } |
833 | |
834 | void JSPropertyNameArrayRelease(JSPropertyNameArrayRef array) |
835 | { |
836 | if (--array->refCount == 0) { |
837 | JSLockHolder locker(array->vm); |
838 | delete array; |
839 | } |
840 | } |
841 | |
842 | size_t JSPropertyNameArrayGetCount(JSPropertyNameArrayRef array) |
843 | { |
844 | return array->array.size(); |
845 | } |
846 | |
847 | JSStringRef JSPropertyNameArrayGetNameAtIndex(JSPropertyNameArrayRef array, size_t index) |
848 | { |
849 | return array->array[static_cast<unsigned>(index)].ptr(); |
850 | } |
851 | |
852 | void JSPropertyNameAccumulatorAddName(JSPropertyNameAccumulatorRef array, JSStringRef propertyName) |
853 | { |
854 | PropertyNameArray* propertyNames = toJS(array); |
855 | VM* vm = propertyNames->vm(); |
856 | JSLockHolder locker(vm); |
857 | propertyNames->add(propertyName->identifier(vm)); |
858 | } |
859 | |
860 | JSObjectRef JSObjectGetProxyTarget(JSObjectRef objectRef) |
861 | { |
862 | JSObject* object = toJS(objectRef); |
863 | if (!object) |
864 | return nullptr; |
865 | VM& vm = *object->vm(); |
866 | JSLockHolder locker(vm); |
867 | JSObject* result = nullptr; |
868 | if (JSProxy* proxy = jsDynamicCast<JSProxy*>(vm, object)) |
869 | result = proxy->target(); |
870 | else if (ProxyObject* proxy = jsDynamicCast<ProxyObject*>(vm, object)) |
871 | result = proxy->target(); |
872 | return toRef(result); |
873 | } |
874 | |
875 | JSGlobalContextRef JSObjectGetGlobalContext(JSObjectRef objectRef) |
876 | { |
877 | JSObject* object = toJS(objectRef); |
878 | if (!object) |
879 | return nullptr; |
880 | return reinterpret_cast<JSGlobalContextRef>(object->globalObject()->globalExec()); |
881 | } |
882 | |
883 | |