Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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 | } |