ScriptSourceObject * frontend::CreateScriptSourceObject(ExclusiveContext *cx, const ReadOnlyCompileOptions &options) { ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return nullptr; ScriptSourceHolder ssHolder(ss); if (!ss->initFromOptions(cx, options)) return nullptr; RootedScriptSource sso(cx, ScriptSourceObject::create(cx, ss)); if (!sso) return nullptr; // Off-thread compilations do all their GC heap allocation, including the // SSO, in a temporary compartment. Hence, for the SSO to refer to the // gc-heap-allocated values in |options|, it would need cross-compartment // wrappers from the temporary compartment to the real compartment --- which // would then be inappropriate once we merged the temporary and real // compartments. // // Instead, we put off populating those SSO slots in off-thread compilations // until after we've merged compartments. if (cx->isJSContext()) { if (!ScriptSourceObject::initFromOptions(cx->asJSContext(), sso, options)) return nullptr; } return sso; }
/////////////////////////////////////////////////////////////////////////////// // cleanup block void PreprocessorBlock::Cleanup () { vector<ScriptSource *>::iterator it = m_inc_sources.begin(), it_end = m_inc_sources.end(); for (; it != it_end; it++) { ScriptSource *pIncludeSource = *it; pIncludeSource->Release (); } m_inc_sources.clear (); }
ScriptSourceObject * frontend::CreateScriptSourceObject(ExclusiveContext *cx, const ReadOnlyCompileOptions &options) { ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return nullptr; ScriptSourceHolder ssHolder(ss); if (!ss->initFromOptions(cx, options)) return nullptr; return ScriptSourceObject::create(cx, ss, options); }
size_t PreprocessorBlock::GetExpandedLength () const { vector<ScriptSource *>::const_iterator it = m_inc_sources.begin(), it_end = m_inc_sources.end(); size_t size = 0; for (; it != it_end; it++) { ScriptSource *pIncludeSource = *it; size+= pIncludeSource->GetLength (); } return size; }
JSString * js::AsmJSFunctionToString(JSContext *cx, HandleFunction fun) { AsmJSModule &module = FunctionToEnclosingModule(fun); const AsmJSModule::ExportedFunction &f = FunctionToExportedFunction(fun, module); uint32_t begin = module.srcStart() + f.startOffsetInModule(); uint32_t end = module.srcStart() + f.endOffsetInModule(); ScriptSource *source = module.scriptSource(); StringBuffer out(cx); // asm.js functions cannot have been created with a Function constructor // as they belong within a module. JS_ASSERT(!(begin == 0 && end == source->length() && source->argumentsNotIncluded())); if (!out.append("function ")) return nullptr; if (module.strict()) { // AppendUseStrictSource expects its input to start right after the // function name, so split the source chars from the src into two parts: // the function name and the rest (arguments + body). // asm.js functions can't be anonymous JS_ASSERT(fun->atom()); if (!out.append(fun->atom())) return nullptr; size_t nameEnd = begin + fun->atom()->length(); Rooted<JSFlatString*> src(cx, source->substring(cx, nameEnd, end)); if (!AppendUseStrictSource(cx, fun, src, out)) return nullptr; } else { Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end)); if (!src) return nullptr; if (!out.append(src)) return nullptr; } return out.finishString(); }
int PreprocessorBlock::ExpandBlock (sys_wchar **p_pCodeBuffer) { sys_wchar *pCodeBuffer = *p_pCodeBuffer; vector<ScriptSource *>::const_iterator it = m_inc_sources.begin(), it_end = m_inc_sources.end(); size_t size = 0; for (; it != it_end; it++) { ScriptSource *pIncludeSource = *it; size_t codeLength = pIncludeSource->GetLength(); memcpy (pCodeBuffer, pIncludeSource->GetCode(), codeLength * sizeof(sys_wchar)); pCodeBuffer+= codeLength; } *p_pCodeBuffer = pCodeBuffer; return kPrepErrorNone; }
void JSRuntime::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, RuntimeSizes *runtime) { runtime->object = mallocSizeOf(this); runtime->atomsTable = atomState.atoms.sizeOfExcludingThis(mallocSizeOf); runtime->contexts = 0; for (ContextIter acx(this); !acx.done(); acx.next()) runtime->contexts += acx->sizeOfIncludingThis(mallocSizeOf); runtime->dtoa = mallocSizeOf(dtoaState); runtime->temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf); if (execAlloc_) execAlloc_->sizeOfCode(&runtime->mjitCode, &runtime->regexpCode, &runtime->unusedCodeMemory); else runtime->mjitCode = runtime->regexpCode = runtime->unusedCodeMemory = 0; runtime->stackCommitted = stackSpace.sizeOfCommitted(); runtime->gcMarker = gcMarker.sizeOfExcludingThis(mallocSizeOf); runtime->mathCache = mathCache_ ? mathCache_->sizeOfIncludingThis(mallocSizeOf) : 0; runtime->scriptFilenames = scriptFilenameTable.sizeOfExcludingThis(mallocSizeOf); for (ScriptFilenameTable::Range r = scriptFilenameTable.all(); !r.empty(); r.popFront()) runtime->scriptFilenames += mallocSizeOf(r.front()); runtime->scriptSources = 0; for (ScriptSource *n = scriptSources; n; n = n->next) runtime->scriptSources += n->sizeOfIncludingThis(mallocSizeOf); runtime->compartmentObjects = 0; CallbackData data(mallocSizeOf); JS_IterateCompartments(this, &data, CompartmentCallback); runtime->compartmentObjects = data.n; }
bool MainWnd::RunScript () { // reset vars m_scriptLoaded = FALSE; m_windowTitle = _T(""); // reset the GL view and redraw the blank scene m_pGLView->ResetView (); m_pGLView->RedrawView (); ScriptSource *pScriptSource; int ret; // preprocess the script source sys_tchar inc_path[MAX_PATH], base_inc_path[MAX_PATH]; GetModuleFileName ( (HMODULE)_hAppInstance, inc_path, sizeof(inc_path)/sizeof(inc_path[0]) ); sysutils_path_get_base (inc_path, base_inc_path, sizeof(base_inc_path)/sizeof(base_inc_path[0]) ); _tcscat (base_inc_path, _tx("include\\")); vector<tstring> search_paths; search_paths.push_back (base_inc_path); pScriptSource = ScriptPreprocessor::PreprocessScript (m_scriptPath.c_str(), NULL, search_paths, ret); if (ret != kPrepErrorNone) { const sys_tchar *error_msg = ScriptPreprocessor::TranslateErrorCode (ret); ::MessageBox (GetHandle(), error_msg, GLSRUN_APP_TITLE, MB_ICONERROR); // preprocessing failed! return false; } // Load the preprocessed script source using the script host managed by the script host driver m_scriptLoaded = m_pScriptHostDriver->GetScriptHost()->SetScriptSource (pScriptSource); m_paused = false; pScriptSource->Release (); UpdateUI (); return m_scriptLoaded ? true : false; }
~AutoAttachToRuntime() { // This makes the source visible to the GC. If compilation fails, and no // script refers to it, it will be collected. if (ss) ss->attachToRuntime(rt); }
JSObject * GlobalObject::initFunctionAndObjectClasses(JSContext *cx) { Rooted<GlobalObject*> self(cx, this); JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); JS_ASSERT(isNative()); cx->setDefaultCompartmentObjectIfUnset(self); RootedObject objectProto(cx); /* * Create |Object.prototype| first, mirroring CreateBlankProto but for the * prototype of the created object. */ objectProto = NewObjectWithGivenProto(cx, &ObjectClass, NULL, self, SingletonObject); if (!objectProto) return NULL; /* * The default 'new' type of Object.prototype is required by type inference * to have unknown properties, to simplify handling of e.g. heterogenous * objects in JSON and script literals. */ if (!setNewTypeUnknown(cx, &ObjectClass, objectProto)) return NULL; /* Create |Function.prototype| next so we can create other functions. */ RootedFunction functionProto(cx); { RawObject functionProto_ = NewObjectWithGivenProto(cx, &FunctionClass, objectProto, self, SingletonObject); if (!functionProto_) return NULL; functionProto = functionProto_->toFunction(); /* * Bizarrely, |Function.prototype| must be an interpreted function, so * give it the guts to be one. */ { RawObject proto = NewFunction(cx, functionProto, NULL, 0, JSFunction::INTERPRETED, self, NullPtr()); if (!proto) return NULL; JS_ASSERT(proto == functionProto); functionProto->setIsFunctionPrototype(); } const char *rawSource = "() {\n}"; size_t sourceLen = strlen(rawSource); jschar *source = InflateString(cx, rawSource, &sourceLen); if (!source) return NULL; ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) { js_free(source); return NULL; } ScriptSourceHolder ssh(ss); ss->setSource(source, sourceLen); CompileOptions options(cx); options.setNoScriptRval(true) .setVersion(JSVERSION_DEFAULT); RootedScript script(cx, JSScript::Create(cx, /* enclosingScope = */ NullPtr(), /* savedCallerFun = */ false, options, /* staticLevel = */ 0, ss, 0, ss->length())); if (!script || !JSScript::fullyInitTrivial(cx, script)) return NULL; functionProto->initScript(script); types::TypeObject* protoType = functionProto->getType(cx); if (!protoType) return NULL; protoType->interpretedFunction = functionProto; script->setFunction(functionProto); /* * The default 'new' type of Function.prototype is required by type * inference to have unknown properties, to simplify handling of e.g. * CloneFunctionObject. */ if (!setNewTypeUnknown(cx, &FunctionClass, functionProto)) return NULL; } /* Create the Object function now that we have a [[Prototype]] for it. */ RootedFunction objectCtor(cx); { RootedObject ctor(cx, NewObjectWithGivenProto(cx, &FunctionClass, functionProto, self, SingletonObject)); if (!ctor) return NULL; RootedAtom objectAtom(cx, cx->names().Object); objectCtor = NewFunction(cx, ctor, obj_construct, 1, JSFunction::NATIVE_CTOR, self, objectAtom); if (!objectCtor) return NULL; } /* * Install |Object| and |Object.prototype| for the benefit of subsequent * code that looks for them. */ self->setObjectClassDetails(objectCtor, objectProto); /* Create |Function| so it and |Function.prototype| can be installed. */ RootedFunction functionCtor(cx); { // Note that ctor is rooted purely for the JS_ASSERT at the end RootedObject ctor(cx, NewObjectWithGivenProto(cx, &FunctionClass, functionProto, self, SingletonObject)); if (!ctor) return NULL; RootedAtom functionAtom(cx, cx->names().Function); functionCtor = NewFunction(cx, ctor, Function, 1, JSFunction::NATIVE_CTOR, self, functionAtom); if (!functionCtor) return NULL; JS_ASSERT(ctor == functionCtor); } /* * Install |Function| and |Function.prototype| so that we can freely create * functions and objects without special effort. */ self->setFunctionClassDetails(functionCtor, functionProto); /* * The hard part's done: now go back and add all the properties these * primordial values have. */ if (!LinkConstructorAndPrototype(cx, objectCtor, objectProto) || !DefinePropertiesAndBrand(cx, objectProto, NULL, object_methods)) { return NULL; } /* * Add an Object.prototype.__proto__ accessor property to implement that * extension (if it's actually enabled). Cache the getter for this * function so that cross-compartment [[Prototype]]-getting is implemented * in one place. */ RootedFunction getter(cx, NewFunction(cx, NullPtr(), ProtoGetter, 0, JSFunction::NATIVE_FUN, self, NullPtr())); if (!getter) return NULL; #if JS_HAS_OBJ_PROTO_PROP RootedFunction setter(cx, NewFunction(cx, NullPtr(), ProtoSetter, 0, JSFunction::NATIVE_FUN, self, NullPtr())); if (!setter) return NULL; RootedValue undefinedValue(cx, UndefinedValue()); if (!JSObject::defineProperty(cx, objectProto, cx->names().proto, undefinedValue, JS_DATA_TO_FUNC_PTR(PropertyOp, getter.get()), JS_DATA_TO_FUNC_PTR(StrictPropertyOp, setter.get()), JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED)) { return NULL; } #endif /* JS_HAS_OBJ_PROTO_PROP */ self->setProtoGetter(getter); if (!DefinePropertiesAndBrand(cx, objectCtor, NULL, object_static_methods) || !LinkConstructorAndPrototype(cx, functionCtor, functionProto) || !DefinePropertiesAndBrand(cx, functionProto, NULL, function_methods) || !DefinePropertiesAndBrand(cx, functionCtor, NULL, NULL)) { return NULL; } /* Add the global Function and Object properties now. */ if (!self->addDataProperty(cx, cx->names().Object, JSProto_Object + JSProto_LIMIT * 2, 0)) return NULL; if (!self->addDataProperty(cx, cx->names().Function, JSProto_Function + JSProto_LIMIT * 2, 0)) return NULL; /* Heavy lifting done, but lingering tasks remain. */ /* ES5 15.1.2.1. */ RootedId evalId(cx, NameToId(cx->names().eval)); RawObject evalobj = DefineFunction(cx, self, evalId, IndirectEval, 1, JSFUN_STUB_GSOPS); if (!evalobj) return NULL; self->setOriginalEval(evalobj); /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */ RootedFunction throwTypeError(cx, NewFunction(cx, NullPtr(), ThrowTypeError, 0, JSFunction::NATIVE_FUN, self, NullPtr())); if (!throwTypeError) return NULL; if (!JSObject::preventExtensions(cx, throwTypeError)) return NULL; self->setThrowTypeError(throwTypeError); RootedObject intrinsicsHolder(cx); if (cx->runtime->isSelfHostingGlobal(self)) { intrinsicsHolder = self; } else { intrinsicsHolder = NewObjectWithClassProto(cx, &ObjectClass, NULL, self, TenuredObject); if (!intrinsicsHolder) return NULL; } self->setIntrinsicsHolder(intrinsicsHolder); /* Define a property 'global' with the current global as its value. */ RootedValue global(cx, ObjectValue(*self)); if (!JSObject::defineProperty(cx, intrinsicsHolder, cx->names().global, global, JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY)) { return NULL; } /* * The global object should have |Object.prototype| as its [[Prototype]]. * Eventually we'd like to have standard classes be there from the start, * and thus we would know we were always setting what had previously been a * null [[Prototype]], but right now some code assumes it can set the * [[Prototype]] before standard classes have been initialized. For now, * only set the [[Prototype]] if it hasn't already been set. */ Rooted<TaggedProto> tagged(cx, TaggedProto(objectProto)); if (self->shouldSplicePrototype(cx) && !self->splicePrototype(cx, self->getClass(), tagged)) return NULL; /* * Notify any debuggers about the creation of the script for * |Function.prototype| -- after all initialization, for simplicity. */ RootedScript functionProtoScript(cx, functionProto->nonLazyScript()); CallNewScriptHook(cx, functionProtoScript, functionProto); return functionProto; }
JSScript * frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject scopeChain, HandleScript evalCaller, const CompileOptions &options, const jschar *chars, size_t length, JSString *source_ /* = NULL */, unsigned staticLevel /* = 0 */, SourceCompressionToken *extraSct /* = NULL */) { RootedString source(cx, source_); SkipRoot skip(cx, &chars); if (cx->isJSContext()) MaybeCallSourceHandler(cx->asJSContext(), options, chars, length); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(evalCaller, options.compileAndGo); JS_ASSERT_IF(evalCaller, options.forEval); JS_ASSERT_IF(staticLevel != 0, evalCaller); if (!CheckLength(cx, length)) return NULL; JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE); ScriptSource *ss = cx->new_<ScriptSource>(options.originPrincipals()); if (!ss) return NULL; if (options.filename && !ss->setFilename(cx, options.filename)) return NULL; JS::RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss)); if (!sourceObject) return NULL; // Saving source is not yet supported when parsing off thread. JS_ASSERT_IF(!cx->isJSContext(), !extraSct && options.sourcePolicy == CompileOptions::NO_SOURCE); SourceCompressionToken *sct = extraSct; Maybe<SourceCompressionToken> mysct; if (cx->isJSContext() && !sct) { mysct.construct(cx->asJSContext()); sct = mysct.addr(); } switch (options.sourcePolicy) { case CompileOptions::SAVE_SOURCE: if (!ss->setSourceCopy(cx->asJSContext(), chars, length, false, sct)) return NULL; break; case CompileOptions::LAZY_SOURCE: ss->setSourceRetrievable(); break; case CompileOptions::NO_SOURCE: break; } bool canLazilyParse = CanLazilyParse(cx, options); Maybe<Parser<SyntaxParseHandler> > syntaxParser; if (canLazilyParse) { syntaxParser.construct(cx, alloc, options, chars, length, /* foldConstants = */ false, (Parser<SyntaxParseHandler> *) NULL, (LazyScript *) NULL); } Parser<FullParseHandler> parser(cx, alloc, options, chars, length, /* foldConstants = */ true, canLazilyParse ? &syntaxParser.ref() : NULL, NULL); parser.sct = sct; parser.ss = ss; Directives directives(options.strictOption); GlobalSharedContext globalsc(cx, scopeChain, directives, options.extraWarningsOption); bool savedCallerFun = options.compileAndGo && evalCaller && (evalCaller->function() || evalCaller->savedCallerFun); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, sourceObject, 0, length)); if (!script) return NULL; // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). InternalHandle<Bindings*> bindings(script, &script->bindings); if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, NULL)) return NULL; // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL; JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter::EmitterMode emitterMode = options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal; BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, options.forEval, evalCaller, !!globalScope, options.lineno, emitterMode); if (!bce.init()) return NULL; // Syntax parsing may cause us to restart processing of top level // statements in the script. Use Maybe<> so that the parse context can be // reset when this occurs. Maybe<ParseContext<FullParseHandler> > pc; pc.construct(&parser, (GenericParseContext *) NULL, (ParseNode *) NULL, &globalsc, (Directives *) NULL, staticLevel, /* bodyid = */ 0); if (!pc.ref().init()) return NULL; /* If this is a direct call to eval, inherit the caller's strictness. */ if (evalCaller && evalCaller->strict) globalsc.strict = true; if (options.compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = AtomizeString<CanGC>(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return NULL; } if (evalCaller && evalCaller->functionOrCallerFunction()) { /* * An eval script in a caller frame needs to have its enclosing * function captured in case it refers to an upvar, and someone * wishes to decompile it while it's running. */ JSFunction *fun = evalCaller->functionOrCallerFunction(); Directives directives(/* strict = */ fun->strict()); ObjectBox *funbox = parser.newFunctionBox(/* fn = */ NULL, fun, pc.addr(), directives); if (!funbox) return NULL; bce.objectList.add(funbox); } } bool canHaveDirectives = true; for (;;) { TokenKind tt = parser.tokenStream.peekToken(TokenStream::Operand); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return NULL; } TokenStream::Position pos(parser.keepAtoms); parser.tokenStream.tell(&pos); ParseNode *pn = parser.statement(canHaveDirectives); if (!pn) { if (parser.hadAbortedSyntaxParse()) { // Parsing inner functions lazily may lead the parser into an // unrecoverable state and may require starting over on the top // level statement. Restart the parse; syntax parsing has // already been disabled for the parser and the result will not // be ambiguous. parser.clearAbortedSyntaxParse(); parser.tokenStream.seek(pos); // Destroying the parse context will destroy its free // variables, so check if any deoptimization is needed. if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref())) return NULL; pc.destroy(); pc.construct(&parser, (GenericParseContext *) NULL, (ParseNode *) NULL, &globalsc, (Directives *) NULL, staticLevel, /* bodyid = */ 0); if (!pc.ref().init()) return NULL; JS_ASSERT(parser.pc == pc.addr()); pn = parser.statement(); } if (!pn) { JS_ASSERT(!parser.hadAbortedSyntaxParse()); return NULL; } } if (canHaveDirectives) { if (!parser.maybeParseDirective(/* stmtList = */ NULL, pn, &canHaveDirectives)) return NULL; } if (!FoldConstants(cx, &pn, &parser)) return NULL; // Inferring names for functions in compiled scripts is currently only // supported while on the main thread. See bug 895395. if (cx->isJSContext() && !NameFunctions(cx->asJSContext(), pn)) return NULL; if (!EmitTree(cx, &bce, pn)) return NULL; parser.handler.freeTree(pn); } if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref())) return NULL; if (!SetSourceMap(cx, parser.tokenStream, ss)) return NULL; /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) return NULL; if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return NULL; bce.tellDebuggerAboutCompiledScript(cx); if (sct && !extraSct && !sct->complete()) return NULL; return script; }
static void StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind, size_t thingSize) { IteratorClosure *closure = static_cast<IteratorClosure *>(data); RuntimeStats *rtStats = closure->rtStats; CompartmentStats *cStats = rtStats->currCompartmentStats; switch (traceKind) { case JSTRACE_OBJECT: { JSObject *obj = static_cast<JSObject *>(thing); if (obj->isFunction()) { cStats->gcHeapObjectsFunction += thingSize; } else if (obj->isDenseArray()) { cStats->gcHeapObjectsDenseArray += thingSize; } else if (obj->isSlowArray()) { cStats->gcHeapObjectsSlowArray += thingSize; } else if (obj->isCrossCompartmentWrapper()) { cStats->gcHeapObjectsCrossCompartmentWrapper += thingSize; } else { cStats->gcHeapObjectsOrdinary += thingSize; } size_t slotsSize, elementsSize, argumentsDataSize, regExpStaticsSize, propertyIteratorDataSize; obj->sizeOfExcludingThis(rtStats->mallocSizeOf, &slotsSize, &elementsSize, &argumentsDataSize, ®ExpStaticsSize, &propertyIteratorDataSize); cStats->objectsExtraSlots += slotsSize; cStats->objectsExtraElements += elementsSize; cStats->objectsExtraArgumentsData += argumentsDataSize; cStats->objectsExtraRegExpStatics += regExpStaticsSize; cStats->objectsExtraPropertyIteratorData += propertyIteratorDataSize; if (ObjectPrivateVisitor *opv = closure->opv) { js::Class *clazz = js::GetObjectClass(obj); if (clazz->flags & JSCLASS_HAS_PRIVATE && clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { cStats->objectsExtraPrivate += opv->sizeOfIncludingThis(GetObjectPrivate(obj)); } } break; } case JSTRACE_STRING: { JSString *str = static_cast<JSString *>(thing); size_t strSize = str->sizeOfExcludingThis(rtStats->mallocSizeOf); // If we can't grow hugeStrings, let's just call this string non-huge. // We're probably about to OOM anyway. if (strSize >= HugeStringInfo::MinSize() && cStats->hugeStrings.growBy(1)) { cStats->gcHeapStringsNormal += thingSize; HugeStringInfo &info = cStats->hugeStrings.back(); info.length = str->length(); info.size = strSize; PutEscapedString(info.buffer, sizeof(info.buffer), &str->asLinear(), 0); } else if (str->isShort()) { MOZ_ASSERT(strSize == 0); cStats->gcHeapStringsShort += thingSize; } else { cStats->gcHeapStringsNormal += thingSize; cStats->stringCharsNonHuge += strSize; } break; } case JSTRACE_SHAPE: { Shape *shape = static_cast<Shape*>(thing); size_t propTableSize, kidsSize; shape->sizeOfExcludingThis(rtStats->mallocSizeOf, &propTableSize, &kidsSize); if (shape->inDictionary()) { cStats->gcHeapShapesDict += thingSize; cStats->shapesExtraDictTables += propTableSize; JS_ASSERT(kidsSize == 0); } else { if (shape->base()->getObjectParent() == shape->compartment()->maybeGlobal()) { cStats->gcHeapShapesTreeGlobalParented += thingSize; } else { cStats->gcHeapShapesTreeNonGlobalParented += thingSize; } cStats->shapesExtraTreeTables += propTableSize; cStats->shapesExtraTreeShapeKids += kidsSize; } break; } case JSTRACE_BASE_SHAPE: { cStats->gcHeapShapesBase += thingSize; break; } case JSTRACE_SCRIPT: { JSScript *script = static_cast<JSScript *>(thing); cStats->gcHeapScripts += thingSize; cStats->scriptData += script->sizeOfData(rtStats->mallocSizeOf); #ifdef JS_METHODJIT cStats->jaegerData += script->sizeOfJitScripts(rtStats->mallocSizeOf); # ifdef JS_ION cStats->ionData += ion::MemoryUsed(script, rtStats->mallocSizeOf); # endif #endif ScriptSource *ss = script->scriptSource(); SourceSet::AddPtr entry = closure->seenSources.lookupForAdd(ss); if (!entry) { closure->seenSources.add(entry, ss); // Not much to be done on failure. rtStats->runtime.scriptSources += ss->sizeOfIncludingThis(rtStats->mallocSizeOf); } break; } case JSTRACE_IONCODE: { #ifdef JS_METHODJIT # ifdef JS_ION cStats->gcHeapIonCodes += thingSize; // The code for a script is counted in ExecutableAllocator::sizeOfCode(). # endif #endif break; } case JSTRACE_TYPE_OBJECT: { types::TypeObject *obj = static_cast<types::TypeObject *>(thing); cStats->gcHeapTypeObjects += thingSize; obj->sizeOfExcludingThis(&cStats->typeInferenceSizes, rtStats->mallocSizeOf); break; } #if JS_HAS_XML_SUPPORT case JSTRACE_XML: { cStats->gcHeapXML += thingSize; break; } #endif } // Yes, this is a subtraction: see StatsArenaCallback() for details. cStats->gcHeapUnusedGcThings -= thingSize; }
// Compile a JS function body, which might appear as the value of an event // handler attribute in an HTML <INPUT> tag, or in a Function() constructor. static bool CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, const ReadOnlyCompileOptions &options, const AutoNameVector &formals, SourceBufferHolder &srcBuf, HandleObject enclosingScope, GeneratorKind generatorKind) { js::TraceLogger *logger = js::TraceLoggerForMainThread(cx->runtime()); uint32_t logId = js::TraceLogCreateTextId(logger, options); js::AutoTraceLog scriptLogger(logger, logId); js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileFunction); // FIXME: make Function pass in two strings and parse them as arguments and // ProgramElements respectively. if (!CheckLength(cx, srcBuf)) return false; RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options)); if (!sourceObject) return false; ScriptSource *ss = sourceObject->source(); SourceCompressionTask sct(cx); MOZ_ASSERT(!options.sourceIsLazy); if (!cx->compartment()->options().discardSource()) { if (!ss->setSourceCopy(cx, srcBuf, true, &sct)) return false; } bool canLazilyParse = CanLazilyParse(cx, options); Maybe<Parser<SyntaxParseHandler> > syntaxParser; if (canLazilyParse) { syntaxParser.emplace(cx, &cx->tempLifoAlloc(), options, srcBuf.get(), srcBuf.length(), /* foldConstants = */ false, (Parser<SyntaxParseHandler> *) nullptr, (LazyScript *) nullptr); } MOZ_ASSERT(!options.forEval); Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, srcBuf.get(), srcBuf.length(), /* foldConstants = */ true, canLazilyParse ? syntaxParser.ptr() : nullptr, nullptr); parser.sct = &sct; parser.ss = ss; MOZ_ASSERT(fun); MOZ_ASSERT(fun->isTenured()); fun->setArgCount(formals.length()); // Speculatively parse using the default directives implied by the context. // If a directive is encountered (e.g., "use strict") that changes how the // function should have been parsed, we backup and reparse with the new set // of directives. Directives directives(options.strictOption); TokenStream::Position start(parser.keepAtoms); parser.tokenStream.tell(&start); ParseNode *fn; while (true) { Directives newDirectives = directives; fn = parser.standaloneFunctionBody(fun, formals, generatorKind, directives, &newDirectives); if (fn) break; if (parser.hadAbortedSyntaxParse()) { // Hit some unrecoverable ambiguity during an inner syntax parse. // Syntax parsing has now been disabled in the parser, so retry // the parse. parser.clearAbortedSyntaxParse(); } else { if (parser.tokenStream.hadError() || directives == newDirectives) return false; // Assignment must be monotonic to prevent reparsing iloops MOZ_ASSERT_IF(directives.strict(), newDirectives.strict()); MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS()); directives = newDirectives; } parser.tokenStream.seek(start); } if (!NameFunctions(cx, fn)) return false; if (!SetDisplayURL(cx, parser.tokenStream, ss)) return false; if (!SetSourceMap(cx, parser.tokenStream, ss)) return false; if (fn->pn_funbox->function()->isInterpreted()) { MOZ_ASSERT(fun == fn->pn_funbox->function()); Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false, options, /* staticLevel = */ 0, sourceObject, /* sourceStart = */ 0, srcBuf.length())); if (!script) return false; script->bindings = fn->pn_funbox->bindings; /* * The reason for checking fun->environment() below is that certain * consumers of JS::CompileFunction, namely * EventListenerManager::CompileEventHandlerInternal, passes in a * nullptr environment. This compiled function is never used, but * instead is cloned immediately onto the right scope chain. */ BytecodeEmitter funbce(/* parent = */ nullptr, &parser, fn->pn_funbox, script, /* lazyScript = */ js::NullPtr(), /* insideEval = */ false, /* evalCaller = */ js::NullPtr(), fun->environment() && fun->environment()->is<GlobalObject>(), options.lineno); if (!funbce.init()) return false; if (!EmitFunctionScript(cx, &funbce, fn->pn_body)) return false; } else { fun.set(fn->pn_funbox->function()); MOZ_ASSERT(IsAsmJSModuleNative(fun->native())); } if (!sct.complete()) return false; return true; }
JSScript* frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject scopeChain, HandleScript evalCaller, Handle<StaticEvalObject*> evalStaticScope, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, JSString* source_ /* = nullptr */, unsigned staticLevel /* = 0 */, SourceCompressionTask* extraSct /* = nullptr */) { MOZ_ASSERT(srcBuf.get()); RootedString source(cx, source_); js::TraceLoggerThread* logger = nullptr; if (cx->isJSContext()) logger = TraceLoggerForMainThread(cx->asJSContext()->runtime()); else logger = TraceLoggerForCurrentThread(); js::TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, options); js::AutoTraceLog scriptLogger(logger, event); js::AutoTraceLog typeLogger(logger, TraceLogger_ParserCompileScript); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ MOZ_ASSERT_IF(evalCaller, options.compileAndGo); MOZ_ASSERT_IF(evalCaller, options.forEval); MOZ_ASSERT_IF(evalCaller && evalCaller->strict(), options.strictOption); MOZ_ASSERT_IF(staticLevel != 0, evalCaller); if (!CheckLength(cx, srcBuf)) return nullptr; MOZ_ASSERT_IF(staticLevel != 0, !options.sourceIsLazy); RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options)); if (!sourceObject) return nullptr; ScriptSource* ss = sourceObject->source(); SourceCompressionTask mysct(cx); SourceCompressionTask* sct = extraSct ? extraSct : &mysct; if (!cx->compartment()->options().discardSource()) { if (options.sourceIsLazy) ss->setSourceRetrievable(); else if (!ss->setSourceCopy(cx, srcBuf, false, sct)) return nullptr; } bool canLazilyParse = CanLazilyParse(cx, options); Maybe<Parser<SyntaxParseHandler> > syntaxParser; if (canLazilyParse) { syntaxParser.emplace(cx, alloc, options, srcBuf.get(), srcBuf.length(), /* foldConstants = */ false, (Parser<SyntaxParseHandler>*) nullptr, (LazyScript*) nullptr); if (!syntaxParser->checkOptions()) return nullptr; } Parser<FullParseHandler> parser(cx, alloc, options, srcBuf.get(), srcBuf.length(), /* foldConstants = */ true, canLazilyParse ? syntaxParser.ptr() : nullptr, nullptr); parser.sct = sct; parser.ss = ss; if (!parser.checkOptions()) return nullptr; Directives directives(options.strictOption); GlobalSharedContext globalsc(cx, scopeChain, directives, options.extraWarningsOption); bool savedCallerFun = evalCaller && evalCaller->functionOrCallerFunction(); Rooted<JSScript*> script(cx, JSScript::Create(cx, evalStaticScope, savedCallerFun, options, staticLevel, sourceObject, 0, srcBuf.length())); if (!script) return nullptr; // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject* globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : nullptr; MOZ_ASSERT_IF(globalScope, globalScope->isNative()); MOZ_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter::EmitterMode emitterMode = options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal; BytecodeEmitter bce(/* parent = */ nullptr, &parser, &globalsc, script, /* lazyScript = */ js::NullPtr(), options.forEval, evalCaller, evalStaticScope, !!globalScope, options.lineno, emitterMode); if (!bce.init()) return nullptr; // Syntax parsing may cause us to restart processing of top level // statements in the script. Use Maybe<> so that the parse context can be // reset when this occurs. Maybe<ParseContext<FullParseHandler> > pc; pc.emplace(&parser, (GenericParseContext*) nullptr, (ParseNode*) nullptr, &globalsc, (Directives*) nullptr, staticLevel, /* bodyid = */ 0, /* blockScopeDepth = */ 0); if (!pc->init(parser.tokenStream)) return nullptr; if (savedCallerFun) { /* * An eval script in a caller frame needs to have its enclosing * function captured in case it refers to an upvar, and someone * wishes to decompile it while it's running. */ JSFunction* fun = evalCaller->functionOrCallerFunction(); MOZ_ASSERT_IF(fun->strict(), options.strictOption); Directives directives(/* strict = */ options.strictOption); ObjectBox* funbox = parser.newFunctionBox(/* fn = */ nullptr, fun, pc.ptr(), directives, fun->generatorKind()); if (!funbox) return nullptr; bce.objectList.add(funbox); } bool canHaveDirectives = true; for (;;) { TokenKind tt; if (!parser.tokenStream.peekToken(&tt, TokenStream::Operand)) return nullptr; if (tt == TOK_EOF) break; TokenStream::Position pos(parser.keepAtoms); parser.tokenStream.tell(&pos); ParseNode* pn = parser.statement(canHaveDirectives); if (!pn) { if (parser.hadAbortedSyntaxParse()) { // Parsing inner functions lazily may lead the parser into an // unrecoverable state and may require starting over on the top // level statement. Restart the parse; syntax parsing has // already been disabled for the parser and the result will not // be ambiguous. parser.clearAbortedSyntaxParse(); parser.tokenStream.seek(pos); // Destroying the parse context will destroy its free // variables, so check if any deoptimization is needed. if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, *pc)) return nullptr; pc.reset(); pc.emplace(&parser, (GenericParseContext*) nullptr, (ParseNode*) nullptr, &globalsc, (Directives*) nullptr, staticLevel, /* bodyid = */ 0, script->bindings.numBlockScoped()); if (!pc->init(parser.tokenStream)) return nullptr; MOZ_ASSERT(parser.pc == pc.ptr()); pn = parser.statement(); } if (!pn) { MOZ_ASSERT(!parser.hadAbortedSyntaxParse()); return nullptr; } } // Accumulate the maximum block scope depth, so that EmitTree can assert // when emitting JSOP_GETLOCAL that the local is indeed within the fixed // part of the stack frame. script->bindings.updateNumBlockScoped(pc->blockScopeDepth); if (canHaveDirectives) { if (!parser.maybeParseDirective(/* stmtList = */ nullptr, pn, &canHaveDirectives)) return nullptr; } if (!FoldConstants(cx, &pn, &parser)) return nullptr; if (!NameFunctions(cx, pn)) return nullptr; if (!bce.updateLocalsToFrameSlots()) return nullptr; if (!EmitTree(cx, &bce, pn)) return nullptr; parser.handler.freeTree(pn); } if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, *pc)) return nullptr; if (!SetDisplayURL(cx, parser.tokenStream, ss)) return nullptr; if (!SetSourceMap(cx, parser.tokenStream, ss)) return nullptr; /* * Source map URLs passed as a compile option (usually via a HTTP source map * header) override any source map urls passed as comment pragmas. */ if (options.sourceMapURL()) { // Warn about the replacement, but use the new one. if (ss->hasSourceMapURL()) { if(!parser.report(ParseWarning, false, nullptr, JSMSG_ALREADY_HAS_PRAGMA, ss->filename(), "//# sourceMappingURL")) return nullptr; } if (!ss->setSourceMapURL(cx, options.sourceMapURL())) return nullptr; } /* * Nowadays the threaded interpreter needs a last return instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_RETRVAL) < 0) return nullptr; // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). They may have block-scoped // locals, however, which are allocated to the fixed part of the stack // frame. InternalHandle<Bindings*> bindings(script, &script->bindings); if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, 0, pc->blockScopeDepth, 0, 0, nullptr)) { return nullptr; } if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return nullptr; // Note that this marking must happen before we tell Debugger // about the new script, in case Debugger delazifies the script's // inner functions. if (options.forEval) MarkFunctionsWithinEvalScript(script); bce.tellDebuggerAboutCompiledScript(cx); if (sct && !extraSct && !sct->complete()) return nullptr; MOZ_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->isExceptionPending()); return script; }
UnrootedScript frontend::CompileScript(JSContext *cx, HandleObject scopeChain, AbstractFramePtr callerFrame, const CompileOptions &options, StableCharPtr chars, size_t length, JSString *source_ /* = NULL */, unsigned staticLevel /* = 0 */) { RootedString source(cx, source_); class ProbesManager { const char* filename; unsigned lineno; public: ProbesManager(const char *f, unsigned l) : filename(f), lineno(l) { Probes::compileScriptBegin(filename, lineno); } ~ProbesManager() { Probes::compileScriptEnd(filename, lineno); } }; ProbesManager probesManager(options.filename, options.lineno); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, options.compileAndGo); JS_ASSERT_IF(staticLevel != 0, callerFrame); if (!CheckLength(cx, length)) return UnrootedScript(NULL); JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE); ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return UnrootedScript(NULL); ScriptSourceHolder ssh(ss); SourceCompressionToken sct(cx); switch (options.sourcePolicy) { case CompileOptions::SAVE_SOURCE: if (!ss->setSourceCopy(cx, chars, length, false, &sct)) return UnrootedScript(NULL); break; case CompileOptions::LAZY_SOURCE: ss->setSourceRetrievable(); break; case CompileOptions::NO_SOURCE: break; } Parser parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return UnrootedScript(NULL); parser.sct = &sct; GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx)); ParseContext pc(&parser, &globalsc, staticLevel, /* bodyid = */ 0); if (!pc.init()) return UnrootedScript(NULL); bool savedCallerFun = options.compileAndGo && callerFrame && callerFrame.isFunctionFrame(); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, ss, 0, length)); if (!script) return UnrootedScript(NULL); // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). InternalHandle<Bindings*> bindings(script, &script->bindings); if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, NULL)) return UnrootedScript(NULL); // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL; JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, callerFrame, !!globalScope, options.lineno, options.selfHostingMode); if (!bce.init()) return UnrootedScript(NULL); /* If this is a direct call to eval, inherit the caller's strictness. */ if (callerFrame && callerFrame.script()->strict) globalsc.strict = true; if (options.compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = AtomizeString<CanGC>(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return UnrootedScript(NULL); } if (callerFrame && callerFrame.isFunctionFrame()) { /* * An eval script in a caller frame needs to have its enclosing * function captured in case it refers to an upvar, and someone * wishes to decompile it while it's running. */ JSFunction *fun = callerFrame.fun(); ObjectBox *funbox = parser.newFunctionBox(fun, &pc, fun->strict()); if (!funbox) return UnrootedScript(NULL); bce.objectList.add(funbox); } } TokenStream &tokenStream = parser.tokenStream; bool canHaveDirectives = true; for (;;) { TokenKind tt = tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return UnrootedScript(NULL); } ParseNode *pn = parser.statement(); if (!pn) return UnrootedScript(NULL); if (canHaveDirectives) { if (!parser.maybeParseDirective(pn, &canHaveDirectives)) return UnrootedScript(NULL); } if (!FoldConstants(cx, &pn, &parser)) return UnrootedScript(NULL); if (!NameFunctions(cx, pn)) return UnrootedScript(NULL); if (!EmitTree(cx, &bce, pn)) return UnrootedScript(NULL); parser.freeTree(pn); } if (!SetSourceMap(cx, tokenStream, ss, script)) return UnrootedScript(NULL); // It's an error to use |arguments| in a function that has a rest parameter. if (callerFrame && callerFrame.isFunctionFrame() && callerFrame.fun()->hasRest()) { HandlePropertyName arguments = cx->names().arguments; for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) { if (r.front().key() == arguments) { parser.reportError(NULL, JSMSG_ARGUMENTS_AND_REST); return UnrootedScript(NULL); } } } /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) return UnrootedScript(NULL); if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return UnrootedScript(NULL); bce.tellDebuggerAboutCompiledScript(cx); if (!sct.complete()) return UnrootedScript(NULL); return script; }
static void StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind, size_t thingSize) { IteratorClosure *closure = static_cast<IteratorClosure *>(data); RuntimeStats *rtStats = closure->rtStats; ZoneStats *zStats = rtStats->currZoneStats; switch (traceKind) { case JSTRACE_OBJECT: { JSObject *obj = static_cast<JSObject *>(thing); CompartmentStats *cStats = GetCompartmentStats(obj->compartment()); if (obj->is<JSFunction>()) cStats->gcHeapObjectsFunction += thingSize; else if (obj->is<ArrayObject>()) cStats->gcHeapObjectsDenseArray += thingSize; else if (obj->isCrossCompartmentWrapper()) cStats->gcHeapObjectsCrossCompartmentWrapper += thingSize; else cStats->gcHeapObjectsOrdinary += thingSize; JS::ObjectsExtraSizes objectsExtra; obj->sizeOfExcludingThis(rtStats->mallocSizeOf_, &objectsExtra); cStats->objectsExtra.add(objectsExtra); // JSObject::sizeOfExcludingThis() doesn't measure objectsExtraPrivate, // so we do it here. if (ObjectPrivateVisitor *opv = closure->opv) { nsISupports *iface; if (opv->getISupports_(obj, &iface) && iface) { cStats->objectsExtra.private_ += opv->sizeOfIncludingThis(iface); } } break; } case JSTRACE_STRING: { JSString *str = static_cast<JSString *>(thing); size_t strSize = str->sizeOfExcludingThis(rtStats->mallocSizeOf_); // If we can't grow hugeStrings, let's just call this string non-huge. // We're probably about to OOM anyway. if (strSize >= JS::HugeStringInfo::MinSize() && zStats->hugeStrings.growBy(1)) { zStats->gcHeapStringsNormal += thingSize; JS::HugeStringInfo &info = zStats->hugeStrings.back(); info.length = str->length(); info.size = strSize; PutEscapedString(info.buffer, sizeof(info.buffer), &str->asLinear(), 0); } else if (str->isShort()) { MOZ_ASSERT(strSize == 0); zStats->gcHeapStringsShort += thingSize; } else { zStats->gcHeapStringsNormal += thingSize; zStats->stringCharsNonHuge += strSize; } break; } case JSTRACE_SHAPE: { Shape *shape = static_cast<Shape *>(thing); CompartmentStats *cStats = GetCompartmentStats(shape->compartment()); size_t propTableSize, kidsSize; shape->sizeOfExcludingThis(rtStats->mallocSizeOf_, &propTableSize, &kidsSize); if (shape->inDictionary()) { cStats->gcHeapShapesDict += thingSize; cStats->shapesExtraDictTables += propTableSize; JS_ASSERT(kidsSize == 0); } else { if (shape->base()->getObjectParent() == shape->compartment()->maybeGlobal()) { cStats->gcHeapShapesTreeGlobalParented += thingSize; } else { cStats->gcHeapShapesTreeNonGlobalParented += thingSize; } cStats->shapesExtraTreeTables += propTableSize; cStats->shapesExtraTreeShapeKids += kidsSize; } break; } case JSTRACE_BASE_SHAPE: { BaseShape *base = static_cast<BaseShape *>(thing); CompartmentStats *cStats = GetCompartmentStats(base->compartment()); cStats->gcHeapShapesBase += thingSize; break; } case JSTRACE_SCRIPT: { JSScript *script = static_cast<JSScript *>(thing); CompartmentStats *cStats = GetCompartmentStats(script->compartment()); cStats->gcHeapScripts += thingSize; cStats->scriptData += script->sizeOfData(rtStats->mallocSizeOf_); #ifdef JS_ION size_t baselineData = 0, baselineStubsFallback = 0; ion::SizeOfBaselineData(script, rtStats->mallocSizeOf_, &baselineData, &baselineStubsFallback); cStats->baselineData += baselineData; cStats->baselineStubsFallback += baselineStubsFallback; cStats->ionData += ion::SizeOfIonData(script, rtStats->mallocSizeOf_); #endif ScriptSource *ss = script->scriptSource(); SourceSet::AddPtr entry = closure->seenSources.lookupForAdd(ss); if (!entry) { closure->seenSources.add(entry, ss); // Not much to be done on failure. rtStats->runtime.scriptSources += ss->sizeOfIncludingThis(rtStats->mallocSizeOf_); } break; } case JSTRACE_LAZY_SCRIPT: { LazyScript *lazy = static_cast<LazyScript *>(thing); zStats->gcHeapLazyScripts += thingSize; zStats->lazyScripts += lazy->sizeOfExcludingThis(rtStats->mallocSizeOf_); break; } case JSTRACE_IONCODE: { #ifdef JS_ION zStats->gcHeapIonCodes += thingSize; // The code for a script is counted in ExecutableAllocator::sizeOfCode(). #endif break; } case JSTRACE_TYPE_OBJECT: { types::TypeObject *obj = static_cast<types::TypeObject *>(thing); zStats->gcHeapTypeObjects += thingSize; zStats->typeObjects += obj->sizeOfExcludingThis(rtStats->mallocSizeOf_); break; } } // Yes, this is a subtraction: see StatsArenaCallback() for details. zStats->gcHeapUnusedGcThings -= thingSize; }
JSScript * frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject scopeChain, HandleScript evalCaller, const ReadOnlyCompileOptions &options, const jschar *chars, size_t length, JSString *source_ /* = nullptr */, unsigned staticLevel /* = 0 */, SourceCompressionTask *extraSct /* = nullptr */) { RootedString source(cx, source_); js::TraceLogger *logger = nullptr; if (cx->isJSContext()) logger = TraceLoggerForMainThread(cx->asJSContext()->runtime()); else logger = TraceLoggerForCurrentThread(); uint32_t logId = js::TraceLogCreateTextId(logger, options); js::AutoTraceLog scriptLogger(logger, logId); js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileScript); if (cx->isJSContext()) MaybeCallSourceHandler(cx->asJSContext(), options, chars, length); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(evalCaller, options.compileAndGo); JS_ASSERT_IF(evalCaller, options.forEval); JS_ASSERT_IF(staticLevel != 0, evalCaller); if (!CheckLength(cx, length)) return nullptr; JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE); RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options)); if (!sourceObject) return nullptr; ScriptSource *ss = sourceObject->source(); SourceCompressionTask mysct(cx); SourceCompressionTask *sct = extraSct ? extraSct : &mysct; switch (options.sourcePolicy) { case CompileOptions::SAVE_SOURCE: if (!ss->setSourceCopy(cx, chars, length, false, sct)) return nullptr; break; case CompileOptions::LAZY_SOURCE: ss->setSourceRetrievable(); break; case CompileOptions::NO_SOURCE: break; } bool canLazilyParse = CanLazilyParse(cx, options); Maybe<Parser<SyntaxParseHandler> > syntaxParser; if (canLazilyParse) { syntaxParser.construct(cx, alloc, options, chars, length, /* foldConstants = */ false, (Parser<SyntaxParseHandler> *) nullptr, (LazyScript *) nullptr); } Parser<FullParseHandler> parser(cx, alloc, options, chars, length, /* foldConstants = */ true, canLazilyParse ? &syntaxParser.ref() : nullptr, nullptr); parser.sct = sct; parser.ss = ss; Directives directives(options.strictOption); GlobalSharedContext globalsc(cx, scopeChain, directives, options.extraWarningsOption); bool savedCallerFun = options.compileAndGo && evalCaller && evalCaller->functionOrCallerFunction(); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, sourceObject, 0, length)); if (!script) return nullptr; // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : nullptr; JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter::EmitterMode emitterMode = options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal; BytecodeEmitter bce(/* parent = */ nullptr, &parser, &globalsc, script, options.forEval, evalCaller, !!globalScope, options.lineno, emitterMode); if (!bce.init()) return nullptr; // Syntax parsing may cause us to restart processing of top level // statements in the script. Use Maybe<> so that the parse context can be // reset when this occurs. Maybe<ParseContext<FullParseHandler> > pc; pc.construct(&parser, (GenericParseContext *) nullptr, (ParseNode *) nullptr, &globalsc, (Directives *) nullptr, staticLevel, /* bodyid = */ 0, /* blockScopeDepth = */ 0); if (!pc.ref().init(parser.tokenStream)) return nullptr; /* If this is a direct call to eval, inherit the caller's strictness. */ if (evalCaller && evalCaller->strict()) globalsc.strict = true; if (options.compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = AtomizeString(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return nullptr; } if (evalCaller && evalCaller->functionOrCallerFunction()) { /* * An eval script in a caller frame needs to have its enclosing * function captured in case it refers to an upvar, and someone * wishes to decompile it while it's running. */ JSFunction *fun = evalCaller->functionOrCallerFunction(); Directives directives(/* strict = */ fun->strict()); ObjectBox *funbox = parser.newFunctionBox(/* fn = */ nullptr, fun, pc.addr(), directives, fun->generatorKind()); if (!funbox) return nullptr; bce.objectList.add(funbox); } } bool canHaveDirectives = true; for (;;) { TokenKind tt = parser.tokenStream.peekToken(TokenStream::Operand); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return nullptr; } TokenStream::Position pos(parser.keepAtoms); parser.tokenStream.tell(&pos); ParseNode *pn = parser.statement(canHaveDirectives); if (!pn) { if (parser.hadAbortedSyntaxParse()) { // Parsing inner functions lazily may lead the parser into an // unrecoverable state and may require starting over on the top // level statement. Restart the parse; syntax parsing has // already been disabled for the parser and the result will not // be ambiguous. parser.clearAbortedSyntaxParse(); parser.tokenStream.seek(pos); // Destroying the parse context will destroy its free // variables, so check if any deoptimization is needed. if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref())) return nullptr; pc.destroy(); pc.construct(&parser, (GenericParseContext *) nullptr, (ParseNode *) nullptr, &globalsc, (Directives *) nullptr, staticLevel, /* bodyid = */ 0, script->bindings.numBlockScoped()); if (!pc.ref().init(parser.tokenStream)) return nullptr; JS_ASSERT(parser.pc == pc.addr()); pn = parser.statement(); } if (!pn) { JS_ASSERT(!parser.hadAbortedSyntaxParse()); return nullptr; } } // Accumulate the maximum block scope depth, so that EmitTree can assert // when emitting JSOP_GETLOCAL that the local is indeed within the fixed // part of the stack frame. script->bindings.updateNumBlockScoped(pc.ref().blockScopeDepth); if (canHaveDirectives) { if (!parser.maybeParseDirective(/* stmtList = */ nullptr, pn, &canHaveDirectives)) return nullptr; } if (!FoldConstants(cx, &pn, &parser)) return nullptr; if (!NameFunctions(cx, pn)) return nullptr; if (!EmitTree(cx, &bce, pn)) return nullptr; parser.handler.freeTree(pn); } if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref())) return nullptr; if (!SetDisplayURL(cx, parser.tokenStream, ss)) return nullptr; if (!SetSourceMap(cx, parser.tokenStream, ss)) return nullptr; /* * Source map URLs passed as a compile option (usually via a HTTP source map * header) override any source map urls passed as comment pragmas. */ if (options.sourceMapURL()) { if (!ss->setSourceMapURL(cx, options.sourceMapURL())) return nullptr; } /* * Nowadays the threaded interpreter needs a last return instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_RETRVAL) < 0) return nullptr; // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). They may have block-scoped // locals, however, which are allocated to the fixed part of the stack // frame. InternalHandle<Bindings*> bindings(script, &script->bindings); if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, nullptr, pc.ref().blockScopeDepth)) return nullptr; if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return nullptr; bce.tellDebuggerAboutCompiledScript(cx); if (sct && !extraSct && !sct->complete()) return nullptr; return script; }
// Compile a JS function body, which might appear as the value of an event // handler attribute in an HTML <INPUT> tag, or in a Function() constructor. bool frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, const AutoNameVector &formals, const jschar *chars, size_t length, bool isAsmJSRecompile) { SkipRoot skip(cx, &chars); if (!CheckLength(cx, length)) return false; ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return false; if (options.filename && !ss->setFilename(cx, options.filename)) return false; JS::RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss)); if (!sourceObject) return false; SourceCompressionToken sct(cx); JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE); if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) { if (!ss->setSourceCopy(cx, chars, length, true, &sct)) return false; } bool canLazilyParse = CanLazilyParse(cx, options); Maybe<Parser<SyntaxParseHandler> > syntaxParser; if (canLazilyParse) { syntaxParser.construct(cx, options, chars, length, /* foldConstants = */ false, (Parser<SyntaxParseHandler> *) NULL, (LazyScript *) NULL); } JS_ASSERT(!options.forEval); Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true, canLazilyParse ? &syntaxParser.ref() : NULL, NULL); parser.sct = &sct; JS_ASSERT(fun); JS_ASSERT(fun->isTenured()); fun->setArgCount(formals.length()); /* FIXME: make Function format the source for a function definition. */ ParseNode *fn = CodeNode::create(PNK_FUNCTION, &parser.handler); if (!fn) return false; fn->pn_body = NULL; fn->pn_funbox = NULL; fn->pn_cookie.makeFree(); ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &parser.handler); if (!argsbody) return false; argsbody->setOp(JSOP_NOP); argsbody->makeEmpty(); fn->pn_body = argsbody; Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options, /* staticLevel = */ 0, sourceObject, /* sourceStart = */ 0, length)); if (!script) return false; // If the context is strict, immediately parse the body in strict // mode. Otherwise, we parse it normally. If we see a "use strict" // directive, we backup and reparse it as strict. TokenStream::Position start(parser.keepAtoms); parser.tokenStream.tell(&start); bool strict = StrictModeFromContext(cx); bool becameStrict; FunctionBox *funbox; ParseNode *pn; while (true) { pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox, strict, &becameStrict); if (pn) break; if (parser.hadAbortedSyntaxParse()) { // Hit some unrecoverable ambiguity during an inner syntax parse. // Syntax parsing has now been disabled in the parser, so retry // the parse. parser.clearAbortedSyntaxParse(); } else { // If the function became strict, reparse in strict mode. if (strict || !becameStrict || parser.tokenStream.hadError()) return false; strict = true; } parser.tokenStream.seek(start); } if (!NameFunctions(cx, pn)) return false; if (fn->pn_body) { JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY)); fn->pn_body->append(pn); fn->pn_body->pn_pos = pn->pn_pos; pn = fn->pn_body; } bool generateBytecode = true; #ifdef JS_ION JS_ASSERT_IF(isAsmJSRecompile, fn->pn_funbox->useAsm); if (fn->pn_funbox->useAsm && !isAsmJSRecompile) { RootedFunction moduleFun(cx); if (!CompileAsmJS(cx, parser.tokenStream, fn, options, ss, /* bufStart = */ 0, /* bufEnd = */ length, &moduleFun)) return false; if (moduleFun) { funbox->object = moduleFun; fun.set(moduleFun); // replace the existing function with the LinkAsmJS native generateBytecode = false; } } #endif if (generateBytecode) { /* * The reason for checking fun->environment() below is that certain * consumers of JS::CompileFunction, namely * nsEventListenerManager::CompileEventHandlerInternal, passes in a * NULL environment. This compiled function is never used, but instead * is cloned immediately onto the right scope chain. */ BytecodeEmitter funbce(/* parent = */ NULL, &parser, funbox, script, /* insideEval = */ false, /* evalCaller = */ NullPtr(), fun->environment() && fun->environment()->is<GlobalObject>(), options.lineno); if (!funbce.init()) return false; if (!EmitFunctionScript(cx, &funbce, pn)) return false; } if (!SetSourceMap(cx, parser.tokenStream, ss, script)) return false; if (!sct.complete()) return false; return true; }
static void StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind, size_t thingSize) { IteratorClosure *closure = static_cast<IteratorClosure *>(data); RuntimeStats *rtStats = closure->rtStats; CompartmentStats *cStats = rtStats->currCompartmentStats; switch (traceKind) { case JSTRACE_OBJECT: { JSObject *obj = static_cast<JSObject *>(thing); if (obj->isFunction()) { cStats->gcHeapObjectsFunction += thingSize; } else { cStats->gcHeapObjectsNonFunction += thingSize; } size_t slotsSize, elementsSize, miscSize; obj->sizeOfExcludingThis(rtStats->mallocSizeOf, &slotsSize, &elementsSize, &miscSize); cStats->objectSlots += slotsSize; cStats->objectElements += elementsSize; cStats->objectMisc += miscSize; if (ObjectPrivateVisitor *opv = closure->opv) { js::Class *clazz = js::GetObjectClass(obj); if (clazz->flags & JSCLASS_HAS_PRIVATE && clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { cStats->objectPrivate += opv->sizeOfIncludingThis(GetObjectPrivate(obj)); } } break; } case JSTRACE_STRING: { JSString *str = static_cast<JSString *>(thing); cStats->gcHeapStrings += thingSize; cStats->stringChars += str->sizeOfExcludingThis(rtStats->mallocSizeOf); break; } case JSTRACE_SHAPE: { Shape *shape = static_cast<Shape*>(thing); size_t propTableSize, kidsSize; shape->sizeOfExcludingThis(rtStats->mallocSizeOf, &propTableSize, &kidsSize); if (shape->inDictionary()) { cStats->gcHeapShapesDict += thingSize; cStats->shapesExtraDictTables += propTableSize; JS_ASSERT(kidsSize == 0); } else { cStats->gcHeapShapesTree += thingSize; cStats->shapesExtraTreeTables += propTableSize; cStats->shapesExtraTreeShapeKids += kidsSize; } break; } case JSTRACE_BASE_SHAPE: { cStats->gcHeapShapesBase += thingSize; break; } case JSTRACE_SCRIPT: { JSScript *script = static_cast<JSScript *>(thing); cStats->gcHeapScripts += thingSize; cStats->scriptData += script->sizeOfData(rtStats->mallocSizeOf); #ifdef JS_METHODJIT cStats->mjitData += script->sizeOfJitScripts(rtStats->mallocSizeOf); # ifdef JS_ION if (script->hasIonScript()) cStats->mjitData += script->ion->size(); # endif #endif ScriptSource *ss = script->scriptSource(); SourceSet::AddPtr entry = closure->seenSources.lookupForAdd(ss); if (!entry) { closure->seenSources.add(entry, ss); // Not much to be done on failure. rtStats->runtime.scriptSources += ss->sizeOfIncludingThis(rtStats->mallocSizeOf); } break; } case JSTRACE_IONCODE: { #ifdef JS_METHODJIT # ifdef JS_ION ion::IonCode *code = static_cast<ion::IonCode *>(thing); cStats->gcHeapScripts += thingSize; cStats->mjitData += code->bufferSize(); # endif #endif break; } case JSTRACE_TYPE_OBJECT: { types::TypeObject *obj = static_cast<types::TypeObject *>(thing); cStats->gcHeapTypeObjects += thingSize; obj->sizeOfExcludingThis(&cStats->typeInferenceSizes, rtStats->mallocSizeOf); break; } #if JS_HAS_XML_SUPPORT case JSTRACE_XML: { cStats->gcHeapXML += thingSize; break; } #endif } // Yes, this is a subtraction: see StatsArenaCallback() for details. cStats->gcHeapUnusedGcThings -= thingSize; }
// Compile a JS function body, which might appear as the value of an event // handler attribute in an HTML <INPUT> tag, or in a Function() constructor. bool frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, const AutoNameVector &formals, const jschar *chars, size_t length) { // FIXME: make Function pass in two strings and parse them as arguments and // ProgramElements respectively. SkipRoot skip(cx, &chars); MaybeCallSourceHandler(cx, options, chars, length); if (!CheckLength(cx, length)) return false; ScriptSource *ss = cx->new_<ScriptSource>(options.originPrincipals()); if (!ss) return false; if (options.filename && !ss->setFilename(cx, options.filename)) return false; JS::RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss)); if (!sourceObject) return false; SourceCompressionToken sct(cx); JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE); if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) { if (!ss->setSourceCopy(cx, chars, length, true, &sct)) return false; } bool canLazilyParse = CanLazilyParse(cx, options); Maybe<Parser<SyntaxParseHandler> > syntaxParser; if (canLazilyParse) { syntaxParser.construct(cx, &cx->tempLifoAlloc(), options, chars, length, /* foldConstants = */ false, (Parser<SyntaxParseHandler> *) NULL, (LazyScript *) NULL); } JS_ASSERT(!options.forEval); Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length, /* foldConstants = */ true, canLazilyParse ? &syntaxParser.ref() : NULL, NULL); parser.sct = &sct; parser.ss = ss; JS_ASSERT(fun); JS_ASSERT(fun->isTenured()); fun->setArgCount(formals.length()); // Speculatively parse using the default directives implied by the context. // If a directive is encountered (e.g., "use strict") that changes how the // function should have been parsed, we backup and reparse with the new set // of directives. Directives directives(options.strictOption); TokenStream::Position start(parser.keepAtoms); parser.tokenStream.tell(&start); ParseNode *fn; while (true) { Directives newDirectives = directives; fn = parser.standaloneFunctionBody(fun, formals, directives, &newDirectives); if (fn) break; if (parser.hadAbortedSyntaxParse()) { // Hit some unrecoverable ambiguity during an inner syntax parse. // Syntax parsing has now been disabled in the parser, so retry // the parse. parser.clearAbortedSyntaxParse(); } else { if (parser.tokenStream.hadError() || directives == newDirectives) return false; // Assignment must be monotonic to prevent reparsing iloops JS_ASSERT_IF(directives.strict(), newDirectives.strict()); JS_ASSERT_IF(directives.asmJS(), newDirectives.asmJS()); directives = newDirectives; } parser.tokenStream.seek(start); } if (!NameFunctions(cx, fn)) return false; if (fn->pn_funbox->function()->isInterpreted()) { JS_ASSERT(fun == fn->pn_funbox->function()); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options, /* staticLevel = */ 0, sourceObject, /* sourceStart = */ 0, length)); if (!script) return false; script->bindings = fn->pn_funbox->bindings; /* * The reason for checking fun->environment() below is that certain * consumers of JS::CompileFunction, namely * nsEventListenerManager::CompileEventHandlerInternal, passes in a * NULL environment. This compiled function is never used, but instead * is cloned immediately onto the right scope chain. */ BytecodeEmitter funbce(/* parent = */ NULL, &parser, fn->pn_funbox, script, /* insideEval = */ false, /* evalCaller = */ NullPtr(), fun->environment() && fun->environment()->is<GlobalObject>(), options.lineno); if (!funbce.init()) return false; if (!EmitFunctionScript(cx, &funbce, fn->pn_body)) return false; } else { fun.set(fn->pn_funbox->function()); JS_ASSERT(IsAsmJSModuleNative(fun->native())); } if (!SetSourceMap(cx, parser.tokenStream, ss)) return false; if (!sct.complete()) return false; return true; }
RawScript frontend::CompileScript(JSContext *cx, HandleObject scopeChain, HandleScript evalCaller, const CompileOptions &options, const jschar *chars, size_t length, JSString *source_ /* = NULL */, unsigned staticLevel /* = 0 */, SourceCompressionToken *extraSct /* = NULL */) { RootedString source(cx, source_); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(evalCaller, options.compileAndGo); JS_ASSERT_IF(staticLevel != 0, evalCaller); if (!CheckLength(cx, length)) return NULL; JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE); ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return NULL; if (options.filename && !ss->setFilename(cx, options.filename)) return NULL; ScriptSourceHolder ssh(ss); SourceCompressionToken mysct(cx); SourceCompressionToken *sct = (extraSct) ? extraSct : &mysct; switch (options.sourcePolicy) { case CompileOptions::SAVE_SOURCE: if (!ss->setSourceCopy(cx, chars, length, false, sct)) return NULL; break; case CompileOptions::LAZY_SOURCE: ss->setSourceRetrievable(); break; case CompileOptions::NO_SOURCE: break; } Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return NULL; parser.sct = sct; GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx)); ParseContext<FullParseHandler> pc(&parser, NULL, &globalsc, staticLevel, /* bodyid = */ 0); if (!pc.init()) return NULL; bool savedCallerFun = options.compileAndGo && evalCaller && (evalCaller->function() || evalCaller->savedCallerFun); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, ss, 0, length)); if (!script) return NULL; // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). InternalHandle<Bindings*> bindings(script, &script->bindings); if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, NULL)) return NULL; // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL; JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, evalCaller, !!globalScope, options.lineno, options.selfHostingMode); if (!bce.init()) return NULL; /* If this is a direct call to eval, inherit the caller's strictness. */ if (evalCaller && evalCaller->strict) globalsc.strict = true; if (options.compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = AtomizeString<CanGC>(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return NULL; } if (evalCaller && evalCaller->functionOrCallerFunction()) { /* * An eval script in a caller frame needs to have its enclosing * function captured in case it refers to an upvar, and someone * wishes to decompile it while it's running. */ JSFunction *fun = evalCaller->functionOrCallerFunction(); ObjectBox *funbox = parser.newFunctionBox(fun, &pc, fun->strict()); if (!funbox) return NULL; bce.objectList.add(funbox); } } bool canHaveDirectives = true; for (;;) { TokenKind tt = parser.tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return NULL; } ParseNode *pn = parser.statement(); if (!pn) return NULL; if (canHaveDirectives) { if (!parser.maybeParseDirective(pn, &canHaveDirectives)) return NULL; } if (!FoldConstants(cx, &pn, &parser)) return NULL; if (!NameFunctions(cx, pn)) return NULL; if (!EmitTree(cx, &bce, pn)) return NULL; parser.handler.freeTree(pn); } if (!SetSourceMap(cx, parser.tokenStream, ss, script)) return NULL; if (evalCaller && evalCaller->functionOrCallerFunction()) { // Watch for uses of 'arguments' within the evaluated script, both as // free variables and as variables redeclared with 'var'. RootedFunction fun(cx, evalCaller->functionOrCallerFunction()); HandlePropertyName arguments = cx->names().arguments; for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) { if (r.front().key() == arguments) { if (!CheckArgumentsWithinEval(cx, parser, fun)) return NULL; } } for (AtomDefnListMap::Range r = pc.decls().all(); !r.empty(); r.popFront()) { if (r.front().key() == arguments) { if (!CheckArgumentsWithinEval(cx, parser, fun)) return NULL; } } // If the eval'ed script contains any debugger statement, force construction // of arguments objects for the caller script and any other scripts it is // transitively nested inside. if (pc.sc->hasDebuggerStatement()) { RootedObject scope(cx, scopeChain); while (scope->isScope() || scope->isDebugScope()) { if (scope->isCall() && !scope->asCall().isForEval()) { RootedScript script(cx, scope->asCall().callee().nonLazyScript()); if (script->argumentsHasVarBinding()) { if (!JSScript::argumentsOptimizationFailed(cx, script)) return NULL; } } scope = scope->enclosingScope(); } } } /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) return NULL; if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return NULL; bce.tellDebuggerAboutCompiledScript(cx); if (sct == &mysct && !sct->complete()) return NULL; return script; }
// Compile a JS function body, which might appear as the value of an event // handler attribute in an HTML <INPUT> tag, or in a Function() constructor. bool frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions options, Bindings *bindings, const jschar *chars, size_t length) { if (!CheckLength(cx, length)) return NULL; ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return NULL; AutoAttachToRuntime attacher(cx->runtime, ss); SourceCompressionToken sct(cx); if (!ss->setSourceCopy(cx, chars, length, true, &sct)) return NULL; options.setCompileAndGo(false); Parser parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return false; parser.sct = &sct; JS_ASSERT(fun); SharedContext funsc(cx, /* scopeChain = */ NULL, fun, /* funbox = */ NULL, StrictModeFromContext(cx)); funsc.bindings.transfer(bindings); fun->setArgCount(funsc.bindings.numArgs()); unsigned staticLevel = 0; TreeContext funtc(&parser, &funsc, staticLevel, /* bodyid = */ 0); if (!funtc.init()) return false; Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options, staticLevel, ss, 0, length)); if (!script) return false; StackFrame *nullCallerFrame = NULL; BytecodeEmitter funbce(/* parent = */ NULL, &parser, &funsc, script, nullCallerFrame, /* hasGlobalScope = */ false, options.lineno); if (!funbce.init()) return false; /* FIXME: make Function format the source for a function definition. */ ParseNode *fn = FunctionNode::create(PNK_NAME, &parser); if (!fn) return false; fn->pn_body = NULL; fn->pn_cookie.makeFree(); ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &parser); if (!argsbody) return false; argsbody->setOp(JSOP_NOP); argsbody->makeEmpty(); fn->pn_body = argsbody; unsigned nargs = fun->nargs; if (nargs) { /* * NB: do not use AutoLocalNameArray because it will release space * allocated from cx->tempLifoAlloc by DefineArg. */ BindingVector names(cx); if (!GetOrderedBindings(cx, funsc.bindings, &names)) return false; for (unsigned i = 0; i < nargs; i++) { if (!DefineArg(fn, names[i].maybeName, i, &parser)) return false; } } /* * After we're done parsing, we must fold constants, analyze any nested * functions, and generate code for this function, including a stop opcode * at the end. */ ParseNode *pn = parser.functionBody(Parser::StatementListBody); if (!pn) return false; if (!parser.tokenStream.matchToken(TOK_EOF)) { parser.reportError(NULL, JSMSG_SYNTAX_ERROR); return false; } if (!FoldConstants(cx, pn, &parser)) return false; if (!AnalyzeFunctions(&parser, nullCallerFrame)) return false; if (fn->pn_body) { JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY)); fn->pn_body->append(pn); fn->pn_body->pn_pos = pn->pn_pos; pn = fn->pn_body; } if (!EmitFunctionScript(cx, &funbce, pn)) return false; return true; }
const char16_t * FrameIter::scriptDisplayURL() const { ScriptSource *ss = scriptSource(); return ss->hasDisplayURL() ? ss->displayURL() : nullptr; }
JSScript * frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame, const CompileOptions &options, const jschar *chars, size_t length, JSString *source_ /* = NULL */, unsigned staticLevel /* = 0 */) { RootedString source(cx, source_); class ProbesManager { const char* filename; unsigned lineno; public: ProbesManager(const char *f, unsigned l) : filename(f), lineno(l) { Probes::compileScriptBegin(filename, lineno); } ~ProbesManager() { Probes::compileScriptEnd(filename, lineno); } }; ProbesManager probesManager(options.filename, options.lineno); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, options.compileAndGo); JS_ASSERT_IF(staticLevel != 0, callerFrame); if (!CheckLength(cx, length)) return NULL; ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return NULL; AutoAttachToRuntime attacher(cx->runtime, ss); SourceCompressionToken sct(cx); if (!cx->hasRunOption(JSOPTION_ONLY_CNG_SOURCE) || options.compileAndGo) { if (!ss->setSourceCopy(cx, chars, length, false, &sct)) return NULL; } Parser parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return NULL; parser.sct = &sct; SharedContext sc(cx, scopeChain, /* fun = */ NULL, /* funbox = */ NULL, StrictModeFromContext(cx)); TreeContext tc(&parser, &sc, staticLevel, /* bodyid = */ 0); if (!tc.init()) return NULL; bool savedCallerFun = options.compileAndGo && callerFrame && callerFrame->isFunctionFrame(); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, ss, 0, length)); if (!script) return NULL; // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL; JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter bce(/* parent = */ NULL, &parser, &sc, script, callerFrame, !!globalScope, options.lineno); if (!bce.init()) return NULL; /* If this is a direct call to eval, inherit the caller's strictness. */ if (callerFrame && callerFrame->isScriptFrame() && callerFrame->script()->strictModeCode) sc.strictModeState = StrictMode::STRICT; if (options.compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = js_AtomizeString(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return NULL; } if (callerFrame && callerFrame->isFunctionFrame()) { /* * An eval script in a caller frame needs to have its enclosing * function captured in case it refers to an upvar, and someone * wishes to decompile it while it's running. */ ObjectBox *funbox = parser.newObjectBox(callerFrame->fun()); if (!funbox) return NULL; funbox->emitLink = bce.objectList.lastbox; bce.objectList.lastbox = funbox; bce.objectList.length++; } } ParseNode *pn; #if JS_HAS_XML_SUPPORT pn = NULL; bool onlyXML; onlyXML = true; #endif TokenStream &tokenStream = parser.tokenStream; { ParseNode *stringsAtStart = ListNode::create(PNK_STATEMENTLIST, &parser); if (!stringsAtStart) return NULL; stringsAtStart->makeEmpty(); bool ok = parser.processDirectives(stringsAtStart) && EmitTree(cx, &bce, stringsAtStart); parser.freeTree(stringsAtStart); if (!ok) return NULL; } JS_ASSERT(sc.strictModeState != StrictMode::UNKNOWN); for (;;) { TokenKind tt = tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return NULL; } pn = parser.statement(); if (!pn) return NULL; if (!FoldConstants(cx, pn, &parser)) return NULL; if (!AnalyzeFunctions(&parser, callerFrame)) return NULL; tc.functionList = NULL; if (!EmitTree(cx, &bce, pn)) return NULL; #if JS_HAS_XML_SUPPORT if (!pn->isKind(PNK_SEMI) || !pn->pn_kid || !pn->pn_kid->isXMLItem()) onlyXML = false; #endif parser.freeTree(pn); } #if JS_HAS_XML_SUPPORT /* * Prevent XML data theft via <script src="http://victim.com/foo.xml">. * For background, see: * * https://bugzilla.mozilla.org/show_bug.cgi?id=336551 */ if (pn && onlyXML && !callerFrame) { parser.reportError(NULL, JSMSG_XML_WHOLE_PROGRAM); return NULL; } #endif // It's an error to use |arguments| in a function that has a rest parameter. if (callerFrame && callerFrame->isFunctionFrame() && callerFrame->fun()->hasRest()) { PropertyName *arguments = cx->runtime->atomState.argumentsAtom; for (AtomDefnRange r = tc.lexdeps->all(); !r.empty(); r.popFront()) { if (r.front().key() == arguments) { parser.reportError(NULL, JSMSG_ARGUMENTS_AND_REST); return NULL; } } // We're not in a function context, so we don't expect any bindings. JS_ASSERT(!sc.bindings.hasBinding(cx, arguments)); } /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) return NULL; if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return NULL; bce.tellDebuggerAboutCompiledScript(cx); return script; }
JSObject * GlobalObject::initFunctionAndObjectClasses(JSContext *cx) { Rooted<GlobalObject*> self(cx, this); JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); JS_ASSERT(isNative()); /* * Calling a function from a cleared global triggers this (yeah, I know). * Uncomment this once bug 470510 is fixed (if that bug doesn't remove * isCleared entirely). */ // JS_ASSERT(!isCleared()); /* If cx has no global object, make this the global object. */ if (!cx->globalObject) JS_SetGlobalObject(cx, self); RootedObject objectProto(cx); /* * Create |Object.prototype| first, mirroring CreateBlankProto but for the * prototype of the created object. */ objectProto = NewObjectWithGivenProto(cx, &ObjectClass, NULL, self); if (!objectProto || !objectProto->setSingletonType(cx)) return NULL; /* * The default 'new' type of Object.prototype is required by type inference * to have unknown properties, to simplify handling of e.g. heterogenous * objects in JSON and script literals. */ if (!objectProto->setNewTypeUnknown(cx)) return NULL; /* Create |Function.prototype| next so we can create other functions. */ RootedFunction functionProto(cx); { JSObject *functionProto_ = NewObjectWithGivenProto(cx, &FunctionClass, objectProto, self); if (!functionProto_) return NULL; functionProto = functionProto_->toFunction(); /* * Bizarrely, |Function.prototype| must be an interpreted function, so * give it the guts to be one. */ JSObject *proto = js_NewFunction(cx, functionProto, NULL, 0, JSFUN_INTERPRETED, self, NULL); if (!proto) return NULL; JS_ASSERT(proto == functionProto); functionProto->flags |= JSFUN_PROTOTYPE; const char *rawSource = "() {\n}"; size_t sourceLen = strlen(rawSource); jschar *source = InflateString(cx, rawSource, &sourceLen); if (!source) return NULL; ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) { cx->free_(source); return NULL; } ScriptSourceHolder ssh(cx->runtime, ss); ss->setSource(source, sourceLen); CompileOptions options(cx); options.setNoScriptRval(true) .setVersion(JSVERSION_DEFAULT); Rooted<JSScript*> script(cx, JSScript::Create(cx, /* enclosingScope = */ NullPtr(), /* savedCallerFun = */ false, options, /* staticLevel = */ 0, ss, 0, ss->length())); if (!script || !JSScript::fullyInitTrivial(cx, script)) return NULL; functionProto->initScript(script); functionProto->getType(cx)->interpretedFunction = functionProto; script->setFunction(functionProto); if (!functionProto->setSingletonType(cx)) return NULL; /* * The default 'new' type of Function.prototype is required by type * inference to have unknown properties, to simplify handling of e.g. * CloneFunctionObject. */ if (!functionProto->setNewTypeUnknown(cx)) return NULL; } /* Create the Object function now that we have a [[Prototype]] for it. */ RootedFunction objectCtor(cx); { JSObject *ctor = NewObjectWithGivenProto(cx, &FunctionClass, functionProto, self); if (!ctor) return NULL; objectCtor = js_NewFunction(cx, ctor, js_Object, 1, JSFUN_CONSTRUCTOR, self, CLASS_NAME(cx, Object)); if (!objectCtor) return NULL; } /* * Install |Object| and |Object.prototype| for the benefit of subsequent * code that looks for them. */ self->setObjectClassDetails(objectCtor, objectProto); /* Create |Function| so it and |Function.prototype| can be installed. */ RootedFunction functionCtor(cx); { // Note that ctor is rooted purely for the JS_ASSERT at the end RootedObject ctor(cx, NewObjectWithGivenProto(cx, &FunctionClass, functionProto, self)); if (!ctor) return NULL; functionCtor = js_NewFunction(cx, ctor, Function, 1, JSFUN_CONSTRUCTOR, self, CLASS_NAME(cx, Function)); if (!functionCtor) return NULL; JS_ASSERT(ctor == functionCtor); } /* * Install |Function| and |Function.prototype| so that we can freely create * functions and objects without special effort. */ self->setFunctionClassDetails(functionCtor, functionProto); /* * The hard part's done: now go back and add all the properties these * primordial values have. */ if (!LinkConstructorAndPrototype(cx, objectCtor, objectProto) || !DefinePropertiesAndBrand(cx, objectProto, NULL, object_methods)) { return NULL; } /* * Add an Object.prototype.__proto__ accessor property to implement that * extension (if it's actually enabled). Cache the getter for this * function so that cross-compartment [[Prototype]]-getting is implemented * in one place. */ Rooted<JSFunction*> getter(cx, js_NewFunction(cx, NULL, ProtoGetter, 0, 0, self, NULL)); if (!getter) return NULL; #if JS_HAS_OBJ_PROTO_PROP Rooted<JSFunction*> setter(cx, js_NewFunction(cx, NULL, ProtoSetter, 0, 0, self, NULL)); if (!setter) return NULL; RootedValue undefinedValue(cx, UndefinedValue()); if (!objectProto->defineProperty(cx, cx->runtime->atomState.protoAtom, undefinedValue, JS_DATA_TO_FUNC_PTR(PropertyOp, getter.get()), JS_DATA_TO_FUNC_PTR(StrictPropertyOp, setter.get()), JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED)) { return NULL; } #endif /* JS_HAS_OBJ_PROTO_PROP */ self->setProtoGetter(getter); if (!DefinePropertiesAndBrand(cx, objectCtor, NULL, object_static_methods) || !LinkConstructorAndPrototype(cx, functionCtor, functionProto) || !DefinePropertiesAndBrand(cx, functionProto, NULL, function_methods) || !DefinePropertiesAndBrand(cx, functionCtor, NULL, NULL)) { return NULL; } /* Add the global Function and Object properties now. */ jsid objectId = NameToId(CLASS_NAME(cx, Object)); if (!self->addDataProperty(cx, objectId, JSProto_Object + JSProto_LIMIT * 2, 0)) return NULL; jsid functionId = NameToId(CLASS_NAME(cx, Function)); if (!self->addDataProperty(cx, functionId, JSProto_Function + JSProto_LIMIT * 2, 0)) return NULL; /* Heavy lifting done, but lingering tasks remain. */ /* ES5 15.1.2.1. */ RootedId id(cx, NameToId(cx->runtime->atomState.evalAtom)); JSObject *evalobj = js_DefineFunction(cx, self, id, IndirectEval, 1, JSFUN_STUB_GSOPS); if (!evalobj) return NULL; self->setOriginalEval(evalobj); /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */ RootedFunction throwTypeError(cx, js_NewFunction(cx, NULL, ThrowTypeError, 0, 0, self, NULL)); if (!throwTypeError) return NULL; if (!throwTypeError->preventExtensions(cx)) return NULL; self->setThrowTypeError(throwTypeError); RootedObject intrinsicsHolder(cx, JS_NewObject(cx, NULL, NULL, self)); if (!intrinsicsHolder) return NULL; self->setIntrinsicsHolder(intrinsicsHolder); if (!JS_DefineFunctions(cx, intrinsicsHolder, intrinsic_functions)) return NULL; /* * The global object should have |Object.prototype| as its [[Prototype]]. * Eventually we'd like to have standard classes be there from the start, * and thus we would know we were always setting what had previously been a * null [[Prototype]], but right now some code assumes it can set the * [[Prototype]] before standard classes have been initialized. For now, * only set the [[Prototype]] if it hasn't already been set. */ if (self->shouldSplicePrototype(cx) && !self->splicePrototype(cx, objectProto)) return NULL; /* * Notify any debuggers about the creation of the script for * |Function.prototype| -- after all initialization, for simplicity. */ js_CallNewScriptHook(cx, functionProto->script(), functionProto); return functionProto; }
JS_GetScriptSourceMap(JSContext *cx, JSScript *script) { ScriptSource *source = script->scriptSource(); JS_ASSERT(source); return source->hasSourceMapURL() ? source->sourceMapURL() : nullptr; }
// Compile a JS function body, which might appear as the value of an event // handler attribute in an HTML <INPUT> tag, or in a Function() constructor. bool frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions options, const AutoNameVector &formals, const jschar *chars, size_t length) { if (!CheckLength(cx, length)) return false; ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return false; ScriptSourceHolder ssh(cx->runtime, ss); SourceCompressionToken sct(cx); JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE); if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) { if (!ss->setSourceCopy(cx, chars, length, true, &sct)) return false; } options.setCompileAndGo(false); Parser parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return false; parser.sct = &sct; JS_ASSERT(fun); StrictMode sms = StrictModeFromContext(cx); FunctionBox *funbox = parser.newFunctionBox(fun, /* outerpc = */ NULL, sms); SharedContext funsc(cx, /* scopeChain = */ NULL, funbox, sms); fun->setArgCount(formals.length()); unsigned staticLevel = 0; ParseContext funpc(&parser, &funsc, staticLevel, /* bodyid = */ 0); if (!funpc.init()) return false; /* FIXME: make Function format the source for a function definition. */ ParseNode *fn = FunctionNode::create(PNK_NAME, &parser); if (!fn) return false; fn->pn_body = NULL; fn->pn_cookie.makeFree(); ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &parser); if (!argsbody) return false; argsbody->setOp(JSOP_NOP); argsbody->makeEmpty(); fn->pn_body = argsbody; for (unsigned i = 0; i < formals.length(); i++) { if (!DefineArg(&parser, fn, formals[i])) return false; } /* * After we're done parsing, we must fold constants, analyze any nested * functions, and generate code for this function, including a stop opcode * at the end. */ ParseNode *pn = parser.functionBody(Parser::StatementListBody); if (!pn) return false; if (!parser.tokenStream.matchToken(TOK_EOF)) { parser.reportError(NULL, JSMSG_SYNTAX_ERROR); return false; } if (!FoldConstants(cx, pn, &parser)) return false; Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options, staticLevel, ss, 0, length)); if (!script) return false; if (!funpc.generateFunctionBindings(cx, &script->bindings)) return false; BytecodeEmitter funbce(/* parent = */ NULL, &parser, &funsc, script, /* callerFrame = */ NULL, /* hasGlobalScope = */ false, options.lineno); if (!funbce.init()) return false; if (!NameFunctions(cx, pn)) return false; if (fn->pn_body) { JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY)); fn->pn_body->append(pn); fn->pn_body->pn_pos = pn->pn_pos; pn = fn->pn_body; } if (!SetSourceMap(cx, parser.tokenStream, ss, script)) return false; if (!EmitFunctionScript(cx, &funbce, pn)) return false; return true; }
JSString * js::AsmJSModuleToString(JSContext *cx, HandleFunction fun, bool addParenToLambda) { AsmJSModule &module = ModuleFunctionToModuleObject(fun).module(); uint32_t begin = module.srcStart(); uint32_t end = module.srcEndAfterCurly(); ScriptSource *source = module.scriptSource(); StringBuffer out(cx); // Whether the function has been created with a Function ctor bool funCtor = begin == 0 && end == source->length() && source->argumentsNotIncluded(); if (addParenToLambda && fun->isLambda() && !out.append("(")) return nullptr; if (!out.append("function ")) return nullptr; if (fun->atom() && !out.append(fun->atom())) return nullptr; if (funCtor) { // Functions created with the function constructor don't have arguments in their source. if (!out.append("(")) return nullptr; if (PropertyName *argName = module.globalArgumentName()) { if (!out.append(argName)) return nullptr; } if (PropertyName *argName = module.importArgumentName()) { if (!out.append(", ") || !out.append(argName)) return nullptr; } if (PropertyName *argName = module.bufferArgumentName()) { if (!out.append(", ") || !out.append(argName)) return nullptr; } if (!out.append(") {\n")) return nullptr; } Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end)); if (!src) return nullptr; if (module.strict()) { if (!AppendUseStrictSource(cx, fun, src, out)) return nullptr; } else { if (!out.append(src)) return nullptr; } if (funCtor && !out.append("\n}")) return nullptr; if (addParenToLambda && fun->isLambda() && !out.append(")")) return nullptr; return out.finishString(); }
// Compile a JS function body, which might appear as the value of an event // handler attribute in an HTML <INPUT> tag, or in a Function() constructor. bool frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions options, const AutoNameVector &formals, StableCharPtr chars, size_t length) { if (!CheckLength(cx, length)) return false; ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return false; ScriptSourceHolder ssh(ss); SourceCompressionToken sct(cx); JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE); if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) { if (!ss->setSourceCopy(cx, chars, length, true, &sct)) return false; } options.setCompileAndGo(false); Parser parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return false; parser.sct = &sct; JS_ASSERT(fun); fun->setArgCount(formals.length()); /* FIXME: make Function format the source for a function definition. */ ParseNode *fn = FunctionNode::create(PNK_FUNCTION, &parser); if (!fn) return false; fn->pn_body = NULL; fn->pn_funbox = NULL; fn->pn_cookie.makeFree(); ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &parser); if (!argsbody) return false; argsbody->setOp(JSOP_NOP); argsbody->makeEmpty(); fn->pn_body = argsbody; Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options, /* staticLevel = */ 0, ss, /* sourceStart = */ 0, length)); if (!script) return false; // If the context is strict, immediately parse the body in strict // mode. Otherwise, we parse it normally. If we see a "use strict" // directive, we backup and reparse it as strict. TokenStream::Position start; parser.tokenStream.tell(&start); bool initiallyStrict = StrictModeFromContext(cx); bool becameStrict; FunctionBox *funbox; ParseNode *pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox, initiallyStrict, &becameStrict); if (!pn) { if (initiallyStrict || !becameStrict || parser.tokenStream.hadError()) return false; // Reparse in strict mode. parser.tokenStream.seek(start); pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox, /* strict = */ true); if (!pn) return false; } BytecodeEmitter funbce(/* parent = */ NULL, &parser, funbox, script, /* callerFrame = */ NullFramePtr(), /* hasGlobalScope = */ false, options.lineno); if (!funbce.init()) return false; if (!NameFunctions(cx, pn)) return false; if (fn->pn_body) { JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY)); fn->pn_body->append(pn); fn->pn_body->pn_pos = pn->pn_pos; pn = fn->pn_body; } if (!SetSourceMap(cx, parser.tokenStream, ss, script)) return false; if (!EmitFunctionScript(cx, &funbce, pn)) return false; if (!sct.complete()) return false; return true; }