JSScript * frontend::CompileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerFrame, JSPrincipals *principals, JSPrincipals *originPrincipals, uint32_t tcflags, const jschar *chars, size_t length, const char *filename, unsigned lineno, JSVersion version, JSString *source /* = NULL */, unsigned staticLevel /* = 0 */) { TokenKind tt; ParseNode *pn; bool inDirectivePrologue; JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_COMPILE_FOR_EVAL | TCF_NEED_SCRIPT_GLOBAL))); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO); JS_ASSERT_IF(staticLevel != 0, callerFrame); Parser parser(cx, principals, originPrincipals, callerFrame); if (!parser.init(chars, length, filename, lineno, version)) return NULL; TokenStream &tokenStream = parser.tokenStream; BytecodeEmitter bce(&parser, tokenStream.getLineno()); if (!bce.init(cx, TreeContext::USED_AS_TREE_CONTEXT)) return NULL; Probes::compileScriptBegin(cx, filename, lineno); MUST_FLOW_THROUGH("out"); // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalObj = scopeChain && scopeChain == &scopeChain->global() ? &scopeChain->global() : NULL; JS_ASSERT_IF(globalObj, globalObj->isNative()); JS_ASSERT_IF(globalObj, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalObj->getClass())); RootedVar<JSScript*> script(cx); GlobalScope globalScope(cx, globalObj, &bce); bce.flags |= tcflags; bce.setScopeChain(scopeChain); bce.globalScope = &globalScope; if (!SetStaticLevel(&bce, staticLevel)) goto out; /* If this is a direct call to eval, inherit the caller's strictness. */ if (callerFrame && callerFrame->isScriptFrame() && callerFrame->script()->strictModeCode) { bce.flags |= TCF_STRICT_MODE_CODE; tokenStream.setStrictMode(); } #ifdef DEBUG bool savedCallerFun; savedCallerFun = false; #endif if (tcflags & TCF_COMPILE_N_GO) { 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, &_)) goto out; } 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) goto out; funbox->emitLink = bce.objectList.lastbox; bce.objectList.lastbox = funbox; bce.objectList.length++; #ifdef DEBUG savedCallerFun = true; #endif } } /* * Inline this->statements to emit as we go to save AST space. We must * generate our script-body blockid since we aren't calling Statements. */ uint32_t bodyid; if (!GenerateBlockId(&bce, bodyid)) goto out; bce.bodyid = bodyid; #if JS_HAS_XML_SUPPORT pn = NULL; bool onlyXML; onlyXML = true; #endif inDirectivePrologue = true; tokenStream.setOctalCharacterEscape(false); for (;;) { tt = tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); goto out; } pn = parser.statement(); if (!pn) goto out; JS_ASSERT(!bce.blockNode); if (inDirectivePrologue && !parser.recognizeDirectivePrologue(pn, &inDirectivePrologue)) goto out; if (!FoldConstants(cx, pn, &bce)) goto out; if (!AnalyzeFunctions(&bce)) goto out; bce.functionList = NULL; if (!EmitTree(cx, &bce, pn)) goto out; #if JS_HAS_XML_SUPPORT if (!pn->isKind(PNK_SEMI) || !pn->pn_kid || !pn->pn_kid->isXMLItem()) onlyXML = false; #endif bce.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.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_XML_WHOLE_PROGRAM); goto out; } #endif /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) goto out; JS_ASSERT(bce.version() == version); script = JSScript::NewScriptFromEmitter(cx, &bce); if (!script) goto out; JS_ASSERT(script->savedCallerFun == savedCallerFun); if (!DefineGlobals(cx, globalScope, script)) script = NULL; out: Probes::compileScriptEnd(cx, script, filename, lineno); return script; }
JSScript * frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame, JSPrincipals *principals, JSPrincipals *originPrincipals, bool compileAndGo, bool noScriptRval, const jschar *chars, size_t length, const char *filename, unsigned lineno, JSVersion version, 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(filename, lineno); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, compileAndGo); JS_ASSERT_IF(staticLevel != 0, callerFrame); Parser parser(cx, principals, originPrincipals, chars, length, filename, lineno, version, /* foldConstants = */ true, compileAndGo); if (!parser.init()) return NULL; SharedContext sc(cx, scopeChain, /* fun = */ NULL, /* funbox = */ NULL, StrictModeFromContext(cx)); TreeContext tc(&parser, &sc, staticLevel, /* bodyid = */ 0); if (!tc.init()) return NULL; bool savedCallerFun = compileAndGo && callerFrame && callerFrame->isFunctionFrame(); Rooted<JSScript*> script(cx, JSScript::Create(cx, /* enclosingScope = */ NullPtr(), savedCallerFun, principals, originPrincipals, compileAndGo, noScriptRval, version, staticLevel)); 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, 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 (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.lookup(cx, arguments, NULL) == NONE); } /* * 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; }
static JSBool Exception(JSContext *cx, uintN argc, Value *vp) { JSString *message, *filename; JSStackFrame *fp; /* * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when * called as functions, without operator new. But as we do not give * each constructor a distinct JSClass, whose .name member is used by * NewNativeClassInstance to find the class prototype, we must get the * class prototype ourselves. */ JSObject &callee = vp[0].toObject(); Value protov; if (!callee.getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov)) return JS_FALSE; JSObject *errProto = &protov.toObject(); JSObject *obj = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent()); if (!obj) return JS_FALSE; /* * If it's a new object of class Exception, then null out the private * data so that the finalizer doesn't attempt to free it. */ if (obj->getClass() == &js_ErrorClass) obj->setPrivate(NULL); /* Set the 'message' property. */ Value *argv = vp + 2; if (argc != 0) { message = js_ValueToString(cx, argv[0]); if (!message) return JS_FALSE; argv[0].setString(message); } else { message = cx->runtime->emptyString; } /* Set the 'fileName' property. */ if (argc > 1) { filename = js_ValueToString(cx, argv[1]); if (!filename) return JS_FALSE; argv[1].setString(filename); fp = NULL; } else { fp = js_GetScriptedCaller(cx, NULL); if (fp) { filename = FilenameToString(cx, fp->script()->filename); if (!filename) return JS_FALSE; } else { filename = cx->runtime->emptyString; } } /* Set the 'lineNumber' property. */ uint32_t lineno; if (argc > 2) { if (!ValueToECMAUint32(cx, argv[2], &lineno)) return JS_FALSE; } else { if (!fp) fp = js_GetScriptedCaller(cx, NULL); lineno = (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0; } if (obj->getClass() == &js_ErrorClass && !InitExnPrivate(cx, obj, message, filename, lineno, NULL)) { return JS_FALSE; } vp->setObject(*obj); return JS_TRUE; }
CompileStatus mjit::Compiler::inlineNativeFunction(uint32_t argc, bool callingNew) { if (!cx->typeInferenceEnabled()) return Compile_InlineAbort; FrameEntry *origCallee = frame.peek(-((int)argc + 2)); FrameEntry *thisValue = frame.peek(-((int)argc + 1)); types::TypeSet *thisTypes = analysis->poppedTypes(PC, argc); if (!origCallee->isConstant() || !origCallee->isType(JSVAL_TYPE_OBJECT)) return Compile_InlineAbort; JSObject *callee = &origCallee->getValue().toObject(); if (!callee->isFunction()) return Compile_InlineAbort; /* * The callee must have the same parent as the script's global, otherwise * inference may not have accounted for any side effects correctly. */ if (!globalObj || globalObj != &callee->global()) return Compile_InlineAbort; Native native = callee->toFunction()->maybeNative(); if (!native) return Compile_InlineAbort; JSValueType type = knownPushedType(0); JSValueType thisType = thisValue->isTypeKnown() ? thisValue->getKnownType() : JSVAL_TYPE_UNKNOWN; /* * Note: when adding new natives which operate on properties, add relevant * constraint generation to the behavior of TypeConstraintCall. */ /* Handle natives that can be called either with or without 'new'. */ if (native == js_Array && type == JSVAL_TYPE_OBJECT && globalObj) { if (argc == 0 || argc == 1) return compileArrayWithLength(argc); return compileArrayWithArgs(argc); } /* Remaining natives must not be called with 'new'. */ if (callingNew) return Compile_InlineAbort; if (native == js::num_parseInt && argc >= 1) { FrameEntry *arg = frame.peek(-(int32_t)argc); JSValueType argType = arg->isTypeKnown() ? arg->getKnownType() : JSVAL_TYPE_UNKNOWN; if ((argType == JSVAL_TYPE_DOUBLE || argType == JSVAL_TYPE_INT32) && type == JSVAL_TYPE_INT32) { return compileParseInt(argType, argc); } } if (argc == 0) { if ((native == js::array_pop || native == js::array_shift) && thisType == JSVAL_TYPE_OBJECT) { /* * Only handle pop/shift on dense arrays which have never been used * in an iterator --- when popping elements we don't account for * suppressing deleted properties in active iterators. * * Constraints propagating properties directly into the result * type set are generated by TypeConstraintCall during inference. */ if (!thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY | types::OBJECT_FLAG_ITERATED) && !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) { bool packed = !thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY); return compileArrayPopShift(thisValue, packed, native == js::array_pop); } } } else if (argc == 1) { FrameEntry *arg = frame.peek(-1); types::TypeSet *argTypes = frame.extra(arg).types; if (!argTypes) return Compile_InlineAbort; JSValueType argType = arg->isTypeKnown() ? arg->getKnownType() : JSVAL_TYPE_UNKNOWN; if (native == js_math_abs) { if (argType == JSVAL_TYPE_INT32 && type == JSVAL_TYPE_INT32) return compileMathAbsInt(arg); if (argType == JSVAL_TYPE_DOUBLE && type == JSVAL_TYPE_DOUBLE) return compileMathAbsDouble(arg); } if (native == js_math_floor && argType == JSVAL_TYPE_DOUBLE && type == JSVAL_TYPE_INT32) { return compileRound(arg, Floor); } if (native == js_math_round && argType == JSVAL_TYPE_DOUBLE && type == JSVAL_TYPE_INT32) { return compileRound(arg, Round); } if (native == js_math_sqrt && type == JSVAL_TYPE_DOUBLE && masm.supportsFloatingPointSqrt() && (argType == JSVAL_TYPE_INT32 || argType == JSVAL_TYPE_DOUBLE)) { return compileMathSqrt(arg); } if (native == js_str_charCodeAt && argType == JSVAL_TYPE_INT32 && thisType == JSVAL_TYPE_STRING && type == JSVAL_TYPE_INT32) { return compileGetChar(thisValue, arg, GetCharCode); } if (native == js_str_charAt && argType == JSVAL_TYPE_INT32 && thisType == JSVAL_TYPE_STRING && type == JSVAL_TYPE_STRING) { return compileGetChar(thisValue, arg, GetChar); } if (native == js::str_fromCharCode && argType == JSVAL_TYPE_INT32 && type == JSVAL_TYPE_STRING) { return compileStringFromCode(arg); } if (native == js::array_push && thisType == JSVAL_TYPE_OBJECT && type == JSVAL_TYPE_INT32) { /* * Constraints propagating properties into the 'this' object are * generated by TypeConstraintCall during inference. */ if (!thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) && !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) { return compileArrayPush(thisValue, arg); } } if (native == js::array_concat && argType == JSVAL_TYPE_OBJECT && thisType == JSVAL_TYPE_OBJECT && type == JSVAL_TYPE_OBJECT && !thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) && !argTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) && !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) { return compileArrayConcat(thisTypes, argTypes, thisValue, arg); } } else if (argc == 2) { FrameEntry *arg1 = frame.peek(-2); FrameEntry *arg2 = frame.peek(-1); JSValueType arg1Type = arg1->isTypeKnown() ? arg1->getKnownType() : JSVAL_TYPE_UNKNOWN; JSValueType arg2Type = arg2->isTypeKnown() ? arg2->getKnownType() : JSVAL_TYPE_UNKNOWN; if (native == js_math_pow && type == JSVAL_TYPE_DOUBLE && masm.supportsFloatingPointSqrt() && (arg1Type == JSVAL_TYPE_DOUBLE || arg1Type == JSVAL_TYPE_INT32) && arg2Type == JSVAL_TYPE_DOUBLE && arg2->isConstant()) { Value arg2Value = arg2->getValue(); if (arg2Value.toDouble() == -0.5 || arg2Value.toDouble() == 0.5) return compileMathPowSimple(arg1, arg2); } if ((native == js_math_min || native == js_math_max)) { if (arg1Type == JSVAL_TYPE_INT32 && arg2Type == JSVAL_TYPE_INT32 && type == JSVAL_TYPE_INT32) { return compileMathMinMaxInt(arg1, arg2, native == js_math_min ? Assembler::LessThan : Assembler::GreaterThan); } if ((arg1Type == JSVAL_TYPE_INT32 || arg1Type == JSVAL_TYPE_DOUBLE) && (arg2Type == JSVAL_TYPE_INT32 || arg2Type == JSVAL_TYPE_DOUBLE) && type == JSVAL_TYPE_DOUBLE) { return compileMathMinMaxDouble(arg1, arg2, (native == js_math_min) ? Assembler::DoubleLessThan : Assembler::DoubleGreaterThan); } } } return Compile_InlineAbort; }
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 */, SourceCompressionTask *extraSct /* = NULL */) { RootedString source(cx, source_); SkipRoot skip(cx, &chars); #if JS_TRACE_LOGGING js::AutoTraceLog logger(js::TraceLogging::defaultLogger(), js::TraceLogging::PARSER_COMPILE_SCRIPT_START, js::TraceLogging::PARSER_COMPILE_SCRIPT_STOP, options); #endif 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; RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss)); if (!sourceObject) return NULL; SourceCompressionTask mysct(cx); SourceCompressionTask *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; } 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(parser.tokenStream)) 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, fun->generatorKind()); 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(parser.tokenStream)) 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; /* * 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 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 { 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; }
PropertyCacheEntry * PropertyCache::fill(JSContext *cx, JSObject *obj, JSObject *pobj, Shape *shape) { JS_ASSERT(this == &JS_PROPERTY_CACHE(cx)); JS_ASSERT(!cx->runtime->isHeapBusy()); /* * Check for overdeep scope and prototype chain. Because resolve, getter, * and setter hooks can change the prototype chain using JS_SetPrototype * after LookupPropertyWithFlags has returned, we calculate the protoIndex * here and not in LookupPropertyWithFlags. */ JSObject *tmp = obj; unsigned protoIndex = 0; while (tmp != pobj) { /* * Don't cache entries across prototype lookups which can mutate in * arbitrary ways without a shape change. */ if (tmp->hasUncacheableProto()) { PCMETER(noprotos++); return JS_NO_PROP_CACHE_FILL; } tmp = tmp->getProto(); /* * We cannot cache properties coming from native objects behind * non-native ones on the prototype chain. The non-natives can * mutate in arbitrary way without changing any shapes. */ if (!tmp || !tmp->isNative()) { PCMETER(noprotos++); return JS_NO_PROP_CACHE_FILL; } ++protoIndex; } typedef PropertyCacheEntry Entry; if (protoIndex > Entry::MaxProtoIndex) { PCMETER(longchains++); return JS_NO_PROP_CACHE_FILL; } /* * Optimize the cached vword based on our parameters and the current pc's * opcode format flags. */ jsbytecode *pc; (void) cx->stack.currentScript(&pc); JSOp op = JSOp(*pc); const JSCodeSpec *cs = &js_CodeSpec[op]; if ((cs->format & JOF_SET) && obj->watched()) return JS_NO_PROP_CACHE_FILL; if (obj == pobj) { JS_ASSERT(protoIndex == 0); } else { JS_ASSERT(protoIndex != 0); JS_ASSERT((protoIndex == 1) == (obj->getProto() == pobj)); if (protoIndex != 1) { /* * Make sure that a later shadowing assignment will enter * PurgeProtoChain and invalidate this entry, bug 479198. */ if (!obj->isDelegate()) return JS_NO_PROP_CACHE_FILL; } } PropertyCacheEntry *entry = &table[hash(pc, obj->lastProperty())]; PCMETER(entry->vword.isNull() || recycles++); entry->assign(pc, obj->lastProperty(), pobj->lastProperty(), shape, protoIndex); empty = false; PCMETER(fills++); /* * The modfills counter is not exact. It increases if a getter or setter * recurse into the interpreter. */ PCMETER(entry == pctestentry || modfills++); PCMETER(pctestentry = NULL); return entry; }
/* * Called from the JSOP_GENERATOR case in the interpreter, with fp referring * to the frame by which the generator function was activated. Create a new * JSGenerator object, which contains its own JSStackFrame that we populate * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return * from the activation in fp, so we can steal away fp->callobj and fp->argsobj * if they are non-null. */ JS_REQUIRES_STACK JSObject * js_NewGenerator(JSContext *cx) { JSObject *obj; uintN argc, nargs, nslots; JSGenerator *gen; jsval *slots; obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL); if (!obj) return NULL; /* Load and compute stack slot counts. */ JSStackFrame *fp = cx->fp; argc = fp->argc; nargs = JS_MAX(argc, fp->fun->nargs); nslots = 2 + nargs + fp->script->nslots; /* Allocate obj's private data struct. */ gen = (JSGenerator *) cx->malloc(sizeof(JSGenerator) + (nslots - 1) * sizeof(jsval)); if (!gen) return NULL; gen->obj = obj; /* Steal away objects reflecting fp and point them at gen->frame. */ gen->frame.callobj = fp->callobj; if (fp->callobj) { fp->callobj->setPrivate(&gen->frame); fp->callobj = NULL; } gen->frame.argsobj = fp->argsobj; if (fp->argsobj) { JSVAL_TO_OBJECT(fp->argsobj)->setPrivate(&gen->frame); fp->argsobj = NULL; } /* These two references can be shared with fp until it goes away. */ gen->frame.thisv = fp->thisv; /* Copy call-invariant script and function references. */ gen->frame.script = fp->script; gen->frame.fun = fp->fun; /* Use slots to carve space out of gen->slots. */ slots = gen->slots; gen->arena.next = NULL; gen->arena.base = (jsuword) slots; gen->arena.limit = gen->arena.avail = (jsuword) (slots + nslots); /* Copy rval, argv and vars. */ gen->frame.rval = fp->rval; memcpy(slots, fp->argv - 2, (2 + nargs) * sizeof(jsval)); gen->frame.argc = fp->argc; gen->frame.argv = slots + 2; slots += 2 + nargs; memcpy(slots, fp->slots, fp->script->nfixed * sizeof(jsval)); /* Initialize or copy virtual machine state. */ gen->frame.down = NULL; gen->frame.annotation = NULL; gen->frame.scopeChain = fp->scopeChain; gen->frame.imacpc = NULL; gen->frame.slots = slots; JS_ASSERT(StackBase(fp) == fp->regs->sp); gen->savedRegs.sp = slots + fp->script->nfixed; gen->savedRegs.pc = fp->regs->pc; gen->frame.regs = &gen->savedRegs; gen->frame.flags = (fp->flags & ~JSFRAME_ROOTED_ARGV) | JSFRAME_GENERATOR; /* JSOP_GENERATOR appears in the prologue, outside all blocks. */ JS_ASSERT(!fp->blockChain); gen->frame.blockChain = NULL; /* Note that gen is newborn. */ gen->state = JSGEN_NEWBORN; obj->setPrivate(gen); return obj; }
static bool Snapshot(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector *props) { /* * FIXME: Bug 575997 - We won't need to initialize this hash table if * (flags & JSITER_OWNONLY) when we eliminate inheritance of * shared-permanent properties as own properties. */ IdSet ht(cx); if (!ht.init(32)) return NULL; JSObject *pobj = obj; do { Class *clasp = pobj->getClass(); if (pobj->isNative() && !pobj->getOps()->enumerate && !(clasp->flags & JSCLASS_NEW_ENUMERATE)) { if (!clasp->enumerate(cx, pobj)) return false; if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props)) return false; } else if (pobj->isDenseArray()) { if (!EnumerateDenseArrayProperties(cx, obj, pobj, flags, ht, props)) return false; } else { if (pobj->isProxy()) { AutoIdVector proxyProps(cx); if (flags & JSITER_OWNONLY) { if (flags & JSITER_HIDDEN) { if (!JSProxy::getOwnPropertyNames(cx, pobj, proxyProps)) return false; } else { if (!JSProxy::keys(cx, pobj, proxyProps)) return false; } } else { if (!JSProxy::enumerate(cx, pobj, proxyProps)) return false; } for (size_t n = 0, len = proxyProps.length(); n < len; n++) { if (!Enumerate(cx, obj, pobj, proxyProps[n], true, false, flags, ht, props)) return false; } /* Proxy objects enumerate the prototype on their own, so we are done here. */ break; } Value state; JSIterateOp op = (flags & JSITER_HIDDEN) ? JSENUMERATE_INIT_ALL : JSENUMERATE_INIT; if (!pobj->enumerate(cx, op, &state, NULL)) return false; if (state.isMagic(JS_NATIVE_ENUMERATE)) { if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props)) return false; } else { while (true) { jsid id; if (!pobj->enumerate(cx, JSENUMERATE_NEXT, &state, &id)) return false; if (state.isNull()) break; if (!Enumerate(cx, obj, pobj, id, true, false, flags, ht, props)) return false; } } } if (JS_UNLIKELY(pobj->isXML())) break; } while ((pobj = pobj->getProto()) != NULL); return true; }
void setDOMException(ExecState* exec, ExceptionCode ec) { if (ec == 0 || exec->hadException()) return; const char* type = "DOM"; int code = ec; const char* const* nameTable; int nameTableSize; int nameIndex; if (code >= RangeExceptionOffset && code <= RangeExceptionMax) { type = "DOM Range"; code -= RangeExceptionOffset; nameIndex = code; nameTable = rangeExceptionNames; nameTableSize = sizeof(rangeExceptionNames) / sizeof(rangeExceptionNames[0]); } else if (code >= EventExceptionOffset && code <= EventExceptionMax) { type = "DOM Events"; code -= EventExceptionOffset; nameIndex = code; nameTable = eventExceptionNames; nameTableSize = sizeof(eventExceptionNames) / sizeof(eventExceptionNames[0]); } else if (code == XMLHttpRequestExceptionOffset) { // FIXME: this exception should be replaced with DOM SECURITY_ERR when it finds its way to the spec. throwError(exec, GeneralError, "Permission denied"); return; } else if (code > XMLHttpRequestExceptionOffset && code <= XMLHttpRequestExceptionMax) { type = "XMLHttpRequest"; // XMLHttpRequest exception codes start with 101 and we don't want 100 empty elements in the name array nameIndex = code - NETWORK_ERR; code -= XMLHttpRequestExceptionOffset; nameTable = xmlHttpRequestExceptionNames; nameTableSize = sizeof(xmlHttpRequestExceptionNames) / sizeof(xmlHttpRequestExceptionNames[0]); #ifdef XPATH_SUPPORT } else if (code >= XPathExceptionOffset && code <= XPathExceptionMax) { type = "DOM XPath"; // XPath exception codes start with 51 and we don't want 51 empty elements in the name array nameIndex = code - INVALID_EXPRESSION_ERR; code -= XPathExceptionOffset; nameTable = xpathExceptionNames; nameTableSize = sizeof(xpathExceptionNames) / sizeof(xpathExceptionNames[0]); #endif #ifdef SVG_SUPPORT } else if (code >= SVGExceptionOffset && code <= SVGExceptionMax) { type = "DOM SVG"; code -= SVGExceptionOffset; nameIndex = code; nameTable = svgExceptionNames; nameTableSize = sizeof(svgExceptionNames) / sizeof(svgExceptionNames[0]); #endif } else { nameIndex = code; nameTable = exceptionNames; nameTableSize = sizeof(exceptionNames) / sizeof(exceptionNames[0]); } const char* name = (nameIndex < nameTableSize && nameIndex >= 0) ? nameTable[nameIndex] : 0; // 100 characters is a big enough buffer, because there are: // 13 characters in the message // 10 characters in the longest type, "DOM Events" // 27 characters in the longest name, "NO_MODIFICATION_ALLOWED_ERR" // 20 or so digits in the longest integer's ASCII form (even if int is 64-bit) // 1 byte for a null character // That adds up to about 70 bytes. char buffer[100]; if (name) sprintf(buffer, "%s: %s Exception %d", name, type, code); else sprintf(buffer, "%s Exception %d", type, code); JSObject* errorObject = throwError(exec, GeneralError, buffer); errorObject->put(exec, "code", jsNumber(code)); }
JSValue JSHTMLCanvasElement::getContext(ExecState* exec) { HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(impl()); const UString& contextId = exec->argument(0).toString(exec)->value(exec); RefPtr<CanvasContextAttributes> attrs; #if ENABLE(WEBGL) if (contextId == "experimental-webgl" || contextId == "webkit-3d") { attrs = WebGLContextAttributes::create(); WebGLContextAttributes* webGLAttrs = static_cast<WebGLContextAttributes*>(attrs.get()); if (exec->argumentCount() > 1 && exec->argument(1).isObject()) { JSObject* jsAttrs = exec->argument(1).getObject(); Identifier alpha(exec, "alpha"); if (jsAttrs->hasProperty(exec, alpha)) webGLAttrs->setAlpha(jsAttrs->get(exec, alpha).toBoolean()); Identifier depth(exec, "depth"); if (jsAttrs->hasProperty(exec, depth)) webGLAttrs->setDepth(jsAttrs->get(exec, depth).toBoolean()); Identifier stencil(exec, "stencil"); if (jsAttrs->hasProperty(exec, stencil)) webGLAttrs->setStencil(jsAttrs->get(exec, stencil).toBoolean()); Identifier antialias(exec, "antialias"); if (jsAttrs->hasProperty(exec, antialias)) webGLAttrs->setAntialias(jsAttrs->get(exec, antialias).toBoolean()); Identifier premultipliedAlpha(exec, "premultipliedAlpha"); if (jsAttrs->hasProperty(exec, premultipliedAlpha)) webGLAttrs->setPremultipliedAlpha(jsAttrs->get(exec, premultipliedAlpha).toBoolean()); Identifier preserveDrawingBuffer(exec, "preserveDrawingBuffer"); if (jsAttrs->hasProperty(exec, preserveDrawingBuffer)) webGLAttrs->setPreserveDrawingBuffer(jsAttrs->get(exec, preserveDrawingBuffer).toBoolean()); } } #endif CanvasRenderingContext* context = canvas->getContext(ustringToString(contextId), attrs.get()); if (!context) return jsNull(); return toJS(exec, globalObject(), WTF::getPtr(context)); }
bool DirectProxyHandler::isCallable(JSObject* obj) const { JSObject * target = obj->as<ProxyObject>().target(); return target->isCallable(); }
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->isArray()) 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; jit::SizeOfBaselineData(script, rtStats->mallocSizeOf_, &baselineData, &baselineStubsFallback); cStats->baselineData += baselineData; cStats->baselineStubsFallback += baselineStubsFallback; cStats->ionData += jit::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; }
bool DefineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript* script) { Root<JSScript*> root(cx, &script); HandleObject globalObj = globalScope.globalObj; /* Define and update global properties. */ for (size_t i = 0; i < globalScope.defs.length(); i++) { GlobalScope::GlobalDef &def = globalScope.defs[i]; /* Names that could be resolved ahead of time can be skipped. */ if (!def.atom) continue; jsid id = ATOM_TO_JSID(def.atom); Value rval; if (def.funbox) { JSFunction *fun = def.funbox->function(); /* * No need to check for redeclarations or anything, global * optimizations only take place if the property is not defined. */ rval.setObject(*fun); types::AddTypePropertyId(cx, globalObj, id, rval); } else { rval.setUndefined(); } /* * Don't update the type information when defining the property for the * global object, per the consistency rules for type properties. If the * property is only undefined before it is ever written, we can check * the global directly during compilation and avoid having to emit type * checks every time it is accessed in the script. */ const Shape *shape = DefineNativeProperty(cx, globalObj, id, rval, JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE | JSPROP_PERMANENT, 0, 0, DNP_SKIP_TYPE); if (!shape) return false; def.knownSlot = shape->slot(); } Vector<JSScript *, 16> worklist(cx); if (!worklist.append(script)) return false; /* * Recursively walk through all scripts we just compiled. For each script, * go through all global uses. Each global use indexes into globalScope->defs. * Use this information to repoint each use to the correct slot in the global * object. */ while (worklist.length()) { JSScript *outer = worklist.back(); worklist.popBack(); if (JSScript::isValidOffset(outer->objectsOffset)) { JSObjectArray *arr = outer->objects(); /* * If this is an eval script, don't treat the saved caller function * stored in the first object slot as an inner function. */ size_t start = outer->savedCallerFun ? 1 : 0; for (size_t i = start; i < arr->length; i++) { JSObject *obj = arr->vector[i]; if (!obj->isFunction()) continue; JSFunction *fun = obj->toFunction(); JS_ASSERT(fun->isInterpreted()); JSScript *inner = fun->script(); if (outer->function() && outer->function()->isHeavyweight()) { outer->isOuterFunction = true; inner->isInnerFunction = true; } if (!JSScript::isValidOffset(inner->globalsOffset) && !JSScript::isValidOffset(inner->objectsOffset)) { continue; } if (!worklist.append(inner)) return false; } } if (!JSScript::isValidOffset(outer->globalsOffset)) continue; GlobalSlotArray *globalUses = outer->globals(); uint32_t nGlobalUses = globalUses->length; for (uint32_t i = 0; i < nGlobalUses; i++) { uint32_t index = globalUses->vector[i].slot; JS_ASSERT(index < globalScope.defs.length()); globalUses->vector[i].slot = globalScope.defs[index].knownSlot; } } return true; }
JSValue JSSQLTransaction::executeSql(ExecState* exec) { if (!exec->argumentCount()) { setDOMException(exec, SYNTAX_ERR); return jsUndefined(); } String sqlStatement = ustringToString(exec->argument(0).toString(exec)); if (exec->hadException()) return jsUndefined(); // Now assemble the list of SQL arguments Vector<SQLValue> sqlValues; if (!exec->argument(1).isUndefinedOrNull()) { JSObject* object = exec->argument(1).getObject(); if (!object) { setDOMException(exec, TYPE_MISMATCH_ERR); return jsUndefined(); } JSValue lengthValue = object->get(exec, exec->propertyNames().length); if (exec->hadException()) return jsUndefined(); unsigned length = lengthValue.toUInt32(exec); if (exec->hadException()) return jsUndefined(); for (unsigned i = 0 ; i < length; ++i) { JSValue value = object->get(exec, i); if (exec->hadException()) return jsUndefined(); if (value.isUndefinedOrNull()) sqlValues.append(SQLValue()); else if (value.isNumber()) sqlValues.append(value.uncheckedGetNumber()); else { // Convert the argument to a string and append it sqlValues.append(ustringToString(value.toString(exec))); if (exec->hadException()) return jsUndefined(); } } } RefPtr<SQLStatementCallback> callback; if (!exec->argument(2).isUndefinedOrNull()) { JSObject* object = exec->argument(2).getObject(); if (!object) { setDOMException(exec, TYPE_MISMATCH_ERR); return jsUndefined(); } callback = JSSQLStatementCallback::create(object, static_cast<JSDOMGlobalObject*>(globalObject())); } RefPtr<SQLStatementErrorCallback> errorCallback; if (!exec->argument(3).isUndefinedOrNull()) { JSObject* object = exec->argument(3).getObject(); if (!object) { setDOMException(exec, TYPE_MISMATCH_ERR); return jsUndefined(); } errorCallback = JSSQLStatementErrorCallback::create(object, static_cast<JSDOMGlobalObject*>(globalObject())); } ExceptionCode ec = 0; m_impl->executeSQL(sqlStatement, sqlValues, callback.release(), errorCallback.release(), ec); setDOMException(exec, ec); return jsUndefined(); }
JSValue IrcExtension::Handle( const std::string &functionName, const JSArray &args) { // connect(channelName) if (functionName == "connect") { // were in the middle of something if (irc_is_connected(session)) { return JSValue::Undefined(); } if (hThread) { // thread finished? if (WaitForSingleObject(hThread, 0) == WAIT_OBJECT_0) { CloseHandle(hThread); hThread = NULL; } else { // thread is still running return JSValue::Undefined(); } } if (args.size() < 1 || !args[0].IsString()) { // TODO: log this and exit return JSValue::Undefined(); } channelName = ToString(args[0].ToString()); serverName = channelName + ".jtvirc.com"; port = 6667; if (args.size() >= 2) { if (!(args[1].IsString())) { // log error return JSValue::Undefined(); } serverName = ToString(args[1].ToString()); } if (args.size() == 3) { if (!args[2].IsInteger()) { // log error return JSValue::Undefined(); } port = (unsigned int)args[2].ToInteger(); } isJoinedChannel = false; hThread = (HANDLE)_beginthreadex(NULL, 0, &IrcThread, this, 0, NULL); return JSValue::Undefined(); } else if (functionName == "disconnect") { // disconnect() if (args.size() != 0) { // TODO: log invalid method call return JSValue::Undefined(); } irc_disconnect(session); if (hThread) { WaitForSingleObject( hThread, INFINITE ); CloseHandle(hThread); hThread = NULL; } isJoinedChannel = false; moderators.clear(); latestMessage.Clear(); EnterCriticalSection(&messageLock); messages.clear(); LeaveCriticalSection(&messageLock); return JSValue::Undefined(); } else if (functionName == "getMessages") { // [message, message, ..] getMessages() EnterCriticalSection(&messageLock); if (args.size() != 0) { // TODO: log invalid method call return JSValue::Undefined(); } JSArray returnArgs; while(messages.size()) { IrcMessage &message = messages[0]; JSObject newMessageObject; newMessageObject.SetProperty(WSLit("nickname"), ToWebString(message.username)); newMessageObject.SetProperty(WSLit("message"), ToWebString(message.message)); newMessageObject.SetProperty(WSLit("color"), ToWebString(message.color)); JSArray groups; for(auto i = message.groups.begin(); i != message.groups.end(); i++) { groups.Push(ToWebString(*i)); } newMessageObject.SetProperty(WSLit("groups"), groups); JSArray emoticons; for(auto i = message.emoticons.begin(); i != message.emoticons.end(); i++) { groups.Push(ToWebString(*i)); } newMessageObject.SetProperty(WSLit("emoticons"), emoticons); returnArgs.Push(newMessageObject); messages.erase(messages.begin()); } LeaveCriticalSection(&messageLock); return returnArgs; } else if (functionName == "isConnected") { return JSValue(irc_is_connected(session) && isJoinedChannel); } return JSValue::Undefined(); }
bool js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options, const jschar *chars, size_t length, HandleObject scopeChain, JS::OffThreadCompileCallback callback, void *callbackData) { // Suppress GC so that calls below do not trigger a new incremental GC // which could require barriers on the atoms compartment. gc::AutoSuppressGC suppress(cx); frontend::MaybeCallSourceHandler(cx, options, chars, length); if (!EnsureWorkerThreadsInitialized(cx)) return false; JS::CompartmentOptions compartmentOptions(cx->compartment()->options()); compartmentOptions.setZone(JS::FreshZone); JSObject *global = JS_NewGlobalObject(cx, &workerGlobalClass, nullptr, JS::FireOnNewGlobalHook, compartmentOptions); if (!global) return false; global->zone()->types.inferenceEnabled = cx->typeInferenceEnabled(); JS_SetCompartmentPrincipals(global->compartment(), cx->compartment()->principals); RootedObject obj(cx); // Initialize all classes needed for parsing while we are still on the main // thread. Do this for both the target and the new global so that prototype // pointers can be changed infallibly after parsing finishes. if (!js_GetClassObject(cx, cx->global(), JSProto_Function, &obj) || !js_GetClassObject(cx, cx->global(), JSProto_Array, &obj) || !js_GetClassObject(cx, cx->global(), JSProto_RegExp, &obj) || !js_GetClassObject(cx, cx->global(), JSProto_GeneratorFunction, &obj)) { return false; } { AutoCompartment ac(cx, global); if (!js_GetClassObject(cx, global, JSProto_Function, &obj) || !js_GetClassObject(cx, global, JSProto_Array, &obj) || !js_GetClassObject(cx, global, JSProto_RegExp, &obj) || !js_GetClassObject(cx, global, JSProto_GeneratorFunction, &obj)) { return false; } } ScopedJSDeletePtr<ExclusiveContext> workercx( cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData *) nullptr, ThreadSafeContext::Context_Exclusive)); if (!workercx) return false; ScopedJSDeletePtr<ParseTask> task( cx->new_<ParseTask>(workercx.get(), global, cx, chars, length, scopeChain, callback, callbackData)); if (!task) return false; workercx.forget(); if (!task->init(cx, options)) return false; WorkerThreadState &state = *cx->runtime()->workerThreadState; JS_ASSERT(state.numThreads); // Off thread parsing can't occur during incremental collections on the // atoms compartment, to avoid triggering barriers. (Outside the atoms // compartment, the compilation will use a new zone which doesn't require // barriers itself.) If an atoms-zone GC is in progress, hold off on // executing the parse task until the atoms-zone GC completes (see // EnqueuePendingParseTasksAfterGC). if (cx->runtime()->activeGCInAtomsZone()) { if (!state.parseWaitingOnGC.append(task.get())) return false; } else { task->activate(cx->runtime()); AutoLockWorkerThreadState lock(state); if (!state.parseWorklist.append(task.get())) return false; state.notifyAll(WorkerThreadState::PRODUCER); } task.forget(); return true; }
EncodedJSValue JSC_HOST_CALL constructJSHTMLElement(ExecState& exec) { VM& vm = exec.vm(); auto scope = DECLARE_THROW_SCOPE(vm); auto* jsConstructor = jsCast<DOMConstructorObject*>(exec.callee()); ASSERT(jsConstructor); auto* context = jsConstructor->scriptExecutionContext(); if (!context) return throwConstructorScriptExecutionContextUnavailableError(exec, scope, "HTMLElement"); ASSERT(context->isDocument()); JSValue newTargetValue = exec.thisValue(); auto* globalObject = jsConstructor->globalObject(); JSValue htmlElementConstructorValue = JSHTMLElement::getConstructor(vm, globalObject); if (newTargetValue == htmlElementConstructorValue) return throwVMTypeError(&exec, scope, ASCIILiteral("new.target is not a valid custom element constructor")); auto& document = downcast<Document>(*context); auto* window = document.domWindow(); if (!window) return throwVMTypeError(&exec, scope, ASCIILiteral("new.target is not a valid custom element constructor")); auto* registry = window->customElementRegistry(); if (!registry) return throwVMTypeError(&exec, scope, ASCIILiteral("new.target is not a valid custom element constructor")); JSObject* newTarget = newTargetValue.getObject(); auto* elementInterface = registry->findInterface(newTarget); if (!elementInterface) return throwVMTypeError(&exec, scope, ASCIILiteral("new.target does not define a custom element")); if (!elementInterface->isUpgradingElement()) { Structure* baseStructure = getDOMStructure<JSHTMLElement>(vm, *globalObject); auto* newElementStructure = InternalFunction::createSubclassStructure(&exec, newTargetValue, baseStructure); RETURN_IF_EXCEPTION(scope, encodedJSValue()); Ref<HTMLElement> element = HTMLElement::create(elementInterface->name(), document); element->setIsDefinedCustomElement(*elementInterface); auto* jsElement = JSHTMLElement::create(newElementStructure, globalObject, element.get()); cacheWrapper(globalObject->world(), element.ptr(), jsElement); return JSValue::encode(jsElement); } Element* elementToUpgrade = elementInterface->lastElementInConstructionStack(); if (!elementToUpgrade) { throwInvalidStateError(exec, scope, ASCIILiteral("Cannot instantiate a custom element inside its own constrcutor during upgrades")); return JSValue::encode(jsUndefined()); } JSValue elementWrapperValue = toJS(&exec, jsConstructor->globalObject(), *elementToUpgrade); ASSERT(elementWrapperValue.isObject()); JSValue newPrototype = newTarget->get(&exec, vm.propertyNames->prototype); RETURN_IF_EXCEPTION(scope, encodedJSValue()); JSObject* elementWrapperObject = asObject(elementWrapperValue); JSObject::setPrototype(elementWrapperObject, &exec, newPrototype, true /* shouldThrowIfCantSet */); RETURN_IF_EXCEPTION(scope, encodedJSValue()); elementInterface->didUpgradeLastElementInConstructionStack(); return JSValue::encode(elementWrapperValue); }
PropertyName * PropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp, PropertyCacheEntry *entry) { JSObject *obj, *pobj; JSScript *script = cx->stack.currentScript(); JS_ASSERT(this == &JS_PROPERTY_CACHE(cx)); JS_ASSERT(uint32_t(pc - script->code) < script->length); JSOp op = JSOp(*pc); obj = *objp; if (entry->kpc != pc) { PCMETER(kpcmisses++); PropertyName *name = GetNameFromBytecode(cx, script, pc, op); #ifdef DEBUG_notme JSAutoByteString printable; fprintf(stderr, "id miss for %s from %s:%u" " (pc %u, kpc %u, kshape %p, shape %p)\n", js_AtomToPrintableString(cx, name, &printable), script->filename, js_PCToLineNumber(cx, script, pc), pc - script->code, entry->kpc - script->code, entry->kshape, obj->lastProperty()); js_Disassemble1(cx, script, pc, pc - script->code, JS_FALSE, stderr); #endif return name; } if (entry->kshape != obj->lastProperty()) { PCMETER(kshapemisses++); return GetNameFromBytecode(cx, script, pc, op); } /* * PropertyCache::test handles only the direct and immediate-prototype hit * cases. All others go here. */ pobj = obj; uint8_t protoIndex = entry->protoIndex; while (protoIndex > 0) { JSObject *tmp = pobj->getProto(); if (!tmp || !tmp->isNative()) break; pobj = tmp; protoIndex--; } if (pobj->lastProperty() == entry->pshape) { #ifdef DEBUG PropertyName *name = GetNameFromBytecode(cx, script, pc, op); JS_ASSERT(pobj->nativeContainsNoAllocation(NameToId(name))); #endif *pobjp = pobj; return NULL; } PCMETER(vcapmisses++); return GetNameFromBytecode(cx, script, pc, op); }
bool JSCompartment::wrap(JSContext *cx, Value *vp) { JS_ASSERT(cx->compartment == this); uintN flags = 0; JS_CHECK_RECURSION(cx, return false); /* Only GC things have to be wrapped or copied. */ if (!vp->isMarkable()) return true; if (vp->isString()) { JSString *str = vp->toString(); /* If the string is already in this compartment, we are done. */ if (str->compartment() == this) return true; /* If the string is an atom, we don't have to copy. */ if (str->isAtom()) { JS_ASSERT(str->compartment() == cx->runtime->atomsCompartment); return true; } } /* * Wrappers should really be parented to the wrapped parent of the wrapped * object, but in that case a wrapped global object would have a NULL * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead, * we parent all wrappers to the global object in their home compartment. * This loses us some transparency, and is generally very cheesy. */ JSObject *global; if (cx->hasfp()) { global = &cx->fp()->scopeChain().global(); } else { global = JS_ObjectToInnerObject(cx, cx->globalObject); if (!global) return false; } /* Unwrap incoming objects. */ if (vp->isObject()) { JSObject *obj = &vp->toObject(); /* If the object is already in this compartment, we are done. */ if (obj->compartment() == this) return true; /* Translate StopIteration singleton. */ if (obj->isStopIteration()) return js_FindClassObject(cx, NULL, JSProto_StopIteration, vp); /* Don't unwrap an outer window proxy. */ if (!obj->getClass()->ext.innerObject) { obj = UnwrapObject(&vp->toObject(), true, &flags); vp->setObject(*obj); if (obj->compartment() == this) return true; if (cx->runtime->preWrapObjectCallback) { obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); if (!obj) return false; } vp->setObject(*obj); if (obj->compartment() == this) return true; } else { if (cx->runtime->preWrapObjectCallback) { obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); if (!obj) return false; } JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject); vp->setObject(*obj); } #ifdef DEBUG { JSObject *outer = obj; OBJ_TO_OUTER_OBJECT(cx, outer); JS_ASSERT(outer && outer == obj); } #endif } /* If we already have a wrapper for this value, use it. */ if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(*vp)) { *vp = p->value; if (vp->isObject()) { JSObject *obj = &vp->toObject(); JS_ASSERT(obj->isCrossCompartmentWrapper()); if (global->getClass() != &dummy_class && obj->getParent() != global) { do { if (!obj->setParent(cx, global)) return false; obj = obj->getProto(); } while (obj && obj->isCrossCompartmentWrapper()); } } return true; } if (vp->isString()) { Value orig = *vp; JSString *str = vp->toString(); const jschar *chars = str->getChars(cx); if (!chars) return false; JSString *wrapped = js_NewStringCopyN(cx, chars, str->length()); if (!wrapped) return false; vp->setString(wrapped); return crossCompartmentWrappers.put(orig, *vp); } JSObject *obj = &vp->toObject(); /* * Recurse to wrap the prototype. Long prototype chains will run out of * stack, causing an error in CHECK_RECURSE. * * Wrapping the proto before creating the new wrapper and adding it to the * cache helps avoid leaving a bad entry in the cache on OOM. But note that * if we wrapped both proto and parent, we would get infinite recursion * here (since Object.prototype->parent->proto leads to Object.prototype * itself). */ JSObject *proto = obj->getProto(); if (!wrap(cx, &proto)) return false; /* * We hand in the original wrapped object into the wrap hook to allow * the wrap hook to reason over what wrappers are currently applied * to the object. */ JSObject *wrapper = cx->runtime->wrapObjectCallback(cx, obj, proto, global, flags); if (!wrapper) return false; vp->setObject(*wrapper); if (wrapper->getProto() != proto && !SetProto(cx, wrapper, proto, false)) return false; if (!crossCompartmentWrappers.put(GetProxyPrivate(wrapper), *vp)) return false; if (!wrapper->setParent(cx, global)) return false; return true; }
CompileStatus mjit::Compiler::compileArrayConcat(types::TypeSet *thisTypes, types::TypeSet *argTypes, FrameEntry *thisValue, FrameEntry *argValue) { /* * Require the 'this' types to have a specific type matching the current * global, so we can create the result object inline. */ if (thisTypes->getObjectCount() != 1) return Compile_InlineAbort; types::TypeObject *thisType = thisTypes->getTypeObject(0); if (!thisType || &thisType->proto->global() != globalObj) return Compile_InlineAbort; /* * Constraints modeling this concat have not been generated by inference, * so check that type information already reflects possible side effects of * this call. */ thisTypes->addFreeze(cx); argTypes->addFreeze(cx); types::TypeSet *thisElemTypes = thisType->getProperty(cx, JSID_VOID, false); if (!thisElemTypes) return Compile_Error; if (!pushedTypeSet(0)->hasType(types::Type::ObjectType(thisType))) return Compile_InlineAbort; for (unsigned i = 0; i < argTypes->getObjectCount(); i++) { if (argTypes->getSingleObject(i)) return Compile_InlineAbort; types::TypeObject *argType = argTypes->getTypeObject(i); if (!argType) continue; types::TypeSet *elemTypes = argType->getProperty(cx, JSID_VOID, false); if (!elemTypes) return Compile_Error; if (!elemTypes->knownSubset(cx, thisElemTypes)) return Compile_InlineAbort; } /* Test for 'length == initializedLength' on both arrays. */ RegisterID slotsReg = frame.allocReg(); RegisterID reg = frame.allocReg(); Int32Key key = Int32Key::FromRegister(reg); RegisterID objReg = frame.tempRegForData(thisValue); masm.loadPtr(Address(objReg, JSObject::offsetOfElements()), slotsReg); masm.load32(Address(slotsReg, ObjectElements::offsetOfLength()), reg); Jump initlenOneGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(), slotsReg, key, Assembler::NotEqual); stubcc.linkExit(initlenOneGuard, Uses(3)); objReg = frame.tempRegForData(argValue); masm.loadPtr(Address(objReg, JSObject::offsetOfElements()), slotsReg); masm.load32(Address(slotsReg, ObjectElements::offsetOfLength()), reg); Jump initlenTwoGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(), slotsReg, key, Assembler::NotEqual); stubcc.linkExit(initlenTwoGuard, Uses(3)); frame.freeReg(reg); frame.freeReg(slotsReg); frame.syncAndForgetEverything(); /* * The current stack layout is 'CALLEE THIS ARG'. Allocate the result and * scribble it over the callee, which will be its final position after the * call. */ JSObject *templateObject = NewDenseEmptyArray(cx, thisType->proto); if (!templateObject) return Compile_Error; templateObject->setType(thisType); RegisterID result = Registers::ReturnReg; Jump emptyFreeList = getNewObject(cx, result, templateObject); stubcc.linkExit(emptyFreeList, Uses(3)); masm.storeValueFromComponents(ImmType(JSVAL_TYPE_OBJECT), result, frame.addressOf(frame.peek(-3))); INLINE_STUBCALL(stubs::ArrayConcatTwoArrays, REJOIN_FALLTHROUGH); stubcc.leave(); stubcc.masm.move(Imm32(1), Registers::ArgReg1); OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH); frame.popn(3); frame.pushSynced(JSVAL_TYPE_OBJECT); stubcc.rejoin(Changes(1)); return Compile_Okay; }
JSBool WrapObject(JSContext *cx, JSObject *scope, jsval v, jsval *vp) { // This might be redundant if called from XPC_SJOW_Construct, but it should // be cheap in that case. JSObject *objToWrap = UnsafeUnwrapSecurityWrapper(cx, JSVAL_TO_OBJECT(v)); if (!objToWrap || JS_TypeOfValue(cx, OBJECT_TO_JSVAL(objToWrap)) == JSTYPE_XML) { return ThrowException(NS_ERROR_INVALID_ARG, cx); } // Prevent script created Script objects from ever being wrapped // with XPCSafeJSObjectWrapper, and never let the eval function // object be directly wrapped. if (objToWrap->getClass() == &js_ScriptClass || (JS_ObjectIsFunction(cx, objToWrap) && JS_GetFunctionFastNative(cx, JS_ValueToFunction(cx, v)) == XPCWrapper::sEvalNative)) { return ThrowException(NS_ERROR_INVALID_ARG, cx); } XPCWrappedNativeScope *xpcscope = XPCWrappedNativeScope::FindInJSObjectScope(cx, scope); NS_ASSERTION(xpcscope, "what crazy scope are we in?"); XPCWrappedNative *wrappedNative; WrapperType type = xpcscope->GetWrapperFor(cx, objToWrap, SJOW, &wrappedNative); // NB: We allow XOW here because we're as restrictive as it is (and we know // we're same origin here). if (type != NONE && type != XOW && !(type & SJOW)) { return ThrowException(NS_ERROR_INVALID_ARG, cx); } SLIM_LOG_WILL_MORPH(cx, objToWrap); if (IS_SLIM_WRAPPER(objToWrap) && !MorphSlimWrapper(cx, objToWrap)) { return ThrowException(NS_ERROR_FAILURE, cx); } XPCWrappedNative *wn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, objToWrap); if (wn) { CheckWindow(wn); } JSObject *wrapperObj = JS_NewObjectWithGivenProto(cx, js::Jsvalify(&SJOWClass), nsnull, scope); if (!wrapperObj) { // JS_NewObjectWithGivenProto already threw. return JS_FALSE; } *vp = OBJECT_TO_JSVAL(wrapperObj); if (!JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sWrappedObjSlot, OBJECT_TO_JSVAL(objToWrap)) || !JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sFlagsSlot, JSVAL_ZERO)) { return JS_FALSE; } return JS_TRUE; }
static JSBool fun_getProperty(JSContext *cx, HandleObject obj_, HandleId id, Value *vp) { JSObject *obj = obj_; while (!obj->isFunction()) { obj = obj->getProto(); if (!obj) return true; } JSFunction *fun = obj->toFunction(); /* * Mark the function's script as uninlineable, to expand any of its * frames on the stack before we go looking for them. This allows the * below walk to only check each explicit frame rather than needing to * check any calls that were inlined. */ if (fun->isInterpreted()) { fun->script()->uninlineable = true; MarkTypeObjectFlags(cx, fun, OBJECT_FLAG_UNINLINEABLE); } /* Set to early to null in case of error */ vp->setNull(); /* Find fun's top-most activation record. */ StackIter iter(cx); for (; !iter.done(); ++iter) { if (!iter.isFunctionFrame() || iter.isEvalFrame()) continue; if (iter.callee() == fun) break; } if (iter.done()) return true; StackFrame *fp = iter.fp(); if (JSID_IS_ATOM(id, cx->runtime->atomState.argumentsAtom)) { if (fun->hasRest()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_FUNCTION_ARGUMENTS_AND_REST); return false; } /* Warn if strict about f.arguments or equivalent unqualified uses. */ if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage, NULL, JSMSG_DEPRECATED_USAGE, js_arguments_str)) { return false; } ArgumentsObject *argsobj = ArgumentsObject::createUnexpected(cx, fp); if (!argsobj) return false; *vp = ObjectValue(*argsobj); return true; } #ifdef JS_METHODJIT if (JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom) && fp && fp->prev()) { /* * If the frame was called from within an inlined frame, mark the * innermost function as uninlineable to expand its frame and allow us * to recover its callee object. */ InlinedSite *inlined; jsbytecode *prevpc = fp->prevpc(&inlined); if (inlined) { mjit::JITChunk *chunk = fp->prev()->jit()->chunk(prevpc); JSFunction *fun = chunk->inlineFrames()[inlined->inlineIndex].fun; fun->script()->uninlineable = true; MarkTypeObjectFlags(cx, fun, OBJECT_FLAG_UNINLINEABLE); } } #endif if (JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) { ++iter; if (iter.done() || !iter.isFunctionFrame()) { JS_ASSERT(vp->isNull()); return true; } *vp = iter.calleev(); /* Censor the caller if it is from another compartment. */ JSObject &caller = vp->toObject(); if (caller.compartment() != cx->compartment) { vp->setNull(); } else if (caller.isFunction()) { JSFunction *callerFun = caller.toFunction(); if (callerFun->isInterpreted() && callerFun->inStrictMode()) { JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, JSMSG_CALLER_IS_STRICT); return false; } } return true; } JS_NOT_REACHED("fun_getProperty"); return false; }
// Wrap a JS value in a safe wrapper of a function wrapper if // needed. Note that rval must point to something rooted when calling // this function. static JSBool WrapJSValue(JSContext *cx, JSObject *obj, jsval val, jsval *rval) { JSBool ok = JS_TRUE; if (JSVAL_IS_PRIMITIVE(val)) { *rval = val; } else { if (!RewrapObject(cx, obj->getParent(), JSVAL_TO_OBJECT(val), SJOW, rval)) { return JS_FALSE; } // Construct a new safe wrapper. Note that it doesn't matter what // parent we pass in here, the construct hook will ensure we get // the right parent for the wrapper. JSObject *safeObj = JSVAL_TO_OBJECT(*rval); if (safeObj->getClass() == &SJOWClass && JS_GetGlobalForObject(cx, obj) != JS_GetGlobalForObject(cx, safeObj)) { // Check to see if the new object we just wrapped is accessible // from the unsafe object we got the new object through. If not, // force the new wrapper to use the principal of the unsafe // object we got the new object from. nsCOMPtr<nsIPrincipal> srcObjPrincipal; nsCOMPtr<nsIPrincipal> subjPrincipal; nsCOMPtr<nsIPrincipal> valObjPrincipal; nsresult rv = FindPrincipals(cx, obj, getter_AddRefs(srcObjPrincipal), getter_AddRefs(subjPrincipal), nsnull); if (NS_FAILED(rv)) { return ThrowException(rv, cx); } rv = FindPrincipals(cx, JSVAL_TO_OBJECT(val), getter_AddRefs(valObjPrincipal), nsnull, nsnull); if (NS_FAILED(rv)) { return ThrowException(rv, cx); } PRBool subsumes = PR_FALSE; rv = srcObjPrincipal->Subsumes(valObjPrincipal, &subsumes); if (NS_FAILED(rv)) { return ThrowException(rv, cx); } // If the subject can access both the source and object principals, then // don't bother forcing the principal below. if (!subsumes && subjPrincipal) { PRBool subjSubsumes = PR_FALSE; rv = subjPrincipal->Subsumes(srcObjPrincipal, &subjSubsumes); if (NS_SUCCEEDED(rv) && subjSubsumes) { rv = subjPrincipal->Subsumes(valObjPrincipal, &subjSubsumes); if (NS_SUCCEEDED(rv) && subjSubsumes) { subsumes = PR_TRUE; } } } if (!subsumes) { // The unsafe object we got the new object from can not access // the new object, force the wrapper we just created to use // the principal of the unsafe object to prevent users of the // new object wrapper from evaluating code through the new // wrapper with the principal of the new object. if (!::JS_SetReservedSlot(cx, safeObj, sPrincipalSlot, PRIVATE_TO_JSVAL(srcObjPrincipal.get()))) { return JS_FALSE; } // Pass on ownership of the new object principal to the // wrapper. nsIPrincipal *tmp = nsnull; srcObjPrincipal.swap(tmp); } } } return ok; }
// ECMA 8.6.2.2 EXPORT void JSObject::put(ExecState* exec, const Identifier &propertyName, JSValue *value, int attr) { assert(value); // non-standard netscape extension if (propertyName == exec->propertyNames().underscoreProto) { JSObject* proto = value->getObject(); while (proto) { if (proto == this) throwError(exec, GeneralError, "cyclic __proto__ value"); proto = proto->prototype() ? proto->prototype()->getObject() : 0; } setPrototype(value); return; } /* TODO: check for write permissions directly w/o this call */ /* Doesn't look very easy with the PropertyMap API - David */ // putValue() is used for JS assignemnts. It passes no attribute. // Assume that a C++ implementation knows what it is doing // and let it override the canPut() check. if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) { #ifdef KJS_VERBOSE fprintf( stderr, "WARNING: canPut %s said NO\n", propertyName.ascii() ); #endif return; } // Check if there are any setters or getters in the prototype chain JSObject *obj = this; bool hasGettersOrSetters = false; while (true) { if (obj->_prop.hasGetterSetterProperties()) { hasGettersOrSetters = true; break; } if (!obj->_proto->isObject()) break; obj = static_cast<JSObject *>(obj->_proto); } if (hasGettersOrSetters) { obj = this; while (true) { unsigned attributes; if (JSValue *gs = obj->_prop.get(propertyName, attributes)) { if (attributes & GetterSetter) { JSObject *setterFunc = static_cast<GetterSetterImp *>(gs)->getSetter(); if (!setterFunc) { throwSetterError(exec); return; } List args; args.append(value); setterFunc->call(exec, this, args); return; } else { // If there's an existing property on the object or one of its // prototype it should be replaced, so we just break here. break; } } if (!obj->_proto->isObject()) break; obj = static_cast<JSObject *>(obj->_proto); } } _prop.put(propertyName,value,attr); }
JSValueRef JSObjectGetPrototype(JSContextRef, JSObjectRef object) { JSObject* jsObject = toJS(object); return toRef(jsObject->prototype()); }
JSBool js_ReportUncaughtException(JSContext *cx) { jsval exn; JSObject *exnObject; jsval roots[5]; JSErrorReport *reportp, report; JSString *str; const char *bytes; if (!JS_IsExceptionPending(cx)) return true; if (!JS_GetPendingException(cx, &exn)) return false; PodArrayZero(roots); AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), Valueify(roots)); /* * Because js_ValueToString below could error and an exception object * could become unrooted, we must root exnObject. Later, if exnObject is * non-null, we need to root other intermediates, so allocate an operand * stack segment to protect all of these values. */ if (JSVAL_IS_PRIMITIVE(exn)) { exnObject = NULL; } else { exnObject = JSVAL_TO_OBJECT(exn); roots[0] = exn; } JS_ClearPendingException(cx); reportp = js_ErrorFromException(cx, exn); /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */ str = js_ValueToString(cx, Valueify(exn)); JSAutoByteString bytesStorage; if (!str) { bytes = "unknown (can't convert to string)"; } else { roots[1] = STRING_TO_JSVAL(str); if (!bytesStorage.encode(cx, str)) return false; bytes = bytesStorage.ptr(); } JSAutoByteString filename; if (!reportp && exnObject && exnObject->getClass() == &js_ErrorClass) { if (!JS_GetProperty(cx, exnObject, js_message_str, &roots[2])) return false; if (JSVAL_IS_STRING(roots[2])) { bytesStorage.clear(); if (!bytesStorage.encode(cx, str)) return false; bytes = bytesStorage.ptr(); } if (!JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3])) return false; str = js_ValueToString(cx, Valueify(roots[3])); if (!str || !filename.encode(cx, str)) return false; if (!JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4])) return false; uint32_t lineno; if (!ValueToECMAUint32(cx, Valueify(roots[4]), &lineno)) return false; reportp = &report; PodZero(&report); report.filename = filename.ptr(); report.lineno = (uintN) lineno; if (JSVAL_IS_STRING(roots[2])) { report.ucmessage = js_GetStringChars(cx, JSVAL_TO_STRING(roots[2])); if (!report.ucmessage) return false; } } if (!reportp) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNCAUGHT_EXCEPTION, bytes); } else { /* Flag the error as an exception. */ reportp->flags |= JSREPORT_EXCEPTION; /* Pass the exception object. */ JS_SetPendingException(cx, exn); js_ReportErrorAgain(cx, bytes, reportp); JS_ClearPendingException(cx); } return true; }
bool JSObjectIsConstructor(JSContextRef, JSObjectRef object) { JSObject* jsObject = toJS(object); ConstructData constructData; return jsObject->getConstructData(constructData) != ConstructTypeNone; }
/* * Return a string that may eval to something similar to the original object. */ static JSBool exn_toSource(JSContext *cx, uintN argc, Value *vp) { JSObject *obj; JSString *name, *message, *filename, *lineno_as_str, *result; jsval localroots[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL}; size_t lineno_length, name_length, message_length, filename_length, length; jschar *chars, *cp; obj = ComputeThisFromVp(cx, vp); if (!obj || !obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.nameAtom), vp)) return false; name = js_ValueToString(cx, *vp); if (!name) return false; vp->setString(name); { AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(localroots), Valueify(localroots)); #ifdef __GNUC__ message = filename = NULL; #endif if (!JS_GetProperty(cx, obj, js_message_str, &localroots[0]) || !(message = js_ValueToSource(cx, Valueify(localroots[0])))) { return false; } localroots[0] = STRING_TO_JSVAL(message); if (!JS_GetProperty(cx, obj, js_fileName_str, &localroots[1]) || !(filename = js_ValueToSource(cx, Valueify(localroots[1])))) { return false; } localroots[1] = STRING_TO_JSVAL(filename); if (!JS_GetProperty(cx, obj, js_lineNumber_str, &localroots[2])) return false; uint32_t lineno; if (!ValueToECMAUint32(cx, Valueify(localroots[2]), &lineno)) return false; if (lineno != 0) { lineno_as_str = js_ValueToString(cx, Valueify(localroots[2])); if (!lineno_as_str) return false; lineno_length = lineno_as_str->length(); } else { lineno_as_str = NULL; lineno_length = 0; } /* Magic 8, for the characters in ``(new ())''. */ name_length = name->length(); message_length = message->length(); length = 8 + name_length + message_length; filename_length = filename->length(); if (filename_length != 0) { /* append filename as ``, {filename}'' */ length += 2 + filename_length; if (lineno_as_str) { /* append lineno as ``, {lineno_as_str}'' */ length += 2 + lineno_length; } } else { if (lineno_as_str) { /* * no filename, but have line number, * need to append ``, "", {lineno_as_str}'' */ length += 6 + lineno_length; } } cp = chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar)); if (!chars) return false; *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' '; js_strncpy(cp, name->chars(), name_length); cp += name_length; *cp++ = '('; if (message_length != 0) { js_strncpy(cp, message->chars(), message_length); cp += message_length; } if (filename_length != 0) { /* append filename as ``, {filename}'' */ *cp++ = ','; *cp++ = ' '; js_strncpy(cp, filename->chars(), filename_length); cp += filename_length; } else { if (lineno_as_str) { /* * no filename, but have line number, * need to append ``, "", {lineno_as_str}'' */ *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"'; } } if (lineno_as_str) { /* append lineno as ``, {lineno_as_str}'' */ *cp++ = ','; *cp++ = ' '; js_strncpy(cp, lineno_as_str->chars(), lineno_length); cp += lineno_length; } *cp++ = ')'; *cp++ = ')'; *cp = 0; result = js_NewString(cx, chars, length); if (!result) { cx->free(chars); return false; } vp->setString(result); return true; } }
// ES5 8.10.5 ToPropertyDescriptor static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc) { if (!in.isObject()) { throwError(exec, TypeError, "Property description must be an object."); return false; } JSObject* description = asObject(in); PropertySlot enumerableSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().enumerable, enumerableSlot)) { desc.setEnumerable(enumerableSlot.getValue(exec, exec->propertyNames().enumerable).toBoolean(exec)); if (exec->hadException()) return false; } PropertySlot configurableSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().configurable, configurableSlot)) { desc.setConfigurable(configurableSlot.getValue(exec, exec->propertyNames().configurable).toBoolean(exec)); if (exec->hadException()) return false; } JSValue value; PropertySlot valueSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().value, valueSlot)) { desc.setValue(valueSlot.getValue(exec, exec->propertyNames().value)); if (exec->hadException()) return false; } PropertySlot writableSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().writable, writableSlot)) { desc.setWritable(writableSlot.getValue(exec, exec->propertyNames().writable).toBoolean(exec)); if (exec->hadException()) return false; } PropertySlot getSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().get, getSlot)) { JSValue get = getSlot.getValue(exec, exec->propertyNames().get); if (exec->hadException()) return false; if (!get.isUndefined()) { CallData callData; if (get.getCallData(callData) == CallTypeNone) { throwError(exec, TypeError, "Getter must be a function."); return false; } } else get = JSValue(); desc.setGetter(get); } PropertySlot setSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().set, setSlot)) { JSValue set = setSlot.getValue(exec, exec->propertyNames().set); if (exec->hadException()) return false; if (!set.isUndefined()) { CallData callData; if (set.getCallData(callData) == CallTypeNone) { throwError(exec, TypeError, "Setter must be a function."); return false; } } else set = JSValue(); desc.setSetter(set); } if (!desc.isAccessorDescriptor()) return true; if (desc.value()) { throwError(exec, TypeError, "Invalid property. 'value' present on property with getter or setter."); return false; } if (desc.writablePresent()) { throwError(exec, TypeError, "Invalid property. 'writable' present on property with getter or setter."); return false; } return true; }