js/src/jit/AsmJSLink.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 "jit/AsmJSLink.h"
michael@0 8
michael@0 9 #include "mozilla/BinarySearch.h"
michael@0 10 #include "mozilla/PodOperations.h"
michael@0 11
michael@0 12 #ifdef MOZ_VTUNE
michael@0 13 # include "vtune/VTuneWrapper.h"
michael@0 14 #endif
michael@0 15
michael@0 16 #include "jscntxt.h"
michael@0 17 #include "jsmath.h"
michael@0 18 #include "jsprf.h"
michael@0 19 #include "jswrapper.h"
michael@0 20
michael@0 21 #include "frontend/BytecodeCompiler.h"
michael@0 22 #include "jit/AsmJSModule.h"
michael@0 23 #include "jit/Ion.h"
michael@0 24 #include "jit/JitCommon.h"
michael@0 25 #ifdef JS_ION_PERF
michael@0 26 # include "jit/PerfSpewer.h"
michael@0 27 #endif
michael@0 28 #include "vm/StringBuffer.h"
michael@0 29
michael@0 30 #include "jsobjinlines.h"
michael@0 31
michael@0 32 using namespace js;
michael@0 33 using namespace js::jit;
michael@0 34
michael@0 35 using mozilla::BinarySearch;
michael@0 36 using mozilla::IsNaN;
michael@0 37 using mozilla::PodZero;
michael@0 38
michael@0 39 AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation *activation)
michael@0 40 {
michael@0 41 if (!activation || activation->isInterruptedSP()) {
michael@0 42 PodZero(this);
michael@0 43 JS_ASSERT(done());
michael@0 44 return;
michael@0 45 }
michael@0 46
michael@0 47 module_ = &activation->module();
michael@0 48 sp_ = activation->exitSP();
michael@0 49
michael@0 50 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
michael@0 51 // For calls to Ion/C++ on x86/x64, the exitSP is the SP right before the call
michael@0 52 // to C++. Since the call instruction pushes the return address, we know
michael@0 53 // that the return address is 1 word below exitSP.
michael@0 54 returnAddress_ = *(uint8_t**)(sp_ - sizeof(void*));
michael@0 55 #else
michael@0 56 // For calls to Ion/C++ on ARM, the *caller* pushes the return address on
michael@0 57 // the stack. For Ion, this is just part of the ABI. For C++, the return
michael@0 58 // address is explicitly pushed before the call since we cannot expect the
michael@0 59 // callee to immediately push lr. This means that exitSP points to the
michael@0 60 // return address.
michael@0 61 returnAddress_ = *(uint8_t**)sp_;
michael@0 62 #endif
michael@0 63
michael@0 64 settle();
michael@0 65 }
michael@0 66
michael@0 67 struct GetCallSite
michael@0 68 {
michael@0 69 const AsmJSModule &module;
michael@0 70 GetCallSite(const AsmJSModule &module) : module(module) {}
michael@0 71 uint32_t operator[](size_t index) const {
michael@0 72 return module.callSite(index).returnAddressOffset();
michael@0 73 }
michael@0 74 };
michael@0 75
michael@0 76 void
michael@0 77 AsmJSFrameIterator::popFrame()
michael@0 78 {
michael@0 79 // After adding stackDepth, sp points to the word before the return address,
michael@0 80 // on both ARM and x86/x64.
michael@0 81 sp_ += callsite_->stackDepth();
michael@0 82 returnAddress_ = *(uint8_t**)(sp_ - sizeof(void*));
michael@0 83 }
michael@0 84
michael@0 85 void
michael@0 86 AsmJSFrameIterator::settle()
michael@0 87 {
michael@0 88 while (true) {
michael@0 89 uint32_t target = returnAddress_ - module_->codeBase();
michael@0 90 size_t lowerBound = 0;
michael@0 91 size_t upperBound = module_->numCallSites();
michael@0 92
michael@0 93 size_t match;
michael@0 94 if (!BinarySearch(GetCallSite(*module_), lowerBound, upperBound, target, &match)) {
michael@0 95 callsite_ = nullptr;
michael@0 96 return;
michael@0 97 }
michael@0 98
michael@0 99 callsite_ = &module_->callSite(match);
michael@0 100
michael@0 101 if (callsite_->isExit()) {
michael@0 102 popFrame();
michael@0 103 continue;
michael@0 104 }
michael@0 105
michael@0 106 if (callsite_->isEntry()) {
michael@0 107 callsite_ = nullptr;
michael@0 108 return;
michael@0 109 }
michael@0 110
michael@0 111 JS_ASSERT(callsite_->isNormal());
michael@0 112 return;
michael@0 113 }
michael@0 114 }
michael@0 115
michael@0 116 JSAtom *
michael@0 117 AsmJSFrameIterator::functionDisplayAtom() const
michael@0 118 {
michael@0 119 JS_ASSERT(!done());
michael@0 120 return module_->functionName(callsite_->functionNameIndex());
michael@0 121 }
michael@0 122
michael@0 123 unsigned
michael@0 124 AsmJSFrameIterator::computeLine(uint32_t *column) const
michael@0 125 {
michael@0 126 JS_ASSERT(!done());
michael@0 127 if (column)
michael@0 128 *column = callsite_->column();
michael@0 129 return callsite_->line();
michael@0 130 }
michael@0 131
michael@0 132 static bool
michael@0 133 CloneModule(JSContext *cx, MutableHandle<AsmJSModuleObject*> moduleObj)
michael@0 134 {
michael@0 135 ScopedJSDeletePtr<AsmJSModule> module;
michael@0 136 if (!moduleObj->module().clone(cx, &module))
michael@0 137 return false;
michael@0 138
michael@0 139 module->staticallyLink(cx);
michael@0 140
michael@0 141 AsmJSModuleObject *newModuleObj = AsmJSModuleObject::create(cx, &module);
michael@0 142 if (!newModuleObj)
michael@0 143 return false;
michael@0 144
michael@0 145 moduleObj.set(newModuleObj);
michael@0 146 return true;
michael@0 147 }
michael@0 148
michael@0 149 static bool
michael@0 150 LinkFail(JSContext *cx, const char *str)
michael@0 151 {
michael@0 152 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, js_GetErrorMessage,
michael@0 153 nullptr, JSMSG_USE_ASM_LINK_FAIL, str);
michael@0 154 return false;
michael@0 155 }
michael@0 156
michael@0 157 static bool
michael@0 158 GetDataProperty(JSContext *cx, HandleValue objVal, HandlePropertyName field, MutableHandleValue v)
michael@0 159 {
michael@0 160 if (!objVal.isObject())
michael@0 161 return LinkFail(cx, "accessing property of non-object");
michael@0 162
michael@0 163 Rooted<JSPropertyDescriptor> desc(cx);
michael@0 164 RootedObject obj(cx, &objVal.toObject());
michael@0 165 RootedId id(cx, NameToId(field));
michael@0 166 if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc))
michael@0 167 return false;
michael@0 168
michael@0 169 if (!desc.object())
michael@0 170 return LinkFail(cx, "property not present on object");
michael@0 171
michael@0 172 if (desc.hasGetterOrSetterObject())
michael@0 173 return LinkFail(cx, "property is not a data property");
michael@0 174
michael@0 175 v.set(desc.value());
michael@0 176 return true;
michael@0 177 }
michael@0 178
michael@0 179 static bool
michael@0 180 ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Global &global,
michael@0 181 HandleValue importVal)
michael@0 182 {
michael@0 183 JS_ASSERT(global.which() == AsmJSModule::Global::Variable);
michael@0 184
michael@0 185 void *datum = module.globalVarIndexToGlobalDatum(global.varIndex());
michael@0 186
michael@0 187 switch (global.varInitKind()) {
michael@0 188 case AsmJSModule::Global::InitConstant: {
michael@0 189 const Value &v = global.varInitConstant();
michael@0 190 switch (global.varInitCoercion()) {
michael@0 191 case AsmJS_ToInt32:
michael@0 192 *(int32_t *)datum = v.toInt32();
michael@0 193 break;
michael@0 194 case AsmJS_ToNumber:
michael@0 195 *(double *)datum = v.toDouble();
michael@0 196 break;
michael@0 197 case AsmJS_FRound:
michael@0 198 *(float *)datum = static_cast<float>(v.toDouble());
michael@0 199 break;
michael@0 200 }
michael@0 201 break;
michael@0 202 }
michael@0 203 case AsmJSModule::Global::InitImport: {
michael@0 204 RootedPropertyName field(cx, global.varImportField());
michael@0 205 RootedValue v(cx);
michael@0 206 if (!GetDataProperty(cx, importVal, field, &v))
michael@0 207 return false;
michael@0 208
michael@0 209 switch (global.varInitCoercion()) {
michael@0 210 case AsmJS_ToInt32:
michael@0 211 if (!ToInt32(cx, v, (int32_t *)datum))
michael@0 212 return false;
michael@0 213 break;
michael@0 214 case AsmJS_ToNumber:
michael@0 215 if (!ToNumber(cx, v, (double *)datum))
michael@0 216 return false;
michael@0 217 break;
michael@0 218 case AsmJS_FRound:
michael@0 219 if (!RoundFloat32(cx, v, (float *)datum))
michael@0 220 return false;
michael@0 221 break;
michael@0 222 }
michael@0 223 break;
michael@0 224 }
michael@0 225 }
michael@0 226
michael@0 227 return true;
michael@0 228 }
michael@0 229
michael@0 230 static bool
michael@0 231 ValidateFFI(JSContext *cx, AsmJSModule::Global &global, HandleValue importVal,
michael@0 232 AutoObjectVector *ffis)
michael@0 233 {
michael@0 234 RootedPropertyName field(cx, global.ffiField());
michael@0 235 RootedValue v(cx);
michael@0 236 if (!GetDataProperty(cx, importVal, field, &v))
michael@0 237 return false;
michael@0 238
michael@0 239 if (!v.isObject() || !v.toObject().is<JSFunction>())
michael@0 240 return LinkFail(cx, "FFI imports must be functions");
michael@0 241
michael@0 242 (*ffis)[global.ffiIndex()] = &v.toObject().as<JSFunction>();
michael@0 243 return true;
michael@0 244 }
michael@0 245
michael@0 246 static bool
michael@0 247 ValidateArrayView(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal,
michael@0 248 HandleValue bufferVal)
michael@0 249 {
michael@0 250 RootedPropertyName field(cx, global.viewName());
michael@0 251 RootedValue v(cx);
michael@0 252 if (!GetDataProperty(cx, globalVal, field, &v))
michael@0 253 return false;
michael@0 254
michael@0 255 if (!IsTypedArrayConstructor(v, global.viewType()))
michael@0 256 return LinkFail(cx, "bad typed array constructor");
michael@0 257
michael@0 258 return true;
michael@0 259 }
michael@0 260
michael@0 261 static bool
michael@0 262 ValidateMathBuiltinFunction(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
michael@0 263 {
michael@0 264 RootedValue v(cx);
michael@0 265 if (!GetDataProperty(cx, globalVal, cx->names().Math, &v))
michael@0 266 return false;
michael@0 267 RootedPropertyName field(cx, global.mathName());
michael@0 268 if (!GetDataProperty(cx, v, field, &v))
michael@0 269 return false;
michael@0 270
michael@0 271 Native native = nullptr;
michael@0 272 switch (global.mathBuiltinFunction()) {
michael@0 273 case AsmJSMathBuiltin_sin: native = math_sin; break;
michael@0 274 case AsmJSMathBuiltin_cos: native = math_cos; break;
michael@0 275 case AsmJSMathBuiltin_tan: native = math_tan; break;
michael@0 276 case AsmJSMathBuiltin_asin: native = math_asin; break;
michael@0 277 case AsmJSMathBuiltin_acos: native = math_acos; break;
michael@0 278 case AsmJSMathBuiltin_atan: native = math_atan; break;
michael@0 279 case AsmJSMathBuiltin_ceil: native = math_ceil; break;
michael@0 280 case AsmJSMathBuiltin_floor: native = math_floor; break;
michael@0 281 case AsmJSMathBuiltin_exp: native = math_exp; break;
michael@0 282 case AsmJSMathBuiltin_log: native = math_log; break;
michael@0 283 case AsmJSMathBuiltin_pow: native = js_math_pow; break;
michael@0 284 case AsmJSMathBuiltin_sqrt: native = js_math_sqrt; break;
michael@0 285 case AsmJSMathBuiltin_min: native = js_math_min; break;
michael@0 286 case AsmJSMathBuiltin_max: native = js_math_max; break;
michael@0 287 case AsmJSMathBuiltin_abs: native = js_math_abs; break;
michael@0 288 case AsmJSMathBuiltin_atan2: native = math_atan2; break;
michael@0 289 case AsmJSMathBuiltin_imul: native = math_imul; break;
michael@0 290 case AsmJSMathBuiltin_fround: native = math_fround; break;
michael@0 291 }
michael@0 292
michael@0 293 if (!IsNativeFunction(v, native))
michael@0 294 return LinkFail(cx, "bad Math.* builtin function");
michael@0 295
michael@0 296 return true;
michael@0 297 }
michael@0 298
michael@0 299 static bool
michael@0 300 ValidateConstant(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
michael@0 301 {
michael@0 302 RootedPropertyName field(cx, global.constantName());
michael@0 303 RootedValue v(cx, globalVal);
michael@0 304
michael@0 305 if (global.constantKind() == AsmJSModule::Global::MathConstant) {
michael@0 306 if (!GetDataProperty(cx, v, cx->names().Math, &v))
michael@0 307 return false;
michael@0 308 }
michael@0 309
michael@0 310 if (!GetDataProperty(cx, v, field, &v))
michael@0 311 return false;
michael@0 312 if (!v.isNumber())
michael@0 313 return LinkFail(cx, "math / global constant value needs to be a number");
michael@0 314
michael@0 315 // NaN != NaN
michael@0 316 if (IsNaN(global.constantValue())) {
michael@0 317 if (!IsNaN(v.toNumber()))
michael@0 318 return LinkFail(cx, "global constant value needs to be NaN");
michael@0 319 } else {
michael@0 320 if (v.toNumber() != global.constantValue())
michael@0 321 return LinkFail(cx, "global constant value mismatch");
michael@0 322 }
michael@0 323
michael@0 324 return true;
michael@0 325 }
michael@0 326
michael@0 327 static bool
michael@0 328 LinkModuleToHeap(JSContext *cx, AsmJSModule &module, Handle<ArrayBufferObject*> heap)
michael@0 329 {
michael@0 330 uint32_t heapLength = heap->byteLength();
michael@0 331 if (!IsValidAsmJSHeapLength(heapLength)) {
michael@0 332 ScopedJSFreePtr<char> msg(
michael@0 333 JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next "
michael@0 334 "valid length is 0x%x",
michael@0 335 heapLength,
michael@0 336 RoundUpToNextValidAsmJSHeapLength(heapLength)));
michael@0 337 return LinkFail(cx, msg.get());
michael@0 338 }
michael@0 339
michael@0 340 // This check is sufficient without considering the size of the loaded datum because heap
michael@0 341 // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
michael@0 342 JS_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
michael@0 343 if (heapLength < module.minHeapLength()) {
michael@0 344 ScopedJSFreePtr<char> msg(
michael@0 345 JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (which is the"
michael@0 346 "largest constant heap access offset rounded up to the next valid "
michael@0 347 "heap size).",
michael@0 348 heapLength,
michael@0 349 module.minHeapLength()));
michael@0 350 return LinkFail(cx, msg.get());
michael@0 351 }
michael@0 352
michael@0 353 if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
michael@0 354 return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
michael@0 355
michael@0 356 module.initHeap(heap, cx);
michael@0 357 return true;
michael@0 358 }
michael@0 359
michael@0 360 static bool
michael@0 361 DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
michael@0 362 {
michael@0 363 module.setIsDynamicallyLinked();
michael@0 364
michael@0 365 RootedValue globalVal(cx);
michael@0 366 if (args.length() > 0)
michael@0 367 globalVal = args[0];
michael@0 368
michael@0 369 RootedValue importVal(cx);
michael@0 370 if (args.length() > 1)
michael@0 371 importVal = args[1];
michael@0 372
michael@0 373 RootedValue bufferVal(cx);
michael@0 374 if (args.length() > 2)
michael@0 375 bufferVal = args[2];
michael@0 376
michael@0 377 Rooted<ArrayBufferObject*> heap(cx);
michael@0 378 if (module.hasArrayView()) {
michael@0 379 if (!IsTypedArrayBuffer(bufferVal))
michael@0 380 return LinkFail(cx, "bad ArrayBuffer argument");
michael@0 381
michael@0 382 heap = &AsTypedArrayBuffer(bufferVal);
michael@0 383 if (!LinkModuleToHeap(cx, module, heap))
michael@0 384 return false;
michael@0 385 }
michael@0 386
michael@0 387 AutoObjectVector ffis(cx);
michael@0 388 if (!ffis.resize(module.numFFIs()))
michael@0 389 return false;
michael@0 390
michael@0 391 for (unsigned i = 0; i < module.numGlobals(); i++) {
michael@0 392 AsmJSModule::Global &global = module.global(i);
michael@0 393 switch (global.which()) {
michael@0 394 case AsmJSModule::Global::Variable:
michael@0 395 if (!ValidateGlobalVariable(cx, module, global, importVal))
michael@0 396 return false;
michael@0 397 break;
michael@0 398 case AsmJSModule::Global::FFI:
michael@0 399 if (!ValidateFFI(cx, global, importVal, &ffis))
michael@0 400 return false;
michael@0 401 break;
michael@0 402 case AsmJSModule::Global::ArrayView:
michael@0 403 if (!ValidateArrayView(cx, global, globalVal, bufferVal))
michael@0 404 return false;
michael@0 405 break;
michael@0 406 case AsmJSModule::Global::MathBuiltinFunction:
michael@0 407 if (!ValidateMathBuiltinFunction(cx, global, globalVal))
michael@0 408 return false;
michael@0 409 break;
michael@0 410 case AsmJSModule::Global::Constant:
michael@0 411 if (!ValidateConstant(cx, global, globalVal))
michael@0 412 return false;
michael@0 413 break;
michael@0 414 }
michael@0 415 }
michael@0 416
michael@0 417 for (unsigned i = 0; i < module.numExits(); i++)
michael@0 418 module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>();
michael@0 419
michael@0 420 return true;
michael@0 421 }
michael@0 422
michael@0 423 static const unsigned ASM_MODULE_SLOT = 0;
michael@0 424 static const unsigned ASM_EXPORT_INDEX_SLOT = 1;
michael@0 425
michael@0 426 static unsigned
michael@0 427 FunctionToExportedFunctionIndex(HandleFunction fun)
michael@0 428 {
michael@0 429 Value v = fun->getExtendedSlot(ASM_EXPORT_INDEX_SLOT);
michael@0 430 return v.toInt32();
michael@0 431 }
michael@0 432
michael@0 433 static const AsmJSModule::ExportedFunction &
michael@0 434 FunctionToExportedFunction(HandleFunction fun, AsmJSModule &module)
michael@0 435 {
michael@0 436 unsigned funIndex = FunctionToExportedFunctionIndex(fun);
michael@0 437 return module.exportedFunction(funIndex);
michael@0 438 }
michael@0 439
michael@0 440 static AsmJSModule &
michael@0 441 FunctionToEnclosingModule(HandleFunction fun)
michael@0 442 {
michael@0 443 return fun->getExtendedSlot(ASM_MODULE_SLOT).toObject().as<AsmJSModuleObject>().module();
michael@0 444 }
michael@0 445
michael@0 446 // The JSNative for the functions nested in an asm.js module. Calling this
michael@0 447 // native will trampoline into generated code.
michael@0 448 static bool
michael@0 449 CallAsmJS(JSContext *cx, unsigned argc, Value *vp)
michael@0 450 {
michael@0 451 CallArgs callArgs = CallArgsFromVp(argc, vp);
michael@0 452 RootedFunction callee(cx, &callArgs.callee().as<JSFunction>());
michael@0 453
michael@0 454 // An asm.js function stores, in its extended slots:
michael@0 455 // - a pointer to the module from which it was returned
michael@0 456 // - its index in the ordered list of exported functions
michael@0 457 AsmJSModule &module = FunctionToEnclosingModule(callee);
michael@0 458
michael@0 459 // An exported function points to the code as well as the exported
michael@0 460 // function's signature, which implies the dynamic coercions performed on
michael@0 461 // the arguments.
michael@0 462 const AsmJSModule::ExportedFunction &func = FunctionToExportedFunction(callee, module);
michael@0 463
michael@0 464 // The calling convention for an external call into asm.js is to pass an
michael@0 465 // array of 8-byte values where each value contains either a coerced int32
michael@0 466 // (in the low word) or double value, with the coercions specified by the
michael@0 467 // asm.js signature. The external entry point unpacks this array into the
michael@0 468 // system-ABI-specified registers and stack memory and then calls into the
michael@0 469 // internal entry point. The return value is stored in the first element of
michael@0 470 // the array (which, therefore, must have length >= 1).
michael@0 471
michael@0 472 js::Vector<uint64_t, 8> coercedArgs(cx);
michael@0 473 if (!coercedArgs.resize(Max<size_t>(1, func.numArgs())))
michael@0 474 return false;
michael@0 475
michael@0 476 RootedValue v(cx);
michael@0 477 for (unsigned i = 0; i < func.numArgs(); ++i) {
michael@0 478 v = i < callArgs.length() ? callArgs[i] : UndefinedValue();
michael@0 479 switch (func.argCoercion(i)) {
michael@0 480 case AsmJS_ToInt32:
michael@0 481 if (!ToInt32(cx, v, (int32_t*)&coercedArgs[i]))
michael@0 482 return false;
michael@0 483 break;
michael@0 484 case AsmJS_ToNumber:
michael@0 485 if (!ToNumber(cx, v, (double*)&coercedArgs[i]))
michael@0 486 return false;
michael@0 487 break;
michael@0 488 case AsmJS_FRound:
michael@0 489 if (!RoundFloat32(cx, v, (float *)&coercedArgs[i]))
michael@0 490 return false;
michael@0 491 break;
michael@0 492 }
michael@0 493 }
michael@0 494
michael@0 495 // An asm.js module is specialized to its heap's base address and length
michael@0 496 // which is normally immutable except for the neuter operation that occurs
michael@0 497 // when an ArrayBuffer is transfered. Throw an internal error if we're
michael@0 498 // about to run with a neutered heap.
michael@0 499 if (module.maybeHeapBufferObject() && module.maybeHeapBufferObject()->isNeutered()) {
michael@0 500 js_ReportOverRecursed(cx);
michael@0 501 return false;
michael@0 502 }
michael@0 503
michael@0 504 {
michael@0 505 // Push an AsmJSActivation to describe the asm.js frames we're about to
michael@0 506 // push when running this module. Additionally, push a JitActivation so
michael@0 507 // that the optimized asm.js-to-Ion FFI call path (which we want to be
michael@0 508 // very fast) can avoid doing so. The JitActivation is marked as
michael@0 509 // inactive so stack iteration will skip over it.
michael@0 510 AsmJSActivation activation(cx, module);
michael@0 511 JitActivation jitActivation(cx, /* firstFrameIsConstructing = */ false, /* active */ false);
michael@0 512
michael@0 513 // Call the per-exported-function trampoline created by GenerateEntry.
michael@0 514 AsmJSModule::CodePtr enter = module.entryTrampoline(func);
michael@0 515 if (!CALL_GENERATED_ASMJS(enter, coercedArgs.begin(), module.globalData()))
michael@0 516 return false;
michael@0 517 }
michael@0 518
michael@0 519 switch (func.returnType()) {
michael@0 520 case AsmJSModule::Return_Void:
michael@0 521 callArgs.rval().set(UndefinedValue());
michael@0 522 break;
michael@0 523 case AsmJSModule::Return_Int32:
michael@0 524 callArgs.rval().set(Int32Value(*(int32_t*)&coercedArgs[0]));
michael@0 525 break;
michael@0 526 case AsmJSModule::Return_Double:
michael@0 527 callArgs.rval().set(NumberValue(*(double*)&coercedArgs[0]));
michael@0 528 break;
michael@0 529 }
michael@0 530
michael@0 531 return true;
michael@0 532 }
michael@0 533
michael@0 534 static JSFunction *
michael@0 535 NewExportedFunction(JSContext *cx, const AsmJSModule::ExportedFunction &func,
michael@0 536 HandleObject moduleObj, unsigned exportIndex)
michael@0 537 {
michael@0 538 RootedPropertyName name(cx, func.name());
michael@0 539 JSFunction *fun = NewFunction(cx, NullPtr(), CallAsmJS, func.numArgs(),
michael@0 540 JSFunction::NATIVE_FUN, cx->global(), name,
michael@0 541 JSFunction::ExtendedFinalizeKind);
michael@0 542 if (!fun)
michael@0 543 return nullptr;
michael@0 544
michael@0 545 fun->setExtendedSlot(ASM_MODULE_SLOT, ObjectValue(*moduleObj));
michael@0 546 fun->setExtendedSlot(ASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
michael@0 547 return fun;
michael@0 548 }
michael@0 549
michael@0 550 static bool
michael@0 551 HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, HandlePropertyName name)
michael@0 552 {
michael@0 553 if (cx->isExceptionPending())
michael@0 554 return false;
michael@0 555
michael@0 556 uint32_t begin = module.offsetToEndOfUseAsm();
michael@0 557 uint32_t end = module.funcEndBeforeCurly();
michael@0 558 Rooted<JSFlatString*> src(cx, module.scriptSource()->substring(cx, begin, end));
michael@0 559 if (!src)
michael@0 560 return false;
michael@0 561
michael@0 562 RootedFunction fun(cx, NewFunction(cx, NullPtr(), nullptr, 0, JSFunction::INTERPRETED,
michael@0 563 cx->global(), name, JSFunction::FinalizeKind,
michael@0 564 TenuredObject));
michael@0 565 if (!fun)
michael@0 566 return false;
michael@0 567
michael@0 568 AutoNameVector formals(cx);
michael@0 569 formals.reserve(3);
michael@0 570 if (module.globalArgumentName())
michael@0 571 formals.infallibleAppend(module.globalArgumentName());
michael@0 572 if (module.importArgumentName())
michael@0 573 formals.infallibleAppend(module.importArgumentName());
michael@0 574 if (module.bufferArgumentName())
michael@0 575 formals.infallibleAppend(module.bufferArgumentName());
michael@0 576
michael@0 577 CompileOptions options(cx);
michael@0 578 options.setOriginPrincipals(module.scriptSource()->originPrincipals())
michael@0 579 .setCompileAndGo(false)
michael@0 580 .setNoScriptRval(false);
michael@0 581
michael@0 582 SourceBufferHolder srcBuf(src->chars(), end - begin, SourceBufferHolder::NoOwnership);
michael@0 583 if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf))
michael@0 584 return false;
michael@0 585
michael@0 586 // Call the function we just recompiled.
michael@0 587 args.setCallee(ObjectValue(*fun));
michael@0 588 return Invoke(cx, args);
michael@0 589 }
michael@0 590
michael@0 591 #ifdef MOZ_VTUNE
michael@0 592 static bool
michael@0 593 SendFunctionsToVTune(JSContext *cx, AsmJSModule &module)
michael@0 594 {
michael@0 595 uint8_t *base = module.codeBase();
michael@0 596
michael@0 597 for (unsigned i = 0; i < module.numProfiledFunctions(); i++) {
michael@0 598 const AsmJSModule::ProfiledFunction &func = module.profiledFunction(i);
michael@0 599
michael@0 600 uint8_t *start = base + func.pod.startCodeOffset;
michael@0 601 uint8_t *end = base + func.pod.endCodeOffset;
michael@0 602 JS_ASSERT(end >= start);
michael@0 603
michael@0 604 unsigned method_id = iJIT_GetNewMethodID();
michael@0 605 if (method_id == 0)
michael@0 606 return false;
michael@0 607
michael@0 608 JSAutoByteString bytes;
michael@0 609 const char *method_name = AtomToPrintableString(cx, func.name, &bytes);
michael@0 610 if (!method_name)
michael@0 611 return false;
michael@0 612
michael@0 613 iJIT_Method_Load method;
michael@0 614 method.method_id = method_id;
michael@0 615 method.method_name = const_cast<char *>(method_name);
michael@0 616 method.method_load_address = (void *)start;
michael@0 617 method.method_size = unsigned(end - start);
michael@0 618 method.line_number_size = 0;
michael@0 619 method.line_number_table = nullptr;
michael@0 620 method.class_id = 0;
michael@0 621 method.class_file_name = nullptr;
michael@0 622 method.source_file_name = nullptr;
michael@0 623
michael@0 624 iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void *)&method);
michael@0 625 }
michael@0 626
michael@0 627 return true;
michael@0 628 }
michael@0 629 #endif
michael@0 630
michael@0 631 #ifdef JS_ION_PERF
michael@0 632 static bool
michael@0 633 SendFunctionsToPerf(JSContext *cx, AsmJSModule &module)
michael@0 634 {
michael@0 635 if (!PerfFuncEnabled())
michael@0 636 return true;
michael@0 637
michael@0 638 uintptr_t base = (uintptr_t) module.codeBase();
michael@0 639 const char *filename = module.scriptSource()->filename();
michael@0 640
michael@0 641 for (unsigned i = 0; i < module.numProfiledFunctions(); i++) {
michael@0 642 const AsmJSModule::ProfiledFunction &func = module.profiledFunction(i);
michael@0 643 uintptr_t start = base + (unsigned long) func.pod.startCodeOffset;
michael@0 644 uintptr_t end = base + (unsigned long) func.pod.endCodeOffset;
michael@0 645 JS_ASSERT(end >= start);
michael@0 646 size_t size = end - start;
michael@0 647
michael@0 648 JSAutoByteString bytes;
michael@0 649 const char *name = AtomToPrintableString(cx, func.name, &bytes);
michael@0 650 if (!name)
michael@0 651 return false;
michael@0 652
michael@0 653 writePerfSpewerAsmJSFunctionMap(start, size, filename, func.pod.lineno,
michael@0 654 func.pod.columnIndex, name);
michael@0 655 }
michael@0 656
michael@0 657 return true;
michael@0 658 }
michael@0 659
michael@0 660 static bool
michael@0 661 SendBlocksToPerf(JSContext *cx, AsmJSModule &module)
michael@0 662 {
michael@0 663 if (!PerfBlockEnabled())
michael@0 664 return true;
michael@0 665
michael@0 666 unsigned long funcBaseAddress = (unsigned long) module.codeBase();
michael@0 667 const char *filename = module.scriptSource()->filename();
michael@0 668
michael@0 669 for (unsigned i = 0; i < module.numPerfBlocksFunctions(); i++) {
michael@0 670 const AsmJSModule::ProfiledBlocksFunction &func = module.perfProfiledBlocksFunction(i);
michael@0 671
michael@0 672 size_t size = func.pod.endCodeOffset - func.pod.startCodeOffset;
michael@0 673
michael@0 674 JSAutoByteString bytes;
michael@0 675 const char *name = AtomToPrintableString(cx, func.name, &bytes);
michael@0 676 if (!name)
michael@0 677 return false;
michael@0 678
michael@0 679 writePerfSpewerAsmJSBlocksMap(funcBaseAddress, func.pod.startCodeOffset,
michael@0 680 func.endInlineCodeOffset, size, filename, name, func.blocks);
michael@0 681 }
michael@0 682
michael@0 683 return true;
michael@0 684 }
michael@0 685 #endif
michael@0 686
michael@0 687 static bool
michael@0 688 SendModuleToAttachedProfiler(JSContext *cx, AsmJSModule &module)
michael@0 689 {
michael@0 690 #if defined(MOZ_VTUNE)
michael@0 691 if (IsVTuneProfilingActive() && !SendFunctionsToVTune(cx, module))
michael@0 692 return false;
michael@0 693 #endif
michael@0 694
michael@0 695 #if defined(JS_ION_PERF)
michael@0 696 if (module.numExportedFunctions() > 0) {
michael@0 697 size_t firstEntryCode = (size_t) module.entryTrampoline(module.exportedFunction(0));
michael@0 698 writePerfSpewerAsmJSEntriesAndExits(firstEntryCode, (size_t) module.globalData() - firstEntryCode);
michael@0 699 }
michael@0 700 if (!SendBlocksToPerf(cx, module))
michael@0 701 return false;
michael@0 702 if (!SendFunctionsToPerf(cx, module))
michael@0 703 return false;
michael@0 704 #endif
michael@0 705
michael@0 706 return true;
michael@0 707 }
michael@0 708
michael@0 709
michael@0 710 static JSObject *
michael@0 711 CreateExportObject(JSContext *cx, Handle<AsmJSModuleObject*> moduleObj)
michael@0 712 {
michael@0 713 AsmJSModule &module = moduleObj->module();
michael@0 714
michael@0 715 if (module.numExportedFunctions() == 1) {
michael@0 716 const AsmJSModule::ExportedFunction &func = module.exportedFunction(0);
michael@0 717 if (!func.maybeFieldName())
michael@0 718 return NewExportedFunction(cx, func, moduleObj, 0);
michael@0 719 }
michael@0 720
michael@0 721 gc::AllocKind allocKind = gc::GetGCObjectKind(module.numExportedFunctions());
michael@0 722 RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind));
michael@0 723 if (!obj)
michael@0 724 return nullptr;
michael@0 725
michael@0 726 for (unsigned i = 0; i < module.numExportedFunctions(); i++) {
michael@0 727 const AsmJSModule::ExportedFunction &func = module.exportedFunction(i);
michael@0 728
michael@0 729 RootedFunction fun(cx, NewExportedFunction(cx, func, moduleObj, i));
michael@0 730 if (!fun)
michael@0 731 return nullptr;
michael@0 732
michael@0 733 JS_ASSERT(func.maybeFieldName() != nullptr);
michael@0 734 RootedId id(cx, NameToId(func.maybeFieldName()));
michael@0 735 RootedValue val(cx, ObjectValue(*fun));
michael@0 736 if (!DefineNativeProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE))
michael@0 737 return nullptr;
michael@0 738 }
michael@0 739
michael@0 740 return obj;
michael@0 741 }
michael@0 742
michael@0 743 static const unsigned MODULE_FUN_SLOT = 0;
michael@0 744
michael@0 745 static AsmJSModuleObject &
michael@0 746 ModuleFunctionToModuleObject(JSFunction *fun)
michael@0 747 {
michael@0 748 return fun->getExtendedSlot(MODULE_FUN_SLOT).toObject().as<AsmJSModuleObject>();
michael@0 749 }
michael@0 750
michael@0 751 // Implements the semantics of an asm.js module function that has been successfully validated.
michael@0 752 static bool
michael@0 753 LinkAsmJS(JSContext *cx, unsigned argc, JS::Value *vp)
michael@0 754 {
michael@0 755 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 756
michael@0 757 // The LinkAsmJS builtin (created by NewAsmJSModuleFunction) is an extended
michael@0 758 // function and stores its module in an extended slot.
michael@0 759 RootedFunction fun(cx, &args.callee().as<JSFunction>());
michael@0 760 Rooted<AsmJSModuleObject*> moduleObj(cx, &ModuleFunctionToModuleObject(fun));
michael@0 761
michael@0 762 // All ICache flushing of the module being linked has been inhibited under the
michael@0 763 // assumption that the module is flushed after dynamic linking (when the last code
michael@0 764 // mutation occurs). Thus, enter an AutoFlushICache context for the entire module
michael@0 765 // now. The module range is set below.
michael@0 766 AutoFlushICache afc("LinkAsmJS");
michael@0 767
michael@0 768 // When a module is linked, it is dynamically specialized to the given
michael@0 769 // arguments (buffer, ffis). Thus, if the module is linked again (it is just
michael@0 770 // a function so it can be called multiple times), we need to clone a new
michael@0 771 // module.
michael@0 772 if (moduleObj->module().isDynamicallyLinked()) {
michael@0 773 if (!CloneModule(cx, &moduleObj))
michael@0 774 return false;
michael@0 775 } else {
michael@0 776 // CloneModule already calls setAutoFlushICacheRange internally before patching
michael@0 777 // the cloned module, so avoid calling twice.
michael@0 778 moduleObj->module().setAutoFlushICacheRange();
michael@0 779 }
michael@0 780
michael@0 781 AsmJSModule &module = moduleObj->module();
michael@0 782
michael@0 783 // Link the module by performing the link-time validation checks in the
michael@0 784 // asm.js spec and then patching the generated module to associate it with
michael@0 785 // the given heap (ArrayBuffer) and a new global data segment (the closure
michael@0 786 // state shared by the inner asm.js functions).
michael@0 787 if (!DynamicallyLinkModule(cx, args, module)) {
michael@0 788 // Linking failed, so reparse the entire asm.js module from scratch to
michael@0 789 // get normal interpreted bytecode which we can simply Invoke. Very slow.
michael@0 790 RootedPropertyName name(cx, fun->name());
michael@0 791 return HandleDynamicLinkFailure(cx, args, module, name);
michael@0 792 }
michael@0 793
michael@0 794 // Notify profilers so that asm.js generated code shows up with JS function
michael@0 795 // names and lines in native (i.e., not SPS) profilers.
michael@0 796 if (!SendModuleToAttachedProfiler(cx, module))
michael@0 797 return false;
michael@0 798
michael@0 799 // Link-time validation succeeded, so wrap all the exported functions with
michael@0 800 // CallAsmJS builtins that trampoline into the generated code.
michael@0 801 JSObject *obj = CreateExportObject(cx, moduleObj);
michael@0 802 if (!obj)
michael@0 803 return false;
michael@0 804
michael@0 805 args.rval().set(ObjectValue(*obj));
michael@0 806 return true;
michael@0 807 }
michael@0 808
michael@0 809 JSFunction *
michael@0 810 js::NewAsmJSModuleFunction(ExclusiveContext *cx, JSFunction *origFun, HandleObject moduleObj)
michael@0 811 {
michael@0 812 RootedPropertyName name(cx, origFun->name());
michael@0 813
michael@0 814 JSFunction::Flags flags = origFun->isLambda() ? JSFunction::NATIVE_LAMBDA_FUN
michael@0 815 : JSFunction::NATIVE_FUN;
michael@0 816 JSFunction *moduleFun = NewFunction(cx, NullPtr(), LinkAsmJS, origFun->nargs(),
michael@0 817 flags, NullPtr(), name,
michael@0 818 JSFunction::ExtendedFinalizeKind, TenuredObject);
michael@0 819 if (!moduleFun)
michael@0 820 return nullptr;
michael@0 821
michael@0 822 moduleFun->setExtendedSlot(MODULE_FUN_SLOT, ObjectValue(*moduleObj));
michael@0 823 return moduleFun;
michael@0 824 }
michael@0 825
michael@0 826 bool
michael@0 827 js::IsAsmJSModuleNative(js::Native native)
michael@0 828 {
michael@0 829 return native == LinkAsmJS;
michael@0 830 }
michael@0 831
michael@0 832 static bool
michael@0 833 IsMaybeWrappedNativeFunction(const Value &v, Native native, JSFunction **fun = nullptr)
michael@0 834 {
michael@0 835 if (!v.isObject())
michael@0 836 return false;
michael@0 837
michael@0 838 JSObject *obj = CheckedUnwrap(&v.toObject());
michael@0 839 if (!obj)
michael@0 840 return false;
michael@0 841
michael@0 842 if (!obj->is<JSFunction>())
michael@0 843 return false;
michael@0 844
michael@0 845 if (fun)
michael@0 846 *fun = &obj->as<JSFunction>();
michael@0 847
michael@0 848 return obj->as<JSFunction>().maybeNative() == native;
michael@0 849 }
michael@0 850
michael@0 851 bool
michael@0 852 js::IsAsmJSModule(JSContext *cx, unsigned argc, Value *vp)
michael@0 853 {
michael@0 854 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 855 bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args.get(0), LinkAsmJS);
michael@0 856 args.rval().set(BooleanValue(rval));
michael@0 857 return true;
michael@0 858 }
michael@0 859
michael@0 860 bool
michael@0 861 js::IsAsmJSModule(HandleFunction fun)
michael@0 862 {
michael@0 863 return fun->isNative() && fun->maybeNative() == LinkAsmJS;
michael@0 864 }
michael@0 865
michael@0 866 JSString *
michael@0 867 js::AsmJSModuleToString(JSContext *cx, HandleFunction fun, bool addParenToLambda)
michael@0 868 {
michael@0 869 AsmJSModule &module = ModuleFunctionToModuleObject(fun).module();
michael@0 870
michael@0 871 uint32_t begin = module.funcStart();
michael@0 872 uint32_t end = module.funcEndAfterCurly();
michael@0 873 ScriptSource *source = module.scriptSource();
michael@0 874 StringBuffer out(cx);
michael@0 875
michael@0 876 // Whether the function has been created with a Function ctor
michael@0 877 bool funCtor = begin == 0 && end == source->length() && source->argumentsNotIncluded();
michael@0 878
michael@0 879 if (addParenToLambda && fun->isLambda() && !out.append("("))
michael@0 880 return nullptr;
michael@0 881
michael@0 882 if (!out.append("function "))
michael@0 883 return nullptr;
michael@0 884
michael@0 885 if (fun->atom() && !out.append(fun->atom()))
michael@0 886 return nullptr;
michael@0 887
michael@0 888 if (funCtor) {
michael@0 889 // Functions created with the function constructor don't have arguments in their source.
michael@0 890 if (!out.append("("))
michael@0 891 return nullptr;
michael@0 892
michael@0 893 if (PropertyName *argName = module.globalArgumentName()) {
michael@0 894 if (!out.append(argName->chars(), argName->length()))
michael@0 895 return nullptr;
michael@0 896 }
michael@0 897 if (PropertyName *argName = module.importArgumentName()) {
michael@0 898 if (!out.append(", ") || !out.append(argName->chars(), argName->length()))
michael@0 899 return nullptr;
michael@0 900 }
michael@0 901 if (PropertyName *argName = module.bufferArgumentName()) {
michael@0 902 if (!out.append(", ") || !out.append(argName->chars(), argName->length()))
michael@0 903 return nullptr;
michael@0 904 }
michael@0 905
michael@0 906 if (!out.append(") {\n"))
michael@0 907 return nullptr;
michael@0 908 }
michael@0 909
michael@0 910 Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
michael@0 911 if (!src)
michael@0 912 return nullptr;
michael@0 913
michael@0 914 if (module.strict()) {
michael@0 915 // We need to add "use strict" in the body right after the opening
michael@0 916 // brace.
michael@0 917 size_t bodyStart = 0, bodyEnd;
michael@0 918
michael@0 919 // No need to test for functions created with the Function ctor as
michael@0 920 // these doesn't implicitly inherit the "use strict" context. Strict mode is
michael@0 921 // enabled for functions created with the Function ctor only if they begin with
michael@0 922 // the "use strict" directive, but these functions won't validate as asm.js
michael@0 923 // modules.
michael@0 924
michael@0 925 ConstTwoByteChars chars(src->chars(), src->length());
michael@0 926 if (!FindBody(cx, fun, chars, src->length(), &bodyStart, &bodyEnd))
michael@0 927 return nullptr;
michael@0 928
michael@0 929 if (!out.append(chars, bodyStart) ||
michael@0 930 !out.append("\n\"use strict\";\n") ||
michael@0 931 !out.append(chars + bodyStart, src->length() - bodyStart))
michael@0 932 {
michael@0 933 return nullptr;
michael@0 934 }
michael@0 935 } else {
michael@0 936 if (!out.append(src->chars(), src->length()))
michael@0 937 return nullptr;
michael@0 938 }
michael@0 939
michael@0 940 if (funCtor && !out.append("\n}"))
michael@0 941 return nullptr;
michael@0 942
michael@0 943 if (addParenToLambda && fun->isLambda() && !out.append(")"))
michael@0 944 return nullptr;
michael@0 945
michael@0 946 return out.finishString();
michael@0 947 }
michael@0 948
michael@0 949 bool
michael@0 950 js::IsAsmJSModuleLoadedFromCache(JSContext *cx, unsigned argc, Value *vp)
michael@0 951 {
michael@0 952 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 953
michael@0 954 JSFunction *fun;
michael@0 955 if (!args.hasDefined(0) || !IsMaybeWrappedNativeFunction(args[0], LinkAsmJS, &fun)) {
michael@0 956 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_USE_ASM_TYPE_FAIL,
michael@0 957 "argument passed to isAsmJSModuleLoadedFromCache is not a "
michael@0 958 "validated asm.js module");
michael@0 959 return false;
michael@0 960 }
michael@0 961
michael@0 962 bool loadedFromCache = ModuleFunctionToModuleObject(fun).module().loadedFromCache();
michael@0 963
michael@0 964 args.rval().set(BooleanValue(loadedFromCache));
michael@0 965 return true;
michael@0 966 }
michael@0 967
michael@0 968 bool
michael@0 969 js::IsAsmJSFunction(JSContext *cx, unsigned argc, Value *vp)
michael@0 970 {
michael@0 971 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 972 bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args[0], CallAsmJS);
michael@0 973 args.rval().set(BooleanValue(rval));
michael@0 974 return true;
michael@0 975 }
michael@0 976
michael@0 977 bool
michael@0 978 js::IsAsmJSFunction(HandleFunction fun)
michael@0 979 {
michael@0 980 return fun->isNative() && fun->maybeNative() == CallAsmJS;
michael@0 981 }
michael@0 982
michael@0 983 JSString *
michael@0 984 js::AsmJSFunctionToString(JSContext *cx, HandleFunction fun)
michael@0 985 {
michael@0 986 AsmJSModule &module = FunctionToEnclosingModule(fun);
michael@0 987 const AsmJSModule::ExportedFunction &f = FunctionToExportedFunction(fun, module);
michael@0 988 uint32_t begin = module.funcStart() + f.startOffsetInModule();
michael@0 989 uint32_t end = module.funcStart() + f.endOffsetInModule();
michael@0 990
michael@0 991 ScriptSource *source = module.scriptSource();
michael@0 992 StringBuffer out(cx);
michael@0 993
michael@0 994 // asm.js functions cannot have been created with a Function constructor
michael@0 995 // as they belong within a module.
michael@0 996 JS_ASSERT(!(begin == 0 && end == source->length() && source->argumentsNotIncluded()));
michael@0 997
michael@0 998 if (!out.append("function "))
michael@0 999 return nullptr;
michael@0 1000
michael@0 1001 Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
michael@0 1002 if (!src)
michael@0 1003 return nullptr;
michael@0 1004
michael@0 1005 if (!out.append(src->chars(), src->length()))
michael@0 1006 return nullptr;
michael@0 1007
michael@0 1008 return out.finishString();
michael@0 1009 }

mercurial