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 ();
}
Beispiel #3
0
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;
}
Beispiel #5
0
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;
}
Beispiel #8
0
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);
 }
Beispiel #10
0
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, &regExpStaticsSize,
                                 &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;
}
Beispiel #14
0
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;
}
Beispiel #15
0
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;
}
Beispiel #17
0
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;
}
Beispiel #18
0
// 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;
}
Beispiel #21
0
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;
}
Beispiel #23
0
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;
}
Beispiel #26
0
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;
}
Beispiel #28
0
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();
}
Beispiel #29
0
// 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;
}