js/src/vm/SelfHosting.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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.

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

mercurial