Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "vm/SelfHosting.h"
9 #include "jscntxt.h"
10 #include "jscompartment.h"
11 #include "jsfriendapi.h"
12 #include "jshashutil.h"
13 #include "jsobj.h"
14 #include "jswrapper.h"
15 #include "selfhosted.out.h"
17 #include "builtin/Intl.h"
18 #include "builtin/SelfHostingDefines.h"
19 #include "builtin/TypedObject.h"
20 #include "gc/Marking.h"
21 #include "vm/Compression.h"
22 #include "vm/ForkJoin.h"
23 #include "vm/Interpreter.h"
24 #include "vm/String.h"
26 #include "jsfuninlines.h"
27 #include "jsscriptinlines.h"
29 #include "vm/BooleanObject-inl.h"
30 #include "vm/NumberObject-inl.h"
31 #include "vm/StringObject-inl.h"
33 using namespace js;
34 using namespace js::selfhosted;
36 static void
37 selfHosting_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
38 {
39 PrintError(cx, stderr, message, report, true);
40 }
42 static const JSClass self_hosting_global_class = {
43 "self-hosting-global", JSCLASS_GLOBAL_FLAGS,
44 JS_PropertyStub, JS_DeletePropertyStub,
45 JS_PropertyStub, JS_StrictPropertyStub,
46 JS_EnumerateStub, JS_ResolveStub,
47 JS_ConvertStub, nullptr,
48 nullptr, nullptr, nullptr,
49 JS_GlobalObjectTraceHook
50 };
52 bool
53 js::intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp)
54 {
55 CallArgs args = CallArgsFromVp(argc, vp);
56 RootedValue val(cx, args[0]);
57 RootedObject obj(cx, ToObject(cx, val));
58 if (!obj)
59 return false;
60 args.rval().setObject(*obj);
61 return true;
62 }
64 static bool
65 intrinsic_ToInteger(JSContext *cx, unsigned argc, Value *vp)
66 {
67 CallArgs args = CallArgsFromVp(argc, vp);
68 double result;
69 if (!ToInteger(cx, args[0], &result))
70 return false;
71 args.rval().setDouble(result);
72 return true;
73 }
75 bool
76 js::intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp)
77 {
78 CallArgs args = CallArgsFromVp(argc, vp);
79 Value val = args[0];
80 bool isCallable = val.isObject() && val.toObject().isCallable();
81 args.rval().setBoolean(isCallable);
82 return true;
83 }
85 bool
86 js::intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp)
87 {
88 CallArgs args = CallArgsFromVp(argc, vp);
89 JS_ASSERT(args.length() >= 1);
90 uint32_t errorNumber = args[0].toInt32();
92 #ifdef DEBUG
93 const JSErrorFormatString *efs =
94 js_GetLocalizedErrorMessage(cx, nullptr, nullptr, errorNumber);
95 JS_ASSERT(efs->argCount == args.length() - 1);
96 #endif
98 JSAutoByteString errorArgs[3];
99 for (unsigned i = 1; i < 4 && i < args.length(); i++) {
100 RootedValue val(cx, args[i]);
101 if (val.isInt32()) {
102 JSString *str = ToString<CanGC>(cx, val);
103 if (!str)
104 return false;
105 errorArgs[i - 1].encodeLatin1(cx, str);
106 } else if (val.isString()) {
107 errorArgs[i - 1].encodeLatin1(cx, val.toString());
108 } else {
109 errorArgs[i - 1].initBytes(DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr()));
110 }
111 if (!errorArgs[i - 1])
112 return false;
113 }
115 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, errorNumber,
116 errorArgs[0].ptr(), errorArgs[1].ptr(), errorArgs[2].ptr());
117 return false;
118 }
120 /**
121 * Handles an assertion failure in self-hosted code just like an assertion
122 * failure in C++ code. Information about the failure can be provided in args[0].
123 */
124 static bool
125 intrinsic_AssertionFailed(JSContext *cx, unsigned argc, Value *vp)
126 {
127 #ifdef DEBUG
128 CallArgs args = CallArgsFromVp(argc, vp);
129 if (args.length() > 0) {
130 // try to dump the informative string
131 JSString *str = ToString<CanGC>(cx, args[0]);
132 if (str) {
133 const jschar *chars = str->getChars(cx);
134 if (chars) {
135 fprintf(stderr, "Self-hosted JavaScript assertion info: ");
136 JSString::dumpChars(chars, str->length());
137 fputc('\n', stderr);
138 }
139 }
140 }
141 #endif
142 JS_ASSERT(false);
143 return false;
144 }
146 static bool
147 intrinsic_MakeConstructible(JSContext *cx, unsigned argc, Value *vp)
148 {
149 CallArgs args = CallArgsFromVp(argc, vp);
150 JS_ASSERT(args.length() == 2);
151 JS_ASSERT(args[0].isObject());
152 JS_ASSERT(args[0].toObject().is<JSFunction>());
153 JS_ASSERT(args[1].isObject());
155 // Normal .prototype properties aren't enumerable. But for this to clone
156 // correctly, it must be enumerable.
157 RootedObject ctor(cx, &args[0].toObject());
158 if (!JSObject::defineProperty(cx, ctor, cx->names().prototype, args[1],
159 JS_PropertyStub, JS_StrictPropertyStub,
160 JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT))
161 {
162 return false;
163 }
165 ctor->as<JSFunction>().setIsSelfHostedConstructor();
166 args.rval().setUndefined();
167 return true;
168 }
170 /*
171 * Used to decompile values in the nearest non-builtin stack frame, falling
172 * back to decompiling in the current frame. Helpful for printing higher-order
173 * function arguments.
174 *
175 * The user must supply the argument number of the value in question; it
176 * _cannot_ be automatically determined.
177 */
178 static bool
179 intrinsic_DecompileArg(JSContext *cx, unsigned argc, Value *vp)
180 {
181 CallArgs args = CallArgsFromVp(argc, vp);
182 JS_ASSERT(args.length() == 2);
184 RootedValue value(cx, args[1]);
185 ScopedJSFreePtr<char> str(DecompileArgument(cx, args[0].toInt32(), value));
186 if (!str)
187 return false;
188 RootedAtom atom(cx, Atomize(cx, str, strlen(str)));
189 if (!atom)
190 return false;
191 args.rval().setString(atom);
192 return true;
193 }
195 /*
196 * SetScriptHints(fun, flags): Sets various internal hints to the ion
197 * compiler for use when compiling |fun| or calls to |fun|. Flags
198 * should be a dictionary object.
199 *
200 * The function |fun| should be a self-hosted function (in particular,
201 * it *must* be a JS function).
202 *
203 * Possible flags:
204 * - |cloneAtCallsite: true| will hint that |fun| should be cloned
205 * each callsite to improve TI resolution. This is important for
206 * higher-order functions like |Array.map|.
207 * - |inline: true| will hint that |fun| be inlined regardless of
208 * JIT heuristics.
209 */
210 static bool
211 intrinsic_SetScriptHints(JSContext *cx, unsigned argc, Value *vp)
212 {
213 CallArgs args = CallArgsFromVp(argc, vp);
214 JS_ASSERT(args.length() >= 2);
215 JS_ASSERT(args[0].isObject() && args[0].toObject().is<JSFunction>());
216 JS_ASSERT(args[1].isObject());
218 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
219 RootedScript funScript(cx, fun->getOrCreateScript(cx));
220 if (!funScript)
221 return false;
222 RootedObject flags(cx, &args[1].toObject());
224 RootedId id(cx);
225 RootedValue propv(cx);
227 id = AtomToId(Atomize(cx, "cloneAtCallsite", strlen("cloneAtCallsite")));
228 if (!JSObject::getGeneric(cx, flags, flags, id, &propv))
229 return false;
230 if (ToBoolean(propv))
231 funScript->setShouldCloneAtCallsite();
233 id = AtomToId(Atomize(cx, "inline", strlen("inline")));
234 if (!JSObject::getGeneric(cx, flags, flags, id, &propv))
235 return false;
236 if (ToBoolean(propv))
237 funScript->setShouldInline();
239 args.rval().setUndefined();
240 return true;
241 }
243 #ifdef DEBUG
244 /*
245 * Dump(val): Dumps a value for debugging, even in parallel mode.
246 */
247 bool
248 intrinsic_Dump(ThreadSafeContext *cx, unsigned argc, Value *vp)
249 {
250 CallArgs args = CallArgsFromVp(argc, vp);
251 js_DumpValue(args[0]);
252 if (args[0].isObject()) {
253 fprintf(stderr, "\n");
254 js_DumpObject(&args[0].toObject());
255 }
256 args.rval().setUndefined();
257 return true;
258 }
260 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(intrinsic_Dump_jitInfo, intrinsic_Dump_jitInfo,
261 intrinsic_Dump);
263 bool
264 intrinsic_ParallelSpew(ThreadSafeContext *cx, unsigned argc, Value *vp)
265 {
266 CallArgs args = CallArgsFromVp(argc, vp);
267 JS_ASSERT(args.length() == 1);
268 JS_ASSERT(args[0].isString());
270 ScopedThreadSafeStringInspector inspector(args[0].toString());
271 if (!inspector.ensureChars(cx))
272 return false;
274 ScopedJSFreePtr<char> bytes(TwoByteCharsToNewUTF8CharsZ(cx, inspector.range()).c_str());
275 parallel::Spew(parallel::SpewOps, bytes);
277 args.rval().setUndefined();
278 return true;
279 }
281 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(intrinsic_ParallelSpew_jitInfo, intrinsic_ParallelSpew_jitInfo,
282 intrinsic_ParallelSpew);
283 #endif
285 /*
286 * ForkJoin(func, feedback): Invokes |func| many times in parallel.
287 *
288 * See ForkJoin.cpp for details and ParallelArray.js for examples.
289 */
290 static bool
291 intrinsic_ForkJoin(JSContext *cx, unsigned argc, Value *vp)
292 {
293 CallArgs args = CallArgsFromVp(argc, vp);
294 return ForkJoin(cx, args);
295 }
297 /*
298 * ForkJoinWorkerNumWorkers(): Returns the number of workers in the fork join
299 * thread pool, including the main thread.
300 */
301 static bool
302 intrinsic_ForkJoinNumWorkers(JSContext *cx, unsigned argc, Value *vp)
303 {
304 CallArgs args = CallArgsFromVp(argc, vp);
305 args.rval().setInt32(cx->runtime()->threadPool.numWorkers());
306 return true;
307 }
309 /*
310 * ForkJoinGetSlice(id): Returns the id of the next slice to be worked
311 * on.
312 *
313 * Acts as the identity function when called from outside of a ForkJoin
314 * thread. This odd API is because intrinsics must be called during the
315 * parallel warm up phase to populate observed type sets, so we must call it
316 * even during sequential execution. But since there is no thread pool during
317 * sequential execution, the selfhosted code is responsible for computing the
318 * next sequential slice id and passing it in itself.
319 */
320 bool
321 js::intrinsic_ForkJoinGetSlice(JSContext *cx, unsigned argc, Value *vp)
322 {
323 CallArgs args = CallArgsFromVp(argc, vp);
324 MOZ_ASSERT(args.length() == 1);
325 MOZ_ASSERT(args[0].isInt32());
326 args.rval().set(args[0]);
327 return true;
328 }
330 static bool
331 intrinsic_ForkJoinGetSlicePar(ForkJoinContext *cx, unsigned argc, Value *vp)
332 {
333 CallArgs args = CallArgsFromVp(argc, vp);
334 MOZ_ASSERT(args.length() == 1);
335 MOZ_ASSERT(args[0].isInt32());
337 uint16_t sliceId;
338 if (cx->getSlice(&sliceId))
339 args.rval().setInt32(sliceId);
340 else
341 args.rval().setInt32(ThreadPool::MAX_SLICE_ID);
343 return true;
344 }
346 JS_JITINFO_NATIVE_PARALLEL(intrinsic_ForkJoinGetSlice_jitInfo,
347 intrinsic_ForkJoinGetSlicePar);
349 /*
350 * NewDenseArray(length): Allocates and returns a new dense array with
351 * the given length where all values are initialized to holes.
352 */
353 bool
354 js::intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp)
355 {
356 CallArgs args = CallArgsFromVp(argc, vp);
358 // Check that index is an int32
359 if (!args[0].isInt32()) {
360 JS_ReportError(cx, "Expected int32 as second argument");
361 return false;
362 }
363 uint32_t length = args[0].toInt32();
365 // Make a new buffer and initialize it up to length.
366 RootedObject buffer(cx, NewDenseAllocatedArray(cx, length));
367 if (!buffer)
368 return false;
370 types::TypeObject *newtype = types::GetTypeCallerInitObject(cx, JSProto_Array);
371 if (!newtype)
372 return false;
373 buffer->setType(newtype);
375 JSObject::EnsureDenseResult edr = buffer->ensureDenseElements(cx, length, 0);
376 switch (edr) {
377 case JSObject::ED_OK:
378 args.rval().setObject(*buffer);
379 return true;
381 case JSObject::ED_SPARSE: // shouldn't happen!
382 JS_ASSERT(!"%EnsureDenseArrayElements() would yield sparse array");
383 JS_ReportError(cx, "%EnsureDenseArrayElements() would yield sparse array");
384 break;
386 case JSObject::ED_FAILED:
387 break;
388 }
389 return false;
390 }
392 /*
393 * UnsafePutElements(arr0, idx0, elem0, ..., arrN, idxN, elemN): For each set of
394 * (arr, idx, elem) arguments that are passed, performs the assignment
395 * |arr[idx] = elem|. |arr| must be either a dense array or a typed array.
396 *
397 * If |arr| is a dense array, the index must be an int32 less than the
398 * initialized length of |arr|. Use |%EnsureDenseResultArrayElements|
399 * to ensure that the initialized length is long enough.
400 *
401 * If |arr| is a typed array, the index must be an int32 less than the
402 * length of |arr|.
403 */
404 bool
405 js::intrinsic_UnsafePutElements(JSContext *cx, unsigned argc, Value *vp)
406 {
407 CallArgs args = CallArgsFromVp(argc, vp);
409 if ((args.length() % 3) != 0) {
410 JS_ReportError(cx, "Incorrect number of arguments, not divisible by 3");
411 return false;
412 }
414 for (uint32_t base = 0; base < args.length(); base += 3) {
415 uint32_t arri = base;
416 uint32_t idxi = base+1;
417 uint32_t elemi = base+2;
419 JS_ASSERT(args[arri].isObject());
420 JS_ASSERT(args[arri].toObject().isNative() || IsTypedObjectArray(args[arri].toObject()));
421 JS_ASSERT(args[idxi].isInt32());
423 RootedObject arrobj(cx, &args[arri].toObject());
424 uint32_t idx = args[idxi].toInt32();
426 if (arrobj->is<TypedArrayObject>() || arrobj->is<TypedObject>()) {
427 JS_ASSERT(!arrobj->is<TypedArrayObject>() || idx < arrobj->as<TypedArrayObject>().length());
428 JS_ASSERT(!arrobj->is<TypedObject>() || idx < uint32_t(arrobj->as<TypedObject>().length()));
429 RootedValue tmp(cx, args[elemi]);
430 // XXX: Always non-strict.
431 if (!JSObject::setElement(cx, arrobj, arrobj, idx, &tmp, false))
432 return false;
433 } else {
434 JS_ASSERT(idx < arrobj->getDenseInitializedLength());
435 arrobj->setDenseElementWithType(cx, idx, args[elemi]);
436 }
437 }
439 args.rval().setUndefined();
440 return true;
441 }
443 bool
444 js::intrinsic_DefineValueProperty(JSContext *cx, unsigned argc, Value *vp)
445 {
446 CallArgs args = CallArgsFromVp(argc, vp);
448 MOZ_ASSERT(args.length() == 4);
449 MOZ_ASSERT(args[0].isObject());
450 MOZ_ASSERT(args[3].isInt32());
452 RootedObject obj(cx, &args[0].toObject());
453 if (obj->is<ProxyObject>()) {
454 JS_ReportError(cx, "_DefineValueProperty can't be used on proxies");
455 return false;
456 }
457 RootedId id(cx);
458 if (!ValueToId<CanGC>(cx, args[1], &id))
459 return false;
460 RootedValue value(cx, args[2]);
461 unsigned attributes = args[3].toInt32();
463 unsigned resolvedAttributes = JSPROP_PERMANENT | JSPROP_READONLY;
465 MOZ_ASSERT(bool(attributes & ATTR_ENUMERABLE) != bool(attributes & ATTR_NONENUMERABLE),
466 "_DefineValueProperty must receive either ATTR_ENUMERABLE xor ATTR_NONENUMERABLE");
467 if (attributes & ATTR_ENUMERABLE)
468 resolvedAttributes |= JSPROP_ENUMERATE;
470 MOZ_ASSERT(bool(attributes & ATTR_CONFIGURABLE) != bool(attributes & ATTR_NONCONFIGURABLE),
471 "_DefineValueProperty must receive either ATTR_CONFIGURABLE xor "
472 "ATTR_NONCONFIGURABLE");
473 if (attributes & ATTR_CONFIGURABLE)
474 resolvedAttributes &= ~JSPROP_PERMANENT;
476 MOZ_ASSERT(bool(attributes & ATTR_WRITABLE) != bool(attributes & ATTR_NONWRITABLE),
477 "_DefineValueProperty must receive either ATTR_WRITABLE xor ATTR_NONWRITABLE");
478 if (attributes & ATTR_WRITABLE)
479 resolvedAttributes &= ~JSPROP_READONLY;
481 return JSObject::defineGeneric(cx, obj, id, value, JS_PropertyStub, JS_StrictPropertyStub,
482 resolvedAttributes);
483 }
485 bool
486 js::intrinsic_UnsafeSetReservedSlot(JSContext *cx, unsigned argc, Value *vp)
487 {
488 CallArgs args = CallArgsFromVp(argc, vp);
489 JS_ASSERT(args.length() == 3);
490 JS_ASSERT(args[0].isObject());
491 JS_ASSERT(args[1].isInt32());
493 args[0].toObject().setReservedSlot(args[1].toPrivateUint32(), args[2]);
494 args.rval().setUndefined();
495 return true;
496 }
498 bool
499 js::intrinsic_UnsafeGetReservedSlot(JSContext *cx, unsigned argc, Value *vp)
500 {
501 CallArgs args = CallArgsFromVp(argc, vp);
502 JS_ASSERT(args.length() == 2);
503 JS_ASSERT(args[0].isObject());
504 JS_ASSERT(args[1].isInt32());
506 args.rval().set(args[0].toObject().getReservedSlot(args[1].toPrivateUint32()));
507 return true;
508 }
510 bool
511 js::intrinsic_HaveSameClass(JSContext *cx, unsigned argc, Value *vp)
512 {
513 CallArgs args = CallArgsFromVp(argc, vp);
514 JS_ASSERT(args.length() == 2);
515 JS_ASSERT(args[0].isObject());
516 JS_ASSERT(args[1].isObject());
518 args.rval().setBoolean(args[0].toObject().getClass() == args[1].toObject().getClass());
519 return true;
520 }
522 bool
523 js::intrinsic_IsPackedArray(JSContext *cx, unsigned argc, Value *vp)
524 {
525 CallArgs args = CallArgsFromVp(argc, vp);
526 JS_ASSERT(args.length() == 1);
527 JS_ASSERT(args[0].isObject());
529 JSObject *obj = &args[0].toObject();
530 bool isPacked = obj->is<ArrayObject>() && !obj->hasLazyType() &&
531 !obj->type()->hasAllFlags(types::OBJECT_FLAG_NON_PACKED) &&
532 obj->getDenseInitializedLength() == obj->as<ArrayObject>().length();
534 args.rval().setBoolean(isPacked);
535 return true;
536 }
538 static bool
539 intrinsic_GetIteratorPrototype(JSContext *cx, unsigned argc, Value *vp)
540 {
541 CallArgs args = CallArgsFromVp(argc, vp);
542 JS_ASSERT(args.length() == 0);
544 JSObject *obj = GlobalObject::getOrCreateIteratorPrototype(cx, cx->global());
545 if (!obj)
546 return false;
548 args.rval().setObject(*obj);
549 return true;
550 }
552 static bool
553 intrinsic_NewArrayIterator(JSContext *cx, unsigned argc, Value *vp)
554 {
555 CallArgs args = CallArgsFromVp(argc, vp);
556 JS_ASSERT(args.length() == 0);
558 RootedObject proto(cx, GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
559 if (!proto)
560 return false;
562 JSObject *obj = NewObjectWithGivenProto(cx, proto->getClass(), proto, cx->global());
563 if (!obj)
564 return false;
566 args.rval().setObject(*obj);
567 return true;
568 }
570 static bool
571 intrinsic_IsArrayIterator(JSContext *cx, unsigned argc, Value *vp)
572 {
573 CallArgs args = CallArgsFromVp(argc, vp);
574 JS_ASSERT(args.length() == 1);
575 JS_ASSERT(args[0].isObject());
577 args.rval().setBoolean(args[0].toObject().is<ArrayIteratorObject>());
578 return true;
579 }
581 static bool
582 intrinsic_NewStringIterator(JSContext *cx, unsigned argc, Value *vp)
583 {
584 CallArgs args = CallArgsFromVp(argc, vp);
585 JS_ASSERT(args.length() == 0);
587 RootedObject proto(cx, GlobalObject::getOrCreateStringIteratorPrototype(cx, cx->global()));
588 if (!proto)
589 return false;
591 JSObject *obj = NewObjectWithGivenProto(cx, &StringIteratorObject::class_, proto, cx->global());
592 if (!obj)
593 return false;
595 args.rval().setObject(*obj);
596 return true;
597 }
599 static bool
600 intrinsic_IsStringIterator(JSContext *cx, unsigned argc, Value *vp)
601 {
602 CallArgs args = CallArgsFromVp(argc, vp);
603 JS_ASSERT(args.length() == 1);
604 JS_ASSERT(args[0].isObject());
606 args.rval().setBoolean(args[0].toObject().is<StringIteratorObject>());
607 return true;
608 }
610 /*
611 * ParallelTestsShouldPass(): Returns false if we are running in a
612 * mode (such as --ion-eager) that is known to cause additional
613 * bailouts or disqualifications for parallel array tests.
614 *
615 * This is needed because the parallel tests generally assert that,
616 * under normal conditions, they will run without bailouts or
617 * compilation failures, but this does not hold under "stress-testing"
618 * conditions like --ion-eager or --no-ti. However, running the tests
619 * under those conditions HAS exposed bugs and thus we do not wish to
620 * disable them entirely. Instead, we simply disable the assertions
621 * that state that no bailouts etc should occur.
622 */
623 static bool
624 intrinsic_ParallelTestsShouldPass(JSContext *cx, unsigned argc, Value *vp)
625 {
626 CallArgs args = CallArgsFromVp(argc, vp);
627 args.rval().setBoolean(ParallelTestsShouldPass(cx));
628 return true;
629 }
631 /*
632 * ShouldForceSequential(): Returns true if parallel ops should take
633 * the sequential fallback path.
634 */
635 bool
636 js::intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp)
637 {
638 CallArgs args = CallArgsFromVp(argc, vp);
639 #ifdef JS_THREADSAFE
640 args.rval().setBoolean(cx->runtime()->forkJoinWarmup ||
641 InParallelSection());
642 #else
643 args.rval().setBoolean(true);
644 #endif
645 return true;
646 }
648 bool
649 js::intrinsic_InParallelSection(JSContext *cx, unsigned argc, Value *vp)
650 {
651 CallArgs args = CallArgsFromVp(argc, vp);
652 args.rval().setBoolean(false);
653 return true;
654 }
656 static bool
657 intrinsic_InParallelSectionPar(ForkJoinContext *cx, unsigned argc, Value *vp)
658 {
659 CallArgs args = CallArgsFromVp(argc, vp);
660 args.rval().setBoolean(true);
661 return true;
662 }
664 JS_JITINFO_NATIVE_PARALLEL(intrinsic_InParallelSection_jitInfo,
665 intrinsic_InParallelSectionPar);
667 /* These wrappers are needed in order to recognize the function
668 * pointers within the JIT, and the raw js:: functions can't be used
669 * directly because they take a ThreadSafeContext* argument.
670 */
671 bool
672 js::intrinsic_ObjectIsTypedObject(JSContext *cx, unsigned argc, Value *vp)
673 {
674 return js::ObjectIsTypedObject(cx, argc, vp);
675 }
677 bool
678 js::intrinsic_ObjectIsTransparentTypedObject(JSContext *cx, unsigned argc, Value *vp)
679 {
680 return js::ObjectIsTransparentTypedObject(cx, argc, vp);
681 }
683 bool
684 js::intrinsic_ObjectIsOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp)
685 {
686 return js::ObjectIsOpaqueTypedObject(cx, argc, vp);
687 }
689 bool
690 js::intrinsic_ObjectIsTypeDescr(JSContext *cx, unsigned argc, Value *vp)
691 {
692 return js::ObjectIsTypeDescr(cx, argc, vp);
693 }
695 bool
696 js::intrinsic_TypeDescrIsSimpleType(JSContext *cx, unsigned argc, Value *vp)
697 {
698 return js::TypeDescrIsSimpleType(cx, argc, vp);
699 }
701 bool
702 js::intrinsic_TypeDescrIsArrayType(JSContext *cx, unsigned argc, Value *vp)
703 {
704 return js::TypeDescrIsArrayType(cx, argc, vp);
705 }
707 bool
708 js::intrinsic_TypeDescrIsUnsizedArrayType(JSContext *cx, unsigned argc, Value *vp)
709 {
710 return js::TypeDescrIsUnsizedArrayType(cx, argc, vp);
711 }
713 bool
714 js::intrinsic_TypeDescrIsSizedArrayType(JSContext *cx, unsigned argc, Value *vp)
715 {
716 return js::TypeDescrIsSizedArrayType(cx, argc, vp);
717 }
719 /**
720 * Returns the default locale as a well-formed, but not necessarily canonicalized,
721 * BCP-47 language tag.
722 */
723 static bool
724 intrinsic_RuntimeDefaultLocale(JSContext *cx, unsigned argc, Value *vp)
725 {
726 CallArgs args = CallArgsFromVp(argc, vp);
728 const char *locale = cx->runtime()->getDefaultLocale();
729 if (!locale) {
730 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEFAULT_LOCALE_ERROR);
731 return false;
732 }
734 RootedString jslocale(cx, JS_NewStringCopyZ(cx, locale));
735 if (!jslocale)
736 return false;
738 args.rval().setString(jslocale);
739 return true;
740 }
742 static const JSFunctionSpec intrinsic_functions[] = {
743 JS_FN("ToObject", intrinsic_ToObject, 1,0),
744 JS_FN("ToInteger", intrinsic_ToInteger, 1,0),
745 JS_FN("IsCallable", intrinsic_IsCallable, 1,0),
746 JS_FN("ThrowError", intrinsic_ThrowError, 4,0),
747 JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1,0),
748 JS_FN("SetScriptHints", intrinsic_SetScriptHints, 2,0),
749 JS_FN("MakeConstructible", intrinsic_MakeConstructible, 1,0),
750 JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0),
751 JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
753 JS_FN("UnsafePutElements", intrinsic_UnsafePutElements, 3,0),
754 JS_FN("_DefineValueProperty", intrinsic_DefineValueProperty, 4,0),
755 JS_FN("UnsafeSetReservedSlot", intrinsic_UnsafeSetReservedSlot, 3,0),
756 JS_FN("UnsafeGetReservedSlot", intrinsic_UnsafeGetReservedSlot, 2,0),
757 JS_FN("HaveSameClass", intrinsic_HaveSameClass, 2,0),
758 JS_FN("IsPackedArray", intrinsic_IsPackedArray, 1,0),
760 JS_FN("GetIteratorPrototype", intrinsic_GetIteratorPrototype, 0,0),
762 JS_FN("NewArrayIterator", intrinsic_NewArrayIterator, 0,0),
763 JS_FN("IsArrayIterator", intrinsic_IsArrayIterator, 1,0),
765 JS_FN("NewStringIterator", intrinsic_NewStringIterator, 0,0),
766 JS_FN("IsStringIterator", intrinsic_IsStringIterator, 1,0),
768 JS_FN("ForkJoin", intrinsic_ForkJoin, 2,0),
769 JS_FN("ForkJoinNumWorkers", intrinsic_ForkJoinNumWorkers, 0,0),
770 JS_FN("NewDenseArray", intrinsic_NewDenseArray, 1,0),
771 JS_FN("ShouldForceSequential", intrinsic_ShouldForceSequential, 0,0),
772 JS_FN("ParallelTestsShouldPass", intrinsic_ParallelTestsShouldPass, 0,0),
773 JS_FNINFO("ClearThreadLocalArenas",
774 intrinsic_ClearThreadLocalArenas,
775 &intrinsic_ClearThreadLocalArenasInfo, 0,0),
776 JS_FNINFO("SetForkJoinTargetRegion",
777 intrinsic_SetForkJoinTargetRegion,
778 &intrinsic_SetForkJoinTargetRegionInfo, 2, 0),
779 JS_FNINFO("ForkJoinGetSlice",
780 intrinsic_ForkJoinGetSlice,
781 &intrinsic_ForkJoinGetSlice_jitInfo, 1, 0),
782 JS_FNINFO("InParallelSection",
783 intrinsic_InParallelSection,
784 &intrinsic_InParallelSection_jitInfo, 0, 0),
786 // See builtin/TypedObject.h for descriptors of the typedobj functions.
787 JS_FN("NewOpaqueTypedObject",
788 js::NewOpaqueTypedObject,
789 1, 0),
790 JS_FN("NewDerivedTypedObject",
791 js::NewDerivedTypedObject,
792 3, 0),
793 JS_FNINFO("AttachTypedObject",
794 JSNativeThreadSafeWrapper<js::AttachTypedObject>,
795 &js::AttachTypedObjectJitInfo, 3, 0),
796 JS_FNINFO("SetTypedObjectOffset",
797 intrinsic_SetTypedObjectOffset,
798 &js::intrinsic_SetTypedObjectOffsetJitInfo, 2, 0),
799 JS_FNINFO("ObjectIsTypeDescr",
800 intrinsic_ObjectIsTypeDescr,
801 &js::ObjectIsTypeDescrJitInfo, 1, 0),
802 JS_FNINFO("ObjectIsTypedObject",
803 intrinsic_ObjectIsTypedObject,
804 &js::ObjectIsTypedObjectJitInfo, 1, 0),
805 JS_FNINFO("ObjectIsTransparentTypedObject",
806 intrinsic_ObjectIsTransparentTypedObject,
807 &js::ObjectIsTransparentTypedObjectJitInfo, 1, 0),
808 JS_FNINFO("TypedObjectIsAttached",
809 JSNativeThreadSafeWrapper<js::TypedObjectIsAttached>,
810 &js::TypedObjectIsAttachedJitInfo, 1, 0),
811 JS_FNINFO("ObjectIsOpaqueTypedObject",
812 intrinsic_ObjectIsOpaqueTypedObject,
813 &js::ObjectIsOpaqueTypedObjectJitInfo, 1, 0),
814 JS_FNINFO("TypeDescrIsArrayType",
815 intrinsic_TypeDescrIsArrayType,
816 &js::TypeDescrIsArrayTypeJitInfo, 1, 0),
817 JS_FNINFO("TypeDescrIsUnsizedArrayType",
818 intrinsic_TypeDescrIsUnsizedArrayType,
819 &js::TypeDescrIsUnsizedArrayTypeJitInfo, 1, 0),
820 JS_FNINFO("TypeDescrIsSizedArrayType",
821 intrinsic_TypeDescrIsSizedArrayType,
822 &js::TypeDescrIsSizedArrayTypeJitInfo, 1, 0),
823 JS_FNINFO("TypeDescrIsSimpleType",
824 intrinsic_TypeDescrIsSimpleType,
825 &js::TypeDescrIsSimpleTypeJitInfo, 1, 0),
826 JS_FNINFO("ClampToUint8",
827 JSNativeThreadSafeWrapper<js::ClampToUint8>,
828 &js::ClampToUint8JitInfo, 1, 0),
829 JS_FNINFO("Memcpy",
830 JSNativeThreadSafeWrapper<js::Memcpy>,
831 &js::MemcpyJitInfo, 5, 0),
832 JS_FN("GetTypedObjectModule", js::GetTypedObjectModule, 0, 0),
833 JS_FN("GetFloat32x4TypeDescr", js::GetFloat32x4TypeDescr, 0, 0),
834 JS_FN("GetInt32x4TypeDescr", js::GetInt32x4TypeDescr, 0, 0),
836 #define LOAD_AND_STORE_SCALAR_FN_DECLS(_constant, _type, _name) \
837 JS_FNINFO("Store_" #_name, \
838 JSNativeThreadSafeWrapper<js::StoreScalar##_type::Func>, \
839 &js::StoreScalar##_type::JitInfo, 3, 0), \
840 JS_FNINFO("Load_" #_name, \
841 JSNativeThreadSafeWrapper<js::LoadScalar##_type::Func>, \
842 &js::LoadScalar##_type::JitInfo, 3, 0),
843 JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(LOAD_AND_STORE_SCALAR_FN_DECLS)
845 #define LOAD_AND_STORE_REFERENCE_FN_DECLS(_constant, _type, _name) \
846 JS_FNINFO("Store_" #_name, \
847 JSNativeThreadSafeWrapper<js::StoreReference##_type::Func>, \
848 &js::StoreReference##_type::JitInfo, 3, 0), \
849 JS_FNINFO("Load_" #_name, \
850 JSNativeThreadSafeWrapper<js::LoadReference##_type::Func>, \
851 &js::LoadReference##_type::JitInfo, 3, 0),
852 JS_FOR_EACH_REFERENCE_TYPE_REPR(LOAD_AND_STORE_REFERENCE_FN_DECLS)
854 // See builtin/Intl.h for descriptions of the intl_* functions.
855 JS_FN("intl_availableCalendars", intl_availableCalendars, 1,0),
856 JS_FN("intl_availableCollations", intl_availableCollations, 1,0),
857 JS_FN("intl_Collator", intl_Collator, 2,0),
858 JS_FN("intl_Collator_availableLocales", intl_Collator_availableLocales, 0,0),
859 JS_FN("intl_CompareStrings", intl_CompareStrings, 3,0),
860 JS_FN("intl_DateTimeFormat", intl_DateTimeFormat, 2,0),
861 JS_FN("intl_DateTimeFormat_availableLocales", intl_DateTimeFormat_availableLocales, 0,0),
862 JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2,0),
863 JS_FN("intl_FormatNumber", intl_FormatNumber, 2,0),
864 JS_FN("intl_NumberFormat", intl_NumberFormat, 2,0),
865 JS_FN("intl_NumberFormat_availableLocales", intl_NumberFormat_availableLocales, 0,0),
866 JS_FN("intl_numberingSystem", intl_numberingSystem, 1,0),
867 JS_FN("intl_patternForSkeleton", intl_patternForSkeleton, 2,0),
869 // See builtin/RegExp.h for descriptions of the regexp_* functions.
870 JS_FN("regexp_exec_no_statics", regexp_exec_no_statics, 2,0),
871 JS_FN("regexp_test_no_statics", regexp_test_no_statics, 2,0),
873 #ifdef DEBUG
874 JS_FNINFO("Dump",
875 JSNativeThreadSafeWrapper<intrinsic_Dump>,
876 &intrinsic_Dump_jitInfo, 1,0),
878 JS_FNINFO("ParallelSpew",
879 JSNativeThreadSafeWrapper<intrinsic_ParallelSpew>,
880 &intrinsic_ParallelSpew_jitInfo, 1,0),
881 #endif
883 JS_FS_END
884 };
886 void
887 js::FillSelfHostingCompileOptions(CompileOptions &options)
888 {
889 /*
890 * In self-hosting mode, scripts emit JSOP_GETINTRINSIC instead of
891 * JSOP_NAME or JSOP_GNAME to access unbound variables. JSOP_GETINTRINSIC
892 * does a name lookup in a special object, whose properties are filled in
893 * lazily upon first access for a given global.
894 *
895 * As that object is inaccessible to client code, the lookups are
896 * guaranteed to return the original objects, ensuring safe implementation
897 * of self-hosted builtins.
898 *
899 * Additionally, the special syntax callFunction(fun, receiver, ...args)
900 * is supported, for which bytecode is emitted that invokes |fun| with
901 * |receiver| as the this-object and ...args as the arguments.
902 */
903 options.setIntroductionType("self-hosted");
904 options.setFileAndLine("self-hosted", 1);
905 options.setSelfHostingMode(true);
906 options.setCanLazilyParse(false);
907 options.setVersion(JSVERSION_LATEST);
908 options.werrorOption = true;
909 options.strictOption = true;
911 #ifdef DEBUG
912 options.extraWarningsOption = true;
913 #endif
914 }
916 bool
917 JSRuntime::initSelfHosting(JSContext *cx)
918 {
919 JS_ASSERT(!selfHostingGlobal_);
921 if (cx->runtime()->parentRuntime) {
922 selfHostingGlobal_ = cx->runtime()->parentRuntime->selfHostingGlobal_;
923 return true;
924 }
926 /*
927 * Self hosted state can be accessed from threads for other runtimes
928 * parented to this one, so cannot include state in the nursery.
929 */
930 JS::AutoDisableGenerationalGC disable(cx->runtime());
932 bool receivesDefaultObject = !cx->options().noDefaultCompartmentObject();
933 RootedObject savedGlobal(cx, receivesDefaultObject
934 ? js::DefaultObjectForContextOrNull(cx)
935 : nullptr);
936 JS::CompartmentOptions compartmentOptions;
937 compartmentOptions.setDiscardSource(true);
938 if (!(selfHostingGlobal_ = JS_NewGlobalObject(cx, &self_hosting_global_class,
939 nullptr, JS::DontFireOnNewGlobalHook,
940 compartmentOptions)))
941 return false;
942 JSAutoCompartment ac(cx, selfHostingGlobal_);
943 if (receivesDefaultObject)
944 js::SetDefaultObjectForContext(cx, selfHostingGlobal_);
945 Rooted<GlobalObject*> shg(cx, &selfHostingGlobal_->as<GlobalObject>());
946 selfHostingGlobal_->compartment()->isSelfHosting = true;
947 selfHostingGlobal_->compartment()->isSystem = true;
948 /*
949 * During initialization of standard classes for the self-hosting global,
950 * all self-hosted functions are ignored. Thus, we don't create cyclic
951 * dependencies in the order of initialization.
952 */
953 if (!GlobalObject::initStandardClasses(cx, shg))
954 return false;
956 if (!JS_DefineFunctions(cx, shg, intrinsic_functions))
957 return false;
959 JS_FireOnNewGlobalObject(cx, shg);
961 CompileOptions options(cx);
962 FillSelfHostingCompileOptions(options);
964 /*
965 * Set a temporary error reporter printing to stderr because it is too
966 * early in the startup process for any other reporter to be registered
967 * and we don't want errors in self-hosted code to be silently swallowed.
968 */
969 JSErrorReporter oldReporter = JS_SetErrorReporter(cx, selfHosting_ErrorReporter);
970 RootedValue rv(cx);
971 bool ok = false;
973 char *filename = getenv("MOZ_SELFHOSTEDJS");
974 if (filename) {
975 RootedScript script(cx, Compile(cx, shg, options, filename));
976 if (script)
977 ok = Execute(cx, script, *shg.get(), rv.address());
978 } else {
979 uint32_t srcLen = GetRawScriptsSize();
981 #ifdef USE_ZLIB
982 const unsigned char *compressed = compressedSources;
983 uint32_t compressedLen = GetCompressedSize();
984 ScopedJSFreePtr<char> src(reinterpret_cast<char *>(cx->malloc_(srcLen)));
985 if (!src || !DecompressString(compressed, compressedLen,
986 reinterpret_cast<unsigned char *>(src.get()), srcLen))
987 {
988 return false;
989 }
990 #else
991 const char *src = rawSources;
992 #endif
994 ok = Evaluate(cx, shg, options, src, srcLen, &rv);
995 }
996 JS_SetErrorReporter(cx, oldReporter);
997 if (receivesDefaultObject)
998 js::SetDefaultObjectForContext(cx, savedGlobal);
999 return ok;
1000 }
1002 void
1003 JSRuntime::finishSelfHosting()
1004 {
1005 selfHostingGlobal_ = nullptr;
1006 }
1008 void
1009 JSRuntime::markSelfHostingGlobal(JSTracer *trc)
1010 {
1011 if (selfHostingGlobal_ && !parentRuntime)
1012 MarkObjectRoot(trc, &selfHostingGlobal_, "self-hosting global");
1013 }
1015 bool
1016 JSRuntime::isSelfHostingCompartment(JSCompartment *comp)
1017 {
1018 return selfHostingGlobal_->compartment() == comp;
1019 }
1021 static bool
1022 CloneValue(JSContext *cx, HandleValue selfHostedValue, MutableHandleValue vp);
1024 static bool
1025 GetUnclonedValue(JSContext *cx, HandleObject selfHostedObject, HandleId id, MutableHandleValue vp)
1026 {
1027 vp.setUndefined();
1029 if (JSID_IS_INT(id)) {
1030 size_t index = JSID_TO_INT(id);
1031 if (index < selfHostedObject->getDenseInitializedLength() &&
1032 !selfHostedObject->getDenseElement(index).isMagic(JS_ELEMENTS_HOLE))
1033 {
1034 vp.set(selfHostedObject->getDenseElement(JSID_TO_INT(id)));
1035 return true;
1036 }
1037 }
1039 // Since all atoms used by self hosting are marked as permanent, any
1040 // attempt to look up a non-permanent atom will fail. We should only
1041 // see such atoms when code is looking for properties on the self
1042 // hosted global which aren't present.
1043 if (JSID_IS_STRING(id) && !JSID_TO_STRING(id)->isPermanentAtom()) {
1044 JS_ASSERT(selfHostedObject->is<GlobalObject>());
1045 RootedValue value(cx, IdToValue(id));
1046 return js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NO_SUCH_SELF_HOSTED_PROP,
1047 JSDVG_IGNORE_STACK, value, NullPtr(), nullptr, nullptr);
1048 }
1050 RootedShape shape(cx, selfHostedObject->nativeLookupPure(id));
1051 if (!shape) {
1052 RootedValue value(cx, IdToValue(id));
1053 return js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NO_SUCH_SELF_HOSTED_PROP,
1054 JSDVG_IGNORE_STACK, value, NullPtr(), nullptr, nullptr);
1055 }
1057 JS_ASSERT(shape->hasSlot() && shape->hasDefaultGetter());
1058 vp.set(selfHostedObject->getSlot(shape->slot()));
1059 return true;
1060 }
1062 static bool
1063 CloneProperties(JSContext *cx, HandleObject selfHostedObject, HandleObject clone)
1064 {
1065 AutoIdVector ids(cx);
1067 for (size_t i = 0; i < selfHostedObject->getDenseInitializedLength(); i++) {
1068 if (!selfHostedObject->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) {
1069 if (!ids.append(INT_TO_JSID(i)))
1070 return false;
1071 }
1072 }
1074 for (Shape::Range<NoGC> range(selfHostedObject->lastProperty()); !range.empty(); range.popFront()) {
1075 Shape &shape = range.front();
1076 if (shape.enumerable() && !ids.append(shape.propid()))
1077 return false;
1078 }
1080 RootedId id(cx);
1081 RootedValue val(cx);
1082 RootedValue selfHostedValue(cx);
1083 for (uint32_t i = 0; i < ids.length(); i++) {
1084 id = ids[i];
1085 if (!GetUnclonedValue(cx, selfHostedObject, id, &selfHostedValue))
1086 return false;
1087 if (!CloneValue(cx, selfHostedValue, &val) ||
1088 !JS_DefinePropertyById(cx, clone, id, val.get(), nullptr, nullptr, 0))
1089 {
1090 return false;
1091 }
1092 }
1094 return true;
1095 }
1097 static JSObject *
1098 CloneObject(JSContext *cx, HandleObject selfHostedObject)
1099 {
1100 AutoCycleDetector detect(cx, selfHostedObject);
1101 if (!detect.init())
1102 return nullptr;
1103 if (detect.foundCycle()) {
1104 JS_ReportError(cx, "SelfHosted cloning cannot handle cyclic object graphs.");
1105 return nullptr;
1106 }
1108 RootedObject clone(cx);
1109 if (selfHostedObject->is<JSFunction>()) {
1110 RootedFunction selfHostedFunction(cx, &selfHostedObject->as<JSFunction>());
1111 bool hasName = selfHostedFunction->atom() != nullptr;
1112 // Arrow functions use the first extended slot for their lexical |this| value.
1113 JS_ASSERT(!selfHostedFunction->isArrow());
1114 js::gc::AllocKind kind = hasName
1115 ? JSFunction::ExtendedFinalizeKind
1116 : selfHostedFunction->getAllocKind();
1117 clone = CloneFunctionObject(cx, selfHostedFunction, cx->global(), kind, TenuredObject);
1118 // To be able to re-lazify the cloned function, its name in the
1119 // self-hosting compartment has to be stored on the clone.
1120 if (clone && hasName)
1121 clone->as<JSFunction>().setExtendedSlot(0, StringValue(selfHostedFunction->atom()));
1122 } else if (selfHostedObject->is<RegExpObject>()) {
1123 RegExpObject &reobj = selfHostedObject->as<RegExpObject>();
1124 RootedAtom source(cx, reobj.getSource());
1125 JS_ASSERT(source->isPermanentAtom());
1126 clone = RegExpObject::createNoStatics(cx, source, reobj.getFlags(), nullptr);
1127 } else if (selfHostedObject->is<DateObject>()) {
1128 clone = JS_NewDateObjectMsec(cx, selfHostedObject->as<DateObject>().UTCTime().toNumber());
1129 } else if (selfHostedObject->is<BooleanObject>()) {
1130 clone = BooleanObject::create(cx, selfHostedObject->as<BooleanObject>().unbox());
1131 } else if (selfHostedObject->is<NumberObject>()) {
1132 clone = NumberObject::create(cx, selfHostedObject->as<NumberObject>().unbox());
1133 } else if (selfHostedObject->is<StringObject>()) {
1134 JSString *selfHostedString = selfHostedObject->as<StringObject>().unbox();
1135 if (!selfHostedString->isFlat())
1136 MOZ_CRASH();
1137 RootedString str(cx, js_NewStringCopyN<CanGC>(cx,
1138 selfHostedString->asFlat().chars(),
1139 selfHostedString->asFlat().length()));
1140 if (!str)
1141 return nullptr;
1142 clone = StringObject::create(cx, str);
1143 } else if (selfHostedObject->is<ArrayObject>()) {
1144 clone = NewDenseEmptyArray(cx, nullptr, TenuredObject);
1145 } else {
1146 JS_ASSERT(selfHostedObject->isNative());
1147 clone = NewObjectWithGivenProto(cx, selfHostedObject->getClass(), nullptr, cx->global(),
1148 selfHostedObject->tenuredGetAllocKind(),
1149 SingletonObject);
1150 }
1151 if (!clone)
1152 return nullptr;
1153 if (!CloneProperties(cx, selfHostedObject, clone))
1154 return nullptr;
1155 return clone;
1156 }
1158 static bool
1159 CloneValue(JSContext *cx, HandleValue selfHostedValue, MutableHandleValue vp)
1160 {
1161 if (selfHostedValue.isObject()) {
1162 RootedObject selfHostedObject(cx, &selfHostedValue.toObject());
1163 JSObject *clone = CloneObject(cx, selfHostedObject);
1164 if (!clone)
1165 return false;
1166 vp.setObject(*clone);
1167 } else if (selfHostedValue.isBoolean() || selfHostedValue.isNumber() || selfHostedValue.isNullOrUndefined()) {
1168 // Nothing to do here: these are represented inline in the value.
1169 vp.set(selfHostedValue);
1170 } else if (selfHostedValue.isString()) {
1171 if (!selfHostedValue.toString()->isFlat())
1172 MOZ_CRASH();
1173 JSFlatString *selfHostedString = &selfHostedValue.toString()->asFlat();
1174 JSString *clone = js_NewStringCopyN<CanGC>(cx,
1175 selfHostedString->chars(),
1176 selfHostedString->length());
1177 if (!clone)
1178 return false;
1179 vp.setString(clone);
1180 } else {
1181 MOZ_CRASH("Self-hosting CloneValue can't clone given value.");
1182 }
1183 return true;
1184 }
1186 bool
1187 JSRuntime::cloneSelfHostedFunctionScript(JSContext *cx, HandlePropertyName name,
1188 HandleFunction targetFun)
1189 {
1190 RootedId id(cx, NameToId(name));
1191 RootedValue funVal(cx);
1192 if (!GetUnclonedValue(cx, HandleObject::fromMarkedLocation(&selfHostingGlobal_), id, &funVal))
1193 return false;
1195 RootedFunction sourceFun(cx, &funVal.toObject().as<JSFunction>());
1196 // JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there
1197 // aren't any.
1198 JS_ASSERT(!sourceFun->isGenerator());
1199 RootedScript sourceScript(cx, sourceFun->getOrCreateScript(cx));
1200 if (!sourceScript)
1201 return false;
1202 JS_ASSERT(!sourceScript->enclosingStaticScope());
1203 JSScript *cscript = CloneScript(cx, NullPtr(), targetFun, sourceScript);
1204 if (!cscript)
1205 return false;
1206 cscript->setFunction(targetFun);
1208 JS_ASSERT(sourceFun->nargs() == targetFun->nargs());
1209 // The target function might have been relazified after it's flags changed.
1210 targetFun->setFlags((targetFun->flags() & ~JSFunction::INTERPRETED_LAZY) |
1211 sourceFun->flags() | JSFunction::EXTENDED);
1212 targetFun->setScript(cscript);
1213 JS_ASSERT(targetFun->isExtended());
1214 return true;
1215 }
1217 bool
1218 JSRuntime::cloneSelfHostedValue(JSContext *cx, HandlePropertyName name, MutableHandleValue vp)
1219 {
1220 RootedId id(cx, NameToId(name));
1221 RootedValue selfHostedValue(cx);
1222 if (!GetUnclonedValue(cx, HandleObject::fromMarkedLocation(&selfHostingGlobal_), id, &selfHostedValue))
1223 return false;
1225 /*
1226 * We don't clone if we're operating in the self-hosting global, as that
1227 * means we're currently executing the self-hosting script while
1228 * initializing the runtime (see JSRuntime::initSelfHosting).
1229 */
1230 if (cx->global() == selfHostingGlobal_) {
1231 vp.set(selfHostedValue);
1232 return true;
1233 }
1235 return CloneValue(cx, selfHostedValue, vp);
1236 }
1238 JSFunction *
1239 js::SelfHostedFunction(JSContext *cx, HandlePropertyName propName)
1240 {
1241 RootedValue func(cx);
1242 if (!GlobalObject::getIntrinsicValue(cx, cx->global(), propName, &func))
1243 return nullptr;
1245 JS_ASSERT(func.isObject());
1246 JS_ASSERT(func.toObject().is<JSFunction>());
1247 return &func.toObject().as<JSFunction>();
1248 }
1250 bool
1251 js::IsSelfHostedFunctionWithName(JSFunction *fun, JSAtom *name)
1252 {
1253 return fun->isSelfHostedBuiltin() && fun->getExtendedSlot(0).toString() == name;
1254 }