static int32 GetChar(JSTokenStream *ts) { int32 c; ptrdiff_t i, j, len, olen; JSBool crflag; char cbuf[JS_LINE_LIMIT]; jschar *ubuf, *nl; if (ts->ungetpos != 0) { c = ts->ungetbuf[--ts->ungetpos]; } else { do { if (ts->linebuf.ptr == ts->linebuf.limit) { len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar); if (len <= 0) { if (!ts->file) { ts->flags |= TSF_EOF; return EOF; } /* Fill ts->userbuf so that \r and \r\n convert to \n. */ crflag = (ts->flags & TSF_CRFLAG) != 0; len = my_fgets(cbuf, JS_LINE_LIMIT - crflag, ts->file); if (len <= 0) { ts->flags |= TSF_EOF; return EOF; } olen = len; ubuf = ts->userbuf.base; i = 0; if (crflag) { ts->flags &= ~TSF_CRFLAG; if (cbuf[0] != '\n') { ubuf[i++] = '\n'; len++; ts->linepos--; } } for (j = 0; i < len; i++, j++) ubuf[i] = (jschar) (unsigned char) cbuf[j]; ts->userbuf.limit = ubuf + len; ts->userbuf.ptr = ubuf; } if (ts->listener) { ts->listener(ts->filename, ts->lineno, ts->userbuf.ptr, len, &ts->listenerTSData, ts->listenerData); } /* * Any one of \n, \r, or \r\n ends a line (longest match wins). * Also allow the Unicode line and paragraph separators. */ for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) { /* * Try to prevent value-testing on most characters by * filtering out characters that aren't 000x or 202x. */ if ((*nl & 0xDFD0) == 0) { if (*nl == '\n') break; if (*nl == '\r') { if (nl + 1 < ts->userbuf.limit && nl[1] == '\n') nl++; break; } if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) break; } } /* * If there was a line terminator, copy thru it into linebuf. * Else copy JS_LINE_LIMIT-1 bytes into linebuf. */ if (nl < ts->userbuf.limit) len = PTRDIFF(nl, ts->userbuf.ptr, jschar) + 1; if (len >= JS_LINE_LIMIT) len = JS_LINE_LIMIT - 1; js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len); ts->userbuf.ptr += len; olen = len; /* * Make sure linebuf contains \n for EOL (don't do this in * userbuf because the user's string might be readonly). */ if (nl < ts->userbuf.limit) { if (*nl == '\r') { if (ts->linebuf.base[len-1] == '\r') { /* * Does the line segment end in \r? We must check * for a \n at the front of the next segment before * storing a \n into linebuf. This case matters * only when we're reading from a file. */ if (nl + 1 == ts->userbuf.limit && ts->file) { len--; ts->flags |= TSF_CRFLAG; /* clear NLFLAG? */ if (len == 0) { /* * This can happen when a segment ends in * \r\r. Start over. ptr == limit in this * case, so we'll fall into buffer-filling * code. */ return GetChar(ts); } } else { ts->linebuf.base[len-1] = '\n'; } } } else if (*nl == '\n') { if (nl > ts->userbuf.base && nl[-1] == '\r' && ts->linebuf.base[len-2] == '\r') { len--; JS_ASSERT(ts->linebuf.base[len] == '\n'); ts->linebuf.base[len-1] = '\n'; } } else if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) { ts->linebuf.base[len-1] = '\n'; } } /* Reset linebuf based on adjusted segment length. */ ts->linebuf.limit = ts->linebuf.base + len; ts->linebuf.ptr = ts->linebuf.base; /* Update position of linebuf within physical userbuf line. */ if (!(ts->flags & TSF_NLFLAG)) ts->linepos += ts->linelen; else ts->linepos = 0; if (ts->linebuf.limit[-1] == '\n') ts->flags |= TSF_NLFLAG; else ts->flags &= ~TSF_NLFLAG; /* Update linelen from original segment length. */ ts->linelen = olen; } c = *ts->linebuf.ptr++; } while (JS_ISFORMAT(c)); } if (c == '\n') ts->lineno++; return c; }
JSBool js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg, uintN flags, const uintN errorNumber, ...) { va_list ap; JSErrorReporter onError; JSErrorReport report; jschar *tokenptr; JSString *linestr = NULL; char *message; JSBool warning; if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) return JS_TRUE; memset(&report, 0, sizeof (struct JSErrorReport)); report.flags = flags; report.errorNumber = errorNumber; message = NULL; va_start(ap, errorNumber); if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL, errorNumber, &message, &report, &warning, JS_TRUE, ap)) { return JS_FALSE; } va_end(ap); js_AddRoot(cx, &linestr, "error line buffer"); JS_ASSERT(!ts || ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT); onError = cx->errorReporter; if (onError) { /* * We are typically called with non-null ts and null cg from jsparse.c. * We can be called with null ts from the regexp compilation functions. * The code generator (jsemit.c) may pass null ts and non-null cg. */ if (ts) { report.filename = ts->filename; report.lineno = ts->lineno; linestr = js_NewStringCopyN(cx, ts->linebuf.base, ts->linebuf.limit - ts->linebuf.base, 0); report.linebuf = linestr ? JS_GetStringBytes(linestr) : NULL; tokenptr = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].ptr; report.tokenptr = linestr ? report.linebuf + (tokenptr - ts->linebuf.base) : NULL; report.uclinebuf = linestr ? JS_GetStringChars(linestr) : NULL; report.uctokenptr = linestr ? report.uclinebuf + (tokenptr - ts->linebuf.base) : NULL; } else if (cg) { report.filename = cg->filename; report.lineno = cg->currentLine; } #if JS_HAS_ERROR_EXCEPTIONS /* * If there's a runtime exception type associated with this error * number, set that as the pending exception. For errors occuring at * compile time, this is very likely to be a JSEXN_SYNTAXERR. * * If an exception is thrown but not caught, the JSREPORT_EXCEPTION * flag will be set in report.flags. Proper behavior for an error * reporter is to ignore a report with this flag for all but top-level * compilation errors. The exception will remain pending, and so long * as the non-top-level "load", "eval", or "compile" native function * returns false, the top-level reporter will eventually receive the * uncaught exception report. * * XXX it'd probably be best if there was only one call to this * function, but there seem to be two error reporter call points. */ /* * Only try to raise an exception if there isn't one already set - * otherwise the exception will describe only the last compile error, * which is likely spurious. */ if (!(ts && (ts->flags & TSF_ERROR))) if (js_ErrorToException(cx, message, &report)) onError = NULL; /* * Suppress any compiletime errors that don't occur at the top level. * This may still fail, as interplevel may be zero in contexts where we * don't really want to call the error reporter, as when js is called * by other code which could catch the error. */ if (cx->interpLevel != 0) onError = NULL; #endif if (cx->runtime->debugErrorHook && onError) { JSDebugErrorHook hook = cx->runtime->debugErrorHook; /* test local in case debugErrorHook changed on another thread */ if (hook && !hook(cx, message, &report, cx->runtime->debugErrorHookData)) { onError = NULL; } } if (onError) (*onError)(cx, message, &report); } if (message) JS_free(cx, message); if (report.messageArgs) { int i = 0; while (report.messageArgs[i]) JS_free(cx, (void *)report.messageArgs[i++]); JS_free(cx, (void *)report.messageArgs); } if (report.ucmessage) JS_free(cx, (void *)report.ucmessage); js_RemoveRoot(cx->runtime, &linestr); if (ts && !JSREPORT_IS_WARNING(flags)) { /* Set the error flag to suppress spurious reports. */ ts->flags |= TSF_ERROR; } return warning; }
void JSD_ASSERT_VALID_THREAD_STATE(JSDThreadState* jsdthreadstate) { JS_ASSERT(jsdthreadstate); JS_ASSERT(jsdthreadstate->stackDepth > 0); }
void JSD_ASSERT_VALID_STACK_FRAME(JSDStackFrameInfo* jsdframe) { JS_ASSERT(jsdframe); JS_ASSERT(jsdframe->jsdthreadstate); }
JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::slurpDownFrames(jsbytecode* return_pc) { /* Missing - no go */ if (cx->fp->argc != cx->fp->fun->nargs) RETURN_STOP_A("argc != nargs"); LIns* argv_ins; unsigned frameDepth; unsigned downPostSlots; JSStackFrame* fp = cx->fp; LIns* fp_ins = addName(lir->insLoad(LIR_ldp, cx_ins, offsetof(JSContext, fp)), "fp"); /* * When first emitting slurp code, do so against the down frame. After * popping the interpreter frame, it is illegal to resume here, as the * down frame has been moved up. So all this code should be skipped if * anchoring off such an exit. */ if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { fp_ins = addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, down)), "downFp"); fp = fp->down; argv_ins = addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, argv)), "argv"); /* If recovering from a SLURP_MISMATCH, all of this is unnecessary. */ if (!anchor || anchor->exitType != RECURSIVE_SLURP_MISMATCH_EXIT) { /* fp->down should not be NULL. */ guard(false, lir->ins_peq0(fp_ins), RECURSIVE_LOOP_EXIT); /* fp->down->argv should not be NULL. */ guard(false, lir->ins_peq0(argv_ins), RECURSIVE_LOOP_EXIT); /* * Guard on the script being the same. This might seem unnecessary, * but it lets the recursive loop end cleanly if it doesn't match. * With only the pc check, it is harder to differentiate between * end-of-recursion and recursion-returns-to-different-pc. */ guard(true, lir->ins2(LIR_peq, addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, script)), "script"), INS_CONSTPTR(cx->fp->down->script)), RECURSIVE_LOOP_EXIT); } /* fp->down->regs->pc should be == pc. */ guard(true, lir->ins2(LIR_peq, lir->insLoad(LIR_ldp, addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, regs)), "regs"), offsetof(JSFrameRegs, pc)), INS_CONSTPTR(return_pc)), RECURSIVE_SLURP_MISMATCH_EXIT); /* fp->down->argc should be == argc. */ guard(true, lir->ins2(LIR_eq, addName(lir->insLoad(LIR_ld, fp_ins, offsetof(JSStackFrame, argc)), "argc"), INS_CONST(cx->fp->argc)), MISMATCH_EXIT); /* Pop the interpreter frame. */ LIns* args[] = { lirbuf->state, cx_ins }; guard(false, lir->ins_eq0(lir->insCall(&js_PopInterpFrame_ci, args)), MISMATCH_EXIT); /* Compute slots for the down frame. */ downPostSlots = NativeStackSlots(cx, 1) - NativeStackSlots(cx, 0); frameDepth = 1; } else { /* Note: loading argv from fp, not fp->down. */ argv_ins = addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, argv)), "argv"); /* Slots for this frame, minus the return value. */ downPostSlots = NativeStackSlots(cx, 0) - 1; frameDepth = 0; } /* * This is a special exit used as a template for the stack-slurping code. * LeaveTree will ignore all but the final slot, which contains the return * value. The slurpSlot variable keeps track of the last slot that has been * unboxed, as to avoid re-unboxing when taking a SLURP_FAIL exit. */ unsigned numGlobalSlots = tree->globalSlots->length(); unsigned safeSlots = NativeStackSlots(cx, frameDepth) + 1 + numGlobalSlots; jsbytecode* recursive_pc = return_pc + JSOP_CALL_LENGTH; VMSideExit* exit = (VMSideExit*) traceMonitor->traceAlloc->alloc(sizeof(VMSideExit) + sizeof(TraceType) * safeSlots); memset(exit, 0, sizeof(VMSideExit)); exit->pc = (jsbytecode*)recursive_pc; exit->from = fragment; exit->exitType = RECURSIVE_SLURP_FAIL_EXIT; exit->numStackSlots = downPostSlots + 1; exit->numGlobalSlots = numGlobalSlots; exit->sp_adj = ((downPostSlots + 1) * sizeof(double)) - tree->nativeStackBase; exit->recursive_pc = recursive_pc; /* * Build the exit typemap. This may capture extra types, but they are * thrown away. */ TraceType* typeMap = exit->stackTypeMap(); jsbytecode* oldpc = cx->fp->regs->pc; cx->fp->regs->pc = exit->pc; CaptureStackTypes(cx, frameDepth, typeMap); cx->fp->regs->pc = oldpc; if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) typeMap[downPostSlots] = determineSlotType(&stackval(-1)); else typeMap[downPostSlots] = anchor->stackTypeMap()[anchor->numStackSlots - 1]; determineGlobalTypes(&typeMap[exit->numStackSlots]); #if defined JS_JIT_SPEW TreevisLogExit(cx, exit); #endif /* * Return values are tricky because there are two cases. Anchoring off a * slurp failure (the second case) means the return value has already been * moved. However it can still be promoted to link trees together, so we * load it from the new location. * * In all other cases, the return value lives in the tracker and it can be * grabbed safely. */ LIns* rval_ins; TraceType returnType = exit->stackTypeMap()[downPostSlots]; if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { rval_ins = get(&stackval(-1)); if (returnType == TT_INT32) { JS_ASSERT(determineSlotType(&stackval(-1)) == TT_INT32); JS_ASSERT(isPromoteInt(rval_ins)); rval_ins = demote(lir, rval_ins); } /* * The return value must be written out early, before slurping can fail, * otherwise it will not be available when there's a type mismatch. */ lir->insStorei(rval_ins, lirbuf->sp, exit->sp_adj - sizeof(double)); } else { switch (returnType) { case TT_PSEUDOBOOLEAN: case TT_INT32: rval_ins = lir->insLoad(LIR_ld, lirbuf->sp, exit->sp_adj - sizeof(double)); break; case TT_DOUBLE: rval_ins = lir->insLoad(LIR_ldf, lirbuf->sp, exit->sp_adj - sizeof(double)); break; case TT_FUNCTION: case TT_OBJECT: case TT_STRING: case TT_NULL: rval_ins = lir->insLoad(LIR_ldp, lirbuf->sp, exit->sp_adj - sizeof(double)); break; default: JS_NOT_REACHED("unknown type"); RETURN_STOP_A("unknown type"); } } /* Slurp */ SlurpInfo info; info.curSlot = 0; info.exit = exit; info.typeMap = typeMap; info.slurpFailSlot = (anchor && anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT) ? anchor->slurpFailSlot : 0; /* callee */ slurpSlot(lir->insLoad(LIR_ldp, argv_ins, -2 * ptrdiff_t(sizeof(jsval))), &fp->argv[-2], &info); /* this */ slurpSlot(lir->insLoad(LIR_ldp, argv_ins, -1 * ptrdiff_t(sizeof(jsval))), &fp->argv[-1], &info); /* args[0..n] */ for (unsigned i = 0; i < JS_MAX(fp->argc, fp->fun->nargs); i++) slurpSlot(lir->insLoad(LIR_ldp, argv_ins, i * sizeof(jsval)), &fp->argv[i], &info); /* argsobj */ slurpSlot(addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, argsobj)), "argsobj"), &fp->argsobj, &info); /* scopeChain */ slurpSlot(addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, scopeChain)), "scopeChain"), (jsval*) &fp->scopeChain, &info); /* vars */ LIns* slots_ins = addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, slots)), "slots"); for (unsigned i = 0; i < fp->script->nfixed; i++) slurpSlot(lir->insLoad(LIR_ldp, slots_ins, i * sizeof(jsval)), &fp->slots[i], &info); /* stack vals */ unsigned nfixed = fp->script->nfixed; jsval* stack = StackBase(fp); LIns* stack_ins = addName(lir->ins2(LIR_piadd, slots_ins, INS_CONSTWORD(nfixed * sizeof(jsval))), "stackBase"); size_t limit = size_t(fp->regs->sp - StackBase(fp)); if (anchor && anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT) limit--; else limit -= fp->fun->nargs + 2; for (size_t i = 0; i < limit; i++) slurpSlot(lir->insLoad(LIR_ldp, stack_ins, i * sizeof(jsval)), &stack[i], &info); JS_ASSERT(info.curSlot == downPostSlots); /* Jump back to the start */ exit = copy(exit); exit->exitType = UNSTABLE_LOOP_EXIT; #if defined JS_JIT_SPEW TreevisLogExit(cx, exit); #endif RecursiveSlotMap slotMap(*this, downPostSlots, rval_ins); for (unsigned i = 0; i < downPostSlots; i++) slotMap.addSlot(typeMap[i]); slotMap.addSlot(&stackval(-1), typeMap[downPostSlots]); VisitGlobalSlots(slotMap, cx, *tree->globalSlots); debug_only_print0(LC_TMTracer, "Compiling up-recursive slurp...\n"); exit = copy(exit); if (exit->recursive_pc == fragment->root->ip) exit->exitType = UNSTABLE_LOOP_EXIT; else exit->exitType = RECURSIVE_UNLINKED_EXIT; debug_only_printf(LC_TMTreeVis, "TREEVIS CHANGEEXIT EXIT=%p TYPE=%s\n", (void*)exit, getExitName(exit->exitType)); JS_ASSERT(tree->recursion >= Recursion_Unwinds); return closeLoop(slotMap, exit); }
/* * Convert a JS value to a Java value of the given type signature. The cost * variable is incremented if coercion is required, e.g. the source value is * a string, but the target type is a boolean. * * Returns JS_FALSE if no conversion is possible, either because the jsval has * a type that is wholly incompatible with the Java value, or because a scalar * jsval can't be represented in a variable of the target type without loss of * precision, e.g. the source value is "4.2" but the destination type is byte. * If conversion is not possible and java_value is non-NULL, the JS error * reporter is called with an appropriate message. */ JSBool jsj_ConvertJSValueToJavaValue(JSContext *cx, JNIEnv *jEnv, jsval v_arg, JavaSignature *signature, int *cost, jvalue *java_value, JSBool *is_local_refp) { JavaSignatureChar type; jsval v; JSBool success = JS_FALSE; /* Initialize to default case, in which no new Java object is synthesized to perform the conversion and, therefore, no JNI local references are being held. */ *is_local_refp = JS_FALSE; type = signature->type; v = v_arg; switch (type) { case JAVA_SIGNATURE_BOOLEAN: if (!JSVAL_IS_BOOLEAN(v)) { if (!JS_ConvertValue(cx, v, JSTYPE_BOOLEAN, &v)) goto conversion_error; if (JSVAL_IS_VOID(v)) goto conversion_error; (*cost)++; } if (java_value) java_value->z = (jboolean)(JSVAL_TO_BOOLEAN(v) == JS_TRUE); break; case JAVA_SIGNATURE_SHORT: JSVAL_TO_INTEGRAL_JVALUE(short, s, jshort, v, java_value); break; case JAVA_SIGNATURE_BYTE: JSVAL_TO_INTEGRAL_JVALUE(byte, b, jbyte, v, java_value); break; case JAVA_SIGNATURE_CHAR: /* A one-character string can be converted into a character */ if (JSVAL_IS_STRING(v) && (JS_GetStringLength(JSVAL_TO_STRING(v)) == 1)) { v = INT_TO_JSVAL(*JS_GetStringChars(JSVAL_TO_STRING(v))); } JSVAL_TO_INTEGRAL_JVALUE(char, c, jchar, v, java_value); break; case JAVA_SIGNATURE_INT: JSVAL_TO_INTEGRAL_JVALUE(int, i, jint, v, java_value); break; case JAVA_SIGNATURE_LONG: #if defined(XP_MAC) || (defined(XP_OS2) && !defined(HAVE_LONG_LONG)) JSVAL_TO_JLONG_JVALUE(j, jlong, v, java_value); #else JSVAL_TO_INTEGRAL_JVALUE(long, j, jlong, v, java_value); #endif break; case JAVA_SIGNATURE_FLOAT: if (!JSVAL_IS_NUMBER(v)) { if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v)) goto conversion_error; (*cost)++; } if (java_value) { if (JSVAL_IS_INT(v)) java_value->f = (jfloat) JSVAL_TO_INT(v); else java_value->f = (jfloat) *JSVAL_TO_DOUBLE(v); } break; case JAVA_SIGNATURE_DOUBLE: if (!JSVAL_IS_NUMBER(v)) { if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v)) goto conversion_error; (*cost)++; } if (java_value) { if (JSVAL_IS_INT(v)) java_value->d = (jdouble) JSVAL_TO_INT(v); else java_value->d = (jdouble) *JSVAL_TO_DOUBLE(v); } break; /* Non-primitive (reference) type */ default: JS_ASSERT(IS_REFERENCE_TYPE(type)); if (!jsj_ConvertJSValueToJavaObject(cx, jEnv, v, signature, cost, &java_value->l, is_local_refp)) goto conversion_error; break; case JAVA_SIGNATURE_UNKNOWN: case JAVA_SIGNATURE_VOID: JS_ASSERT(0); return JS_FALSE; } /* Success */ return JS_TRUE; numeric_conversion_error: success = JS_TRUE; /* Fall through ... */ conversion_error: if (java_value) { const char *jsval_string; const char *class_name; JSString *jsstr; jsval_string = NULL; jsstr = JS_ValueToString(cx, v_arg); if (jsstr) jsval_string = JS_GetStringBytes(jsstr); if (!jsval_string) jsval_string = ""; class_name = jsj_ConvertJavaSignatureToHRString(cx, signature); JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, JSJMSG_CANT_CONVERT_JS, jsval_string, class_name); return JS_FALSE; } return success; }
JSObject * WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj, uintN flags) { // Don't unwrap an outer window, just double wrap it if needed. if (obj->getClass()->ext.innerObject) return DoubleWrap(cx, obj, flags); // Here are the rules for wrapping: // We should never get a proxy here (the JS engine unwraps those for us). JS_ASSERT(!obj->isWrapper()); // As soon as an object is wrapped in a security wrapper, it morphs to be // a fat wrapper. (see also: bug XXX). if (IS_SLIM_WRAPPER(obj) && !MorphSlimWrapper(cx, obj)) return nsnull; // We only hand out outer objects to script. obj = GetCurrentOuter(cx, obj); if (obj->getClass()->ext.innerObject) return DoubleWrap(cx, obj, flags); // Now, our object is ready to be wrapped, but several objects (notably // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of // those objects in a security wrapper, then we need to hand back the // wrapper for the new scope instead. Also, global objects don't move // between scopes so for those we also want to return the wrapper. So... if (!IS_WN_WRAPPER(obj) || !obj->getParent()) return DoubleWrap(cx, obj, flags); XPCWrappedNative *wn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj)); // If the object doesn't have classinfo we want to return the same // XPCWrappedNative so that we keep the same set of interfaces. if (!wn->GetClassInfo()) return DoubleWrap(cx, obj, flags); JSAutoEnterCompartment ac; if (!ac.enter(cx, obj)) return nsnull; XPCCallContext ccx(JS_CALLER, cx, obj); { if (NATIVE_HAS_FLAG(&ccx, WantPreCreate)) { // We have a precreate hook. This object might enforce that we only // ever create JS object for it. JSObject *originalScope = scope; nsresult rv = wn->GetScriptableInfo()->GetCallback()-> PreCreate(wn->Native(), cx, scope, &scope); NS_ENSURE_SUCCESS(rv, DoubleWrap(cx, obj, flags)); // If the handed back scope differs from the passed-in scope and is in // a separate compartment, then this object is explicitly requesting // that we don't create a second JS object for it: create a security // wrapper. if (originalScope->compartment() != scope->getCompartment()) return DoubleWrap(cx, obj, flags); // Note: this penalizes objects that only have one wrapper, but are // being accessed across compartments. We would really prefer to // replace the above code with a test that says "do you only have one // wrapper?" } } // NB: Passing a holder here inhibits slim wrappers under // WrapNativeToJSVal. nsCOMPtr<nsIXPConnectJSObjectHolder> holder; // This public WrapNativeToJSVal API enters the compartment of 'scope' // so we don't have to. jsval v; nsresult rv = nsXPConnect::FastGetXPConnect()->WrapNativeToJSVal(cx, scope, wn->Native(), nsnull, &NS_GET_IID(nsISupports), PR_FALSE, &v, getter_AddRefs(holder)); if (NS_SUCCEEDED(rv)) { obj = JSVAL_TO_OBJECT(v); NS_ASSERTION(IS_WN_WRAPPER(obj), "bad object"); XPCWrappedNative *newwn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj)); if (newwn->GetSet()->GetInterfaceCount() < wn->GetSet()->GetInterfaceCount()) newwn->SetSet(wn->GetSet()); } return DoubleWrap(cx, obj, flags); }
SnapshotOffset snapshotOffset() const { JS_ASSERT(frameClass() == FrameSizeClass::None()); return snapshotOffset_; }
static JSBool InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, JSString *filename, uintN lineno, JSErrorReport *report) { JSCheckAccessOp checkAccess; JSErrorReporter older; JSExceptionState *state; jsval callerid, v; JSStackFrame *fp, *fpstop; size_t stackDepth, valueCount, size; JSBool overflow; JSExnPrivate *priv; JSStackTraceElem *elem; jsval *values; JS_ASSERT(OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass); /* * Prepare stack trace data. * * Set aside any error reporter for cx and save its exception state * so we can suppress any checkAccess failures. Such failures should stop * the backtrace procedure, not result in a failure of this constructor. */ checkAccess = cx->runtime->checkObjectAccess; older = JS_SetErrorReporter(cx, NULL); state = JS_SaveExceptionState(cx); callerid = ATOM_KEY(cx->runtime->atomState.callerAtom); stackDepth = 0; valueCount = 0; for (fp = cx->fp; fp; fp = fp->down) { if (fp->fun && fp->argv) { v = JSVAL_NULL; if (checkAccess && !checkAccess(cx, fp->callee, callerid, JSACC_READ, &v)) { break; } valueCount += fp->argc; } ++stackDepth; } JS_RestoreExceptionState(cx, state); JS_SetErrorReporter(cx, older); fpstop = fp; size = offsetof(JSExnPrivate, stackElems); overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem)); size += stackDepth * sizeof(JSStackTraceElem); overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval)); size += valueCount * sizeof(jsval); if (overflow) { js_ReportAllocationOverflow(cx); return JS_FALSE; } priv = (JSExnPrivate *)JS_malloc(cx, size); if (!priv) return JS_FALSE; /* * We initialize errorReport with a copy of report after setting the * private slot, to prevent GC accessing a junk value we clear the field * here. */ priv->errorReport = NULL; priv->message = message; priv->filename = filename; priv->lineno = lineno; priv->stackDepth = stackDepth; values = GetStackTraceValueBuffer(priv); elem = priv->stackElems; for (fp = cx->fp; fp != fpstop; fp = fp->down) { if (!fp->fun) { elem->funName = NULL; elem->argc = 0; } else { JSAtom *atom; atom = FUN_ATOM(fp->fun); elem->funName = atom ? ATOM_TO_STRING(atom) : cx->runtime->emptyString; elem->argc = fp->argc; memcpy(values, fp->argv, fp->argc * sizeof(jsval)); values += fp->argc; } elem->ulineno = 0; elem->filename = NULL; if (fp->script) { elem->filename = fp->script->filename; if (fp->regs) elem->ulineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc); } ++elem; } JS_ASSERT(priv->stackElems + stackDepth == elem); JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values); STOBJ_SET_SLOT(exnObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(priv)); if (report) { /* * Construct a new copy of the error report struct. We can't use the * error report struct that was passed in, because it's allocated on * the stack, and also because it may point to transient data in the * JSTokenStream. */ priv->errorReport = CopyErrorReport(cx, report); if (!priv->errorReport) { /* The finalizer realeases priv since it is in the private slot. */ return JS_FALSE; } } return JS_TRUE; }
static JSString * StackTraceToString(JSContext *cx, JSExnPrivate *priv) { jschar *stackbuf; size_t stacklen, stackmax; JSStackTraceElem *elem, *endElem; jsval *values; size_t i; JSString *str; const char *cp; char ulnbuf[11]; /* After this point, failing control flow must goto bad. */ stackbuf = NULL; stacklen = stackmax = 0; /* Limit the stackbuf length to a reasonable value to avoid overflow checks. */ #define STACK_LENGTH_LIMIT JS_BIT(20) #define APPEND_CHAR_TO_STACK(c) \ JS_BEGIN_MACRO \ if (stacklen == stackmax) { \ void *ptr_; \ if (stackmax >= STACK_LENGTH_LIMIT) \ goto done; \ stackmax = stackmax ? 2 * stackmax : 64; \ ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ if (!ptr_) \ goto bad; \ stackbuf = (jschar *) ptr_; \ } \ stackbuf[stacklen++] = (c); \ JS_END_MACRO #define APPEND_STRING_TO_STACK(str) \ JS_BEGIN_MACRO \ JSString *str_ = str; \ jschar *chars_; \ size_t length_; \ \ JSSTRING_CHARS_AND_LENGTH(str_, chars_, length_); \ if (length_ > stackmax - stacklen) { \ void *ptr_; \ if (stackmax >= STACK_LENGTH_LIMIT || \ length_ >= STACK_LENGTH_LIMIT - stacklen) { \ goto done; \ } \ stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \ ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ if (!ptr_) \ goto bad; \ stackbuf = (jschar *) ptr_; \ } \ js_strncpy(stackbuf + stacklen, chars_, length_); \ stacklen += length_; \ JS_END_MACRO values = GetStackTraceValueBuffer(priv); elem = priv->stackElems; for (endElem = elem + priv->stackDepth; elem != endElem; elem++) { if (elem->funName) { APPEND_STRING_TO_STACK(elem->funName); APPEND_CHAR_TO_STACK('('); for (i = 0; i != elem->argc; i++, values++) { if (i > 0) APPEND_CHAR_TO_STACK(','); str = ValueToShortSource(cx, *values); if (!str) goto bad; APPEND_STRING_TO_STACK(str); } APPEND_CHAR_TO_STACK(')'); } APPEND_CHAR_TO_STACK('@'); if (elem->filename) { for (cp = elem->filename; *cp; cp++) APPEND_CHAR_TO_STACK(*cp); } APPEND_CHAR_TO_STACK(':'); JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", elem->ulineno); for (cp = ulnbuf; *cp; cp++) APPEND_CHAR_TO_STACK(*cp); APPEND_CHAR_TO_STACK('\n'); } #undef APPEND_CHAR_TO_STACK #undef APPEND_STRING_TO_STACK #undef STACK_LENGTH_LIMIT done: if (stacklen == 0) { JS_ASSERT(!stackbuf); return cx->runtime->emptyString; } if (stacklen < stackmax) { /* * Realloc can fail when shrinking on some FreeBSD versions, so * don't use JS_realloc here; simply let the oversized allocation * be owned by the string in that rare case. */ void *shrunk = JS_realloc(cx, stackbuf, (stacklen+1) * sizeof(jschar)); if (shrunk) stackbuf = (jschar *) shrunk; } stackbuf[stacklen] = 0; str = js_NewString(cx, stackbuf, stacklen); if (str) return str; bad: if (stackbuf) JS_free(cx, stackbuf); return NULL; }
JSBool js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) { JSErrNum errorNumber; const JSErrorFormatString *errorString; JSExnType exn; jsval tv[4]; JSTempValueRooter tvr; JSBool ok; JSObject *errProto, *errObject; JSString *messageStr, *filenameStr; /* * Tell our caller to report immediately if cx has no active frames, or if * this report is just a warning. */ JS_ASSERT(reportp); if (!cx->fp || JSREPORT_IS_WARNING(reportp->flags)) return JS_FALSE; /* Find the exception index associated with this error. */ errorNumber = (JSErrNum) reportp->errorNumber; errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber); exn = errorString ? (JSExnType) errorString->exnType : JSEXN_NONE; JS_ASSERT(exn < JSEXN_LIMIT); #if defined( DEBUG_mccabe ) && defined ( PRINTNAMES ) /* Print the error name and the associated exception name to stderr */ fprintf(stderr, "%s\t%s\n", errortoexnname[errorNumber].name, errortoexnname[errorNumber].exception); #endif /* * Return false (no exception raised) if no exception is associated * with the given error number. */ if (exn == JSEXN_NONE) return JS_FALSE; /* * Prevent runaway recursion, via cx->generatingError. If an out-of-memory * error occurs, no exception object will be created, but we don't assume * that OOM is the only kind of error that subroutines of this function * called below might raise. */ if (cx->generatingError) return JS_FALSE; /* After this point the control must flow through the label out. */ cx->generatingError = JS_TRUE; /* Protect the newly-created strings below from nesting GCs. */ memset(tv, 0, sizeof tv); JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(tv), tv, &tvr); /* * Try to get an appropriate prototype by looking up the corresponding * exception constructor name in the scope chain of the current context's * top stack frame, or in the global object if no frame is active. */ ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(exceptions[exn].key), &errProto); if (!ok) goto out; tv[0] = OBJECT_TO_JSVAL(errProto); errObject = js_NewObject(cx, &js_ErrorClass, errProto, NULL, 0); if (!errObject) { ok = JS_FALSE; goto out; } tv[1] = OBJECT_TO_JSVAL(errObject); messageStr = JS_NewStringCopyZ(cx, message); if (!messageStr) { ok = JS_FALSE; goto out; } tv[2] = STRING_TO_JSVAL(messageStr); filenameStr = JS_NewStringCopyZ(cx, reportp->filename); if (!filenameStr) { ok = JS_FALSE; goto out; } tv[3] = STRING_TO_JSVAL(filenameStr); ok = InitExnPrivate(cx, errObject, messageStr, filenameStr, reportp->lineno, reportp); if (!ok) goto out; JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject)); /* Flag the error report passed in to indicate an exception was raised. */ reportp->flags |= JSREPORT_EXCEPTION; out: JS_POP_TEMP_ROOT(cx, &tvr); cx->generatingError = JS_FALSE; return ok; }
static JSErrorReport * CopyErrorReport(JSContext *cx, JSErrorReport *report) { /* * We use a single malloc block to make a deep copy of JSErrorReport with * the following layout: * JSErrorReport * array of copies of report->messageArgs * jschar array with characters for all messageArgs * jschar array with characters for ucmessage * jschar array with characters for uclinebuf and uctokenptr * char array with characters for linebuf and tokenptr * char array with characters for filename * Such layout together with the properties enforced by the following * asserts does not need any extra alignment padding. */ JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0); JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0); size_t filenameSize; size_t linebufSize; size_t uclinebufSize; size_t ucmessageSize; size_t i, argsArraySize, argsCopySize, argSize; size_t mallocSize; JSErrorReport *copy; uint8 *cursor; #define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar)) filenameSize = report->filename ? strlen(report->filename) + 1 : 0; linebufSize = report->linebuf ? strlen(report->linebuf) + 1 : 0; uclinebufSize = report->uclinebuf ? JS_CHARS_SIZE(report->uclinebuf) : 0; ucmessageSize = 0; argsArraySize = 0; argsCopySize = 0; if (report->ucmessage) { ucmessageSize = JS_CHARS_SIZE(report->ucmessage); if (report->messageArgs) { for (i = 0; report->messageArgs[i]; ++i) argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]); /* Non-null messageArgs should have at least one non-null arg. */ JS_ASSERT(i != 0); argsArraySize = (i + 1) * sizeof(const jschar *); } } /* * The mallocSize can not overflow since it represents the sum of the * sizes of already allocated objects. */ mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize + ucmessageSize + uclinebufSize + linebufSize + filenameSize; cursor = (uint8 *)JS_malloc(cx, mallocSize); if (!cursor) return NULL; copy = (JSErrorReport *)cursor; memset(cursor, 0, sizeof(JSErrorReport)); cursor += sizeof(JSErrorReport); if (argsArraySize != 0) { copy->messageArgs = (const jschar **)cursor; cursor += argsArraySize; for (i = 0; report->messageArgs[i]; ++i) { copy->messageArgs[i] = (const jschar *)cursor; argSize = JS_CHARS_SIZE(report->messageArgs[i]); memcpy(cursor, report->messageArgs[i], argSize); cursor += argSize; } copy->messageArgs[i] = NULL; JS_ASSERT(cursor == (uint8 *)copy->messageArgs[0] + argsCopySize); } if (report->ucmessage) { copy->ucmessage = (const jschar *)cursor; memcpy(cursor, report->ucmessage, ucmessageSize); cursor += ucmessageSize; } if (report->uclinebuf) { copy->uclinebuf = (const jschar *)cursor; memcpy(cursor, report->uclinebuf, uclinebufSize); cursor += uclinebufSize; if (report->uctokenptr) { copy->uctokenptr = copy->uclinebuf + (report->uctokenptr - report->uclinebuf); } } if (report->linebuf) { copy->linebuf = (const char *)cursor; memcpy(cursor, report->linebuf, linebufSize); cursor += linebufSize; if (report->tokenptr) { copy->tokenptr = copy->linebuf + (report->tokenptr - report->linebuf); } } if (report->filename) { copy->filename = (const char *)cursor; memcpy(cursor, report->filename, filenameSize); } JS_ASSERT(cursor + filenameSize == (uint8 *)copy + mallocSize); /* Copy non-pointer members. */ copy->lineno = report->lineno; copy->errorNumber = report->errorNumber; /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */ copy->flags = report->flags; #undef JS_CHARS_SIZE return copy; }
JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::downRecursion() { JSStackFrame* fp = cx->fp; if ((jsbytecode*)fragment->ip < fp->script->code || (jsbytecode*)fragment->ip >= fp->script->code + fp->script->length) { RETURN_STOP_A("inner recursive call must compile first"); } /* Adjust the stack by the budget the down-frame needs. */ int slots = NativeStackSlots(cx, 1) - NativeStackSlots(cx, 0); JS_ASSERT(unsigned(slots) == NativeStackSlots(cx, 1) - fp->argc - 2 - fp->script->nfixed - 2); /* Guard that there is enough stack space. */ JS_ASSERT(tree->maxNativeStackSlots >= tree->nativeStackBase / sizeof(double)); int guardSlots = slots + tree->maxNativeStackSlots - tree->nativeStackBase / sizeof(double); LIns* sp_top = lir->ins2(LIR_piadd, lirbuf->sp, lir->insImmWord(guardSlots * sizeof(double))); guard(true, lir->ins2(LIR_plt, sp_top, eos_ins), OOM_EXIT); /* Guard that there is enough call stack space. */ LIns* rp_top = lir->ins2(LIR_piadd, lirbuf->rp, lir->insImmWord(sizeof(FrameInfo*))); guard(true, lir->ins2(LIR_plt, rp_top, eor_ins), OOM_EXIT); /* * For every slot in the new frame that is not in the tracker, create a load * in the tracker. This is necessary because otherwise snapshot() will see * missing imports and use the down frame, rather than the new frame. * This won't affect performance because the loads will be killed if not * used. */ ImportFrameSlotsVisitor visitor(*this); VisitStackSlots(visitor, cx, callDepth); /* Add space for a new JIT frame. */ lirbuf->sp = lir->ins2(LIR_piadd, lirbuf->sp, lir->insImmWord(slots * sizeof(double))); lir->insStorei(lirbuf->sp, lirbuf->state, offsetof(InterpState, sp)); lirbuf->rp = lir->ins2(LIR_piadd, lirbuf->rp, lir->insImmWord(sizeof(FrameInfo*))); lir->insStorei(lirbuf->rp, lirbuf->state, offsetof(InterpState, rp)); --callDepth; clearCurrentFrameSlotsFromTracker(nativeFrameTracker); /* * If the callee and caller have identical call sites, this is a down- * recursive loop. Otherwise something special happened. For example, a * recursive call that is unwinding could nest back down recursively again. * In this case, we build a fragment that ideally we'll never invoke * directly, but link from a down-recursive branch. The UNLINKED_EXIT tells * closeLoop() that the peer trees should match the recursive pc, not the * tree pc. */ VMSideExit* exit; if ((jsbytecode*)fragment->root->ip == fp->script->code) exit = snapshot(UNSTABLE_LOOP_EXIT); else exit = snapshot(RECURSIVE_UNLINKED_EXIT); exit->recursive_pc = fp->script->code; debug_only_print0(LC_TMTracer, "Compiling down-recursive function call.\n"); JS_ASSERT(tree->recursion != Recursion_Disallowed); tree->recursion = Recursion_Detected; return closeLoop(exit); }
JSTokenType js_GetToken(JSContext *cx, JSTokenStream *ts) { JSTokenType tt; JSToken *tp; int32 c; JSAtom *atom; JSBool hadUnicodeEscape; #define INIT_TOKENBUF(tb) ((tb)->ptr = (tb)->base) #define FINISH_TOKENBUF(tb) if (!AddToTokenBuf(cx, tb, 0)) RETURN(TOK_ERROR) #define TOKEN_LENGTH(tb) ((tb)->ptr - (tb)->base - 1) #define RETURN(tt) { if (tt == TOK_ERROR) ts->flags |= TSF_ERROR; \ tp->pos.end.index = ts->linepos + \ (ts->linebuf.ptr - ts->linebuf.base) - \ ts->ungetpos; \ return (tp->type = tt); } /* If there was a fatal error, keep returning TOK_ERROR. */ if (ts->flags & TSF_ERROR) return TOK_ERROR; /* Check for a pushed-back token resulting from mismatching lookahead. */ while (ts->lookahead != 0) { ts->lookahead--; ts->cursor = (ts->cursor + 1) & NTOKENS_MASK; tt = CURRENT_TOKEN(ts).type; if (tt != TOK_EOL || (ts->flags & TSF_NEWLINES)) return tt; } retry: do { c = GetChar(ts); if (c == '\n') { ts->flags &= ~TSF_DIRTYLINE; if (ts->flags & TSF_NEWLINES) break; } } while (JS_ISSPACE(c)); ts->cursor = (ts->cursor + 1) & NTOKENS_MASK; tp = &CURRENT_TOKEN(ts); tp->ptr = ts->linebuf.ptr - 1; tp->pos.begin.index = ts->linepos + (tp->ptr - ts->linebuf.base); tp->pos.begin.lineno = tp->pos.end.lineno = ts->lineno; if (c == EOF) RETURN(TOK_EOF); if (c != '-' && c != '\n') ts->flags |= TSF_DIRTYLINE; hadUnicodeEscape = JS_FALSE; if (JS_ISIDENT_START(c) || (c == '\\' && (c = GetUnicodeEscape(ts), hadUnicodeEscape = JS_ISIDENT_START(c)))) { INIT_TOKENBUF(&ts->tokenbuf); for (;;) { if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) RETURN(TOK_ERROR); c = GetChar(ts); if (c == '\\') { c = GetUnicodeEscape(ts); if (!JS_ISIDENT(c)) break; hadUnicodeEscape = JS_TRUE; } else { if (!JS_ISIDENT(c)) break; } } UngetChar(ts, c); FINISH_TOKENBUF(&ts->tokenbuf); atom = js_AtomizeChars(cx, ts->tokenbuf.base, TOKEN_LENGTH(&ts->tokenbuf), 0); if (!atom) RETURN(TOK_ERROR); if (!hadUnicodeEscape && ATOM_KEYWORD(atom)) { struct keyword *kw = ATOM_KEYWORD(atom); if (JSVERSION_IS_ECMA(cx->version) || kw->version <= cx->version) { tp->t_op = (JSOp) kw->op; RETURN(kw->tokentype); } } tp->t_op = JSOP_NAME; tp->t_atom = atom; RETURN(TOK_NAME); } if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) { jsint radix; const jschar *endptr; jsdouble dval; radix = 10; INIT_TOKENBUF(&ts->tokenbuf); if (c == '0') { if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) RETURN(TOK_ERROR); c = GetChar(ts); if (JS_TOLOWER(c) == 'x') { if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) RETURN(TOK_ERROR); c = GetChar(ts); radix = 16; } else if (JS7_ISDEC(c)) { radix = 8; } } while (JS7_ISHEX(c)) { if (radix < 16) { if (JS7_ISLET(c)) break; /* * We permit 08 and 09 as decimal numbers, which makes our * behaviour a superset of the ECMA numeric grammar. We might * not always be so permissive, so we warn about it. */ if (radix == 8 && c >= '8') { if (!js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_WARNING, JSMSG_BAD_OCTAL, c == '8' ? "08" : "09")) { RETURN(TOK_ERROR); } radix = 10; } } if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) RETURN(TOK_ERROR); c = GetChar(ts); } if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) { if (c == '.') { do { if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) RETURN(TOK_ERROR); c = GetChar(ts); } while (JS7_ISDEC(c)); } if (JS_TOLOWER(c) == 'e') { if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) RETURN(TOK_ERROR); c = GetChar(ts); if (c == '+' || c == '-') { if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) RETURN(TOK_ERROR); c = GetChar(ts); } if (!JS7_ISDEC(c)) { js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_MISSING_EXPONENT); RETURN(TOK_ERROR); } do { if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) RETURN(TOK_ERROR); c = GetChar(ts); } while (JS7_ISDEC(c)); } } UngetChar(ts, c); FINISH_TOKENBUF(&ts->tokenbuf); if (radix == 10) { if (!js_strtod(cx, ts->tokenbuf.base, &endptr, &dval)) { js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_OUT_OF_MEMORY); RETURN(TOK_ERROR); } } else { if (!js_strtointeger(cx, ts->tokenbuf.base, &endptr, radix, &dval)) { js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_OUT_OF_MEMORY); RETURN(TOK_ERROR); } } tp->t_dval = dval; RETURN(TOK_NUMBER); } if (c == '"' || c == '\'') { int32 val, qc = c; INIT_TOKENBUF(&ts->tokenbuf); while ((c = GetChar(ts)) != qc) { if (c == '\n' || c == EOF) { UngetChar(ts, c); js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_UNTERMINATED_STRING); RETURN(TOK_ERROR); } if (c == '\\') { switch (c = GetChar(ts)) { case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; default: if ('0' <= c && c < '8') { val = JS7_UNDEC(c); c = PeekChar(ts); if ('0' <= c && c < '8') { val = 8 * val + JS7_UNDEC(c); GetChar(ts); c = PeekChar(ts); if ('0' <= c && c < '8') { int32 save = val; val = 8 * val + JS7_UNDEC(c); if (val <= 0377) GetChar(ts); else val = save; } } c = (jschar)val; } else if (c == 'u') { jschar cp[4]; if (PeekChars(ts, 4, cp) && JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) { c = (((((JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1])) << 4) + JS7_UNHEX(cp[2])) << 4) + JS7_UNHEX(cp[3]); SkipChars(ts, 4); } } else if (c == 'x') { jschar cp[2]; if (PeekChars(ts, 2, cp) && JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) { c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]); SkipChars(ts, 2); } } else if (c == '\n' && JSVERSION_IS_ECMA(cx->version)) { /* ECMA follows C by removing escaped newlines. */ continue; } break; } } if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) RETURN(TOK_ERROR); } FINISH_TOKENBUF(&ts->tokenbuf); atom = js_AtomizeChars(cx, ts->tokenbuf.base, TOKEN_LENGTH(&ts->tokenbuf), 0); if (!atom) RETURN(TOK_ERROR); tp->pos.end.lineno = ts->lineno; tp->t_op = JSOP_STRING; tp->t_atom = atom; RETURN(TOK_STRING); } switch (c) { case '\n': c = TOK_EOL; break; case ';': c = TOK_SEMI; break; case '.': c = TOK_DOT; break; case '[': c = TOK_LB; break; case ']': c = TOK_RB; break; case '{': c = TOK_LC; break; case '}': c = TOK_RC; break; case '(': c = TOK_LP; break; case ')': c = TOK_RP; break; case ',': c = TOK_COMMA; break; case '?': c = TOK_HOOK; break; case ':': /* * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an * object initializer, likewise for setter. */ tp->t_op = JSOP_NOP; c = TOK_COLON; break; case '|': if (MatchChar(ts, c)) { c = TOK_OR; } else if (MatchChar(ts, '=')) { tp->t_op = JSOP_BITOR; c = TOK_ASSIGN; } else { c = TOK_BITOR; } break; case '^': if (MatchChar(ts, '=')) { tp->t_op = JSOP_BITXOR; c = TOK_ASSIGN; } else { c = TOK_BITXOR; } break; case '&': if (MatchChar(ts, c)) { c = TOK_AND; } else if (MatchChar(ts, '=')) { tp->t_op = JSOP_BITAND; c = TOK_ASSIGN; } else { c = TOK_BITAND; } break; case '=': if (MatchChar(ts, c)) { #if JS_HAS_TRIPLE_EQOPS tp->t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : (JSOp)cx->jsop_eq; #else tp->t_op = cx->jsop_eq; #endif c = TOK_EQOP; } else { tp->t_op = JSOP_NOP; c = TOK_ASSIGN; } break; case '!': if (MatchChar(ts, '=')) { #if JS_HAS_TRIPLE_EQOPS tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne; #else tp->t_op = cx->jsop_ne; #endif c = TOK_EQOP; } else { tp->t_op = JSOP_NOT; c = TOK_UNARYOP; } break; case '<': /* NB: treat HTML begin-comment as comment-till-end-of-line */ if (MatchChar(ts, '!')) { if (MatchChar(ts, '-')) { if (MatchChar(ts, '-')) goto skipline; UngetChar(ts, '-'); } UngetChar(ts, '!'); } if (MatchChar(ts, c)) { tp->t_op = JSOP_LSH; c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP; } else { tp->t_op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT; c = TOK_RELOP; } break; case '>': if (MatchChar(ts, c)) { tp->t_op = MatchChar(ts, c) ? JSOP_URSH : JSOP_RSH; c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP; } else { tp->t_op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT; c = TOK_RELOP; } break; case '*': tp->t_op = JSOP_MUL; c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_STAR; break; case '/': if (MatchChar(ts, '/')) { skipline: while ((c = GetChar(ts)) != EOF && c != '\n') /* skip to end of line */; UngetChar(ts, c); goto retry; } if (MatchChar(ts, '*')) { while ((c = GetChar(ts)) != EOF && !(c == '*' && MatchChar(ts, '/'))) { /* Ignore all characters until comment close. */ } if (c == EOF) { js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_UNTERMINATED_COMMENT); RETURN(TOK_ERROR); } goto retry; } #if JS_HAS_REGEXPS if (ts->flags & TSF_REGEXP) { JSObject *obj; uintN flags; INIT_TOKENBUF(&ts->tokenbuf); while ((c = GetChar(ts)) != '/') { if (c == '\n' || c == EOF) { UngetChar(ts, c); js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_UNTERMINATED_REGEXP); RETURN(TOK_ERROR); } if (c == '\\') { if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) RETURN(TOK_ERROR); c = GetChar(ts); } if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) RETURN(TOK_ERROR); } FINISH_TOKENBUF(&ts->tokenbuf); for (flags = 0; ; ) { if (MatchChar(ts, 'g')) flags |= JSREG_GLOB; else if (MatchChar(ts, 'i')) flags |= JSREG_FOLD; else if (MatchChar(ts, 'm')) flags |= JSREG_MULTILINE; else break; } c = PeekChar(ts); if (JS7_ISLET(c)) { tp->ptr = ts->linebuf.ptr - 1; js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_REGEXP_FLAG); (void) GetChar(ts); RETURN(TOK_ERROR); } obj = js_NewRegExpObject(cx, ts, ts->tokenbuf.base, TOKEN_LENGTH(&ts->tokenbuf), flags); if (!obj) RETURN(TOK_ERROR); atom = js_AtomizeObject(cx, obj, 0); if (!atom) RETURN(TOK_ERROR); tp->t_op = JSOP_OBJECT; tp->t_atom = atom; RETURN(TOK_OBJECT); } #endif /* JS_HAS_REGEXPS */ tp->t_op = JSOP_DIV; c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP; break; case '%': tp->t_op = JSOP_MOD; c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP; break; case '~': tp->t_op = JSOP_BITNOT; c = TOK_UNARYOP; break; case '+': if (MatchChar(ts, '=')) { tp->t_op = JSOP_ADD; c = TOK_ASSIGN; } else if (MatchChar(ts, c)) { c = TOK_INC; } else { tp->t_op = JSOP_POS; c = TOK_PLUS; } break; case '-': if (MatchChar(ts, '=')) { tp->t_op = JSOP_SUB; c = TOK_ASSIGN; } else if (MatchChar(ts, c)) { if (PeekChar(ts) == '>' && !(ts->flags & TSF_DIRTYLINE)) goto skipline; c = TOK_DEC; } else { tp->t_op = JSOP_NEG; c = TOK_MINUS; } ts->flags |= TSF_DIRTYLINE; break; #if JS_HAS_SHARP_VARS case '#': { uint32 n; c = GetChar(ts); if (!JS7_ISDEC(c)) { UngetChar(ts, c); goto badchar; } n = (uint32)JS7_UNDEC(c); for (;;) { c = GetChar(ts); if (!JS7_ISDEC(c)) break; n = 10 * n + JS7_UNDEC(c); if (n >= ATOM_INDEX_LIMIT) { js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_SHARPVAR_TOO_BIG); RETURN(TOK_ERROR); } } tp->t_dval = (jsdouble) n; if (JS_HAS_STRICT_OPTION(cx) && (c == '=' || c == '#')) { char buf[20]; JS_snprintf(buf, sizeof buf, "#%u%c", n, c); if (!js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DEPRECATED_USAGE, buf)) { RETURN(TOK_ERROR); } } if (c == '=') RETURN(TOK_DEFSHARP); if (c == '#') RETURN(TOK_USESHARP); goto badchar; } badchar: #endif /* JS_HAS_SHARP_VARS */ default: js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_ILLEGAL_CHARACTER); RETURN(TOK_ERROR); } JS_ASSERT(c < TOK_LIMIT); RETURN((JSTokenType)c); #undef INIT_TOKENBUF #undef FINISH_TOKENBUF #undef TOKEN_LENGTH #undef RETURN }
JSObject * WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, uintN flags) { NS_ASSERTION(!obj->isWrapper() || (obj->isWrapper() && obj->getProxyHandler() == &WaiveXrayWrapperWrapper) || obj->getClass()->ext.innerObject, "wrapped object passed to rewrap"); NS_ASSERTION(JS_GET_CLASS(cx, obj) != &XrayUtils::HolderClass, "trying to wrap a holder"); JSCompartment *origin = obj->compartment(); JSCompartment *target = cx->compartment; JSObject *xrayHolder = nsnull; JSWrapper *wrapper; CompartmentPrivate *targetdata = static_cast<CompartmentPrivate *>(target->data); if (AccessCheck::isChrome(target)) { if (AccessCheck::isChrome(origin)) { wrapper = &JSCrossCompartmentWrapper::singleton; } else { bool isSystem; { JSAutoEnterCompartment ac; if (!ac.enter(cx, obj)) return nsnull; JSObject *globalObj = JS_GetGlobalForObject(cx, obj); JS_ASSERT(globalObj); isSystem = JS_IsSystemObject(cx, globalObj); } if (isSystem) { wrapper = &JSCrossCompartmentWrapper::singleton; } else if (flags & WAIVE_XRAY_WRAPPER_FLAG) { // If we waived the X-ray wrapper for this object, wrap it into a // special wrapper to transitively maintain the X-ray waiver. wrapper = &CrossOriginWrapper::singleton; } else { // Native objects must be wrapped into an X-ray wrapper. if (IS_WN_WRAPPER(obj) || obj->getClass()->ext.innerObject) { typedef XrayWrapper<JSCrossCompartmentWrapper> Xray; wrapper = &Xray::singleton; xrayHolder = Xray::createHolder(cx, obj, parent); if (!xrayHolder) return nsnull; } else { wrapper = &NoWaiverWrapper::singleton; } } } } else if (AccessCheck::isChrome(origin)) { if (obj->isFunction()) { JSFunction *fun = obj->getFunctionPrivate(); if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) { JS_ReportError(cx, "Not allowed to access chrome eval or Function from content"); return nsnull; } } XPCWrappedNative *wn; if (targetdata && (wn = GetWrappedNative(cx, obj)) && wn->HasProto() && wn->GetProto()->ClassIsDOMObject()) { typedef XrayWrapper<JSCrossCompartmentWrapper> Xray; wrapper = &FilteringWrapper<Xray, CrossOriginAccessiblePropertiesOnly>::singleton; xrayHolder = Xray::createHolder(cx, obj, parent); if (!xrayHolder) return nsnull; } else { wrapper = &FilteringWrapper<JSCrossCompartmentWrapper, ExposedPropertiesOnly>::singleton; } } else if (AccessCheck::isSameOrigin(origin, target)) { // Same origin we use a transparent wrapper, unless the compartment asks // for an Xray or the wrapper needs a SOW. if (AccessCheck::needsSystemOnlyWrapper(obj)) { wrapper = &FilteringWrapper<JSCrossCompartmentWrapper, OnlyIfSubjectIsSystem>::singleton; } else if (targetdata && targetdata->wantXrays && (IS_WN_WRAPPER(obj) || obj->getClass()->ext.innerObject)) { typedef XrayWrapper<JSCrossCompartmentWrapper> Xray; wrapper = &Xray::singleton; xrayHolder = Xray::createHolder(cx, obj, parent); if (!xrayHolder) return nsnull; } else { wrapper = &JSCrossCompartmentWrapper::singleton; } } else { NS_ASSERTION(!AccessCheck::needsSystemOnlyWrapper(obj), "bad object exposed across origins"); // Cross origin we want to disallow scripting and limit access to // a predefined set of properties. XrayWrapper adds a property // (.wrappedJSObject) which allows bypassing the XrayWrapper, but // we filter out access to that property. if (!IS_WN_WRAPPER(obj) && !obj->getClass()->ext.innerObject) { wrapper = &FilteringWrapper<JSCrossCompartmentWrapper, CrossOriginAccessiblePropertiesOnly>::singleton; } else { typedef XrayWrapper<JSCrossCompartmentWrapper> Xray; // Location objects can become same origin after navigation, so we might // have to grant transparent access later on. if (IsLocationObject(obj)) { wrapper = &FilteringWrapper<Xray, SameOriginOrCrossOriginAccessiblePropertiesOnly>::singleton; } else { wrapper = &FilteringWrapper<Xray, CrossOriginAccessiblePropertiesOnly>::singleton; } xrayHolder = Xray::createHolder(cx, obj, parent); if (!xrayHolder) return nsnull; } } JSObject *wrapperObj = JSWrapper::New(cx, obj, wrappedProto, parent, wrapper); if (!wrapperObj || !xrayHolder) return wrapperObj; // NB: The fact that the only wrappers to use ProxyExtra are XrayWrappers // is relied on by XPCNativeWrapper.unwrap. wrapperObj->setProxyExtra(js::ObjectValue(*xrayHolder)); return wrapperObj; }
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; JSScript *script = JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, JSVERSION_DEFAULT); if (!script) return NULL; script->noScriptRval = true; script->code[0] = JSOP_STOP; script->code[1] = SRC_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); { JSObject *ctor = 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, object_props, object_methods) || !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, eval, 1, JSFUN_STUB_GSOPS); if (!evalobj) return NULL; self->setOriginalEval(evalobj); /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */ RootedFunction throwTypeError(cx); throwTypeError = js_NewFunction(cx, NULL, ThrowTypeError, 0, 0, self, NULL); if (!throwTypeError) return NULL; if (!throwTypeError->preventExtensions(cx)) return NULL; self->setThrowTypeError(throwTypeError); /* * 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; }
/* * Convert a JS value to an instance of java.lang.Object or one of its subclasses, * performing any necessary type coercion. If non-trivial coercion is required, * the cost value is incremented. If the java_value pass-by-reference argument * is non-NULL, the resulting Java value is stored there. * * Returns JS_TRUE if the conversion is possible, JS_FALSE otherwise */ JSBool jsj_ConvertJSValueToJavaObject(JSContext *cx, JNIEnv *jEnv, jsval v, JavaSignature *signature, int *cost, jobject *java_value, JSBool *is_local_refp) { JSString *jsstr; jclass target_java_class; JS_ASSERT(IS_REFERENCE_TYPE(signature->type)); /* Initialize to default case, in which no new Java object is synthesized to perform the conversion and, therefore, no JNI local references are being held. */ *is_local_refp = JS_FALSE; /* Get the Java type of the target value */ target_java_class = signature->java_class; if (JSVAL_IS_OBJECT(v)) { JSObject *js_obj = JSVAL_TO_OBJECT(v); /* JS null is always assignable to a Java object */ if (!js_obj) { if (java_value) *java_value = NULL; return JS_TRUE; } if (JS_InstanceOf(cx, js_obj, &JavaObject_class, 0) || JS_InstanceOf(cx, js_obj, &JavaArray_class, 0)) { /* The source value is a Java object wrapped inside a JavaScript object. Unwrap the JS object and return the original Java object if it's class makes it assignment-compatible with the target class using Java's assignability rules. */ JavaObjectWrapper *java_wrapper = JS_GetPrivate(cx, js_obj); jobject java_obj = java_wrapper->java_obj; if ((*jEnv)->IsInstanceOf(jEnv, java_obj, target_java_class)) { if (java_value) *java_value = java_obj; return JS_TRUE; } /* Fall through, to attempt conversion to a Java string */ } else if (JS_InstanceOf(cx, js_obj, &JavaClass_class, 0)) { /* We're dealing with the reflection of a Java class */ JavaClassDescriptor *java_class_descriptor = JS_GetPrivate(cx, js_obj); /* Check if target type is java.lang.Class class */ if ((*jEnv)->IsAssignableFrom(jEnv, jlClass, target_java_class)) { if (java_value) *java_value = java_class_descriptor->java_class; return JS_TRUE; } /* Check if target type is netscape.javascript.JSObject wrapper class */ if (convert_js_obj_to_JSObject_wrapper(cx, jEnv, js_obj, signature, cost, java_value)) { if (java_value && *java_value) *is_local_refp = JS_TRUE; return JS_TRUE; } /* Fall through, to attempt conversion to a Java string */ } else if (JS_InstanceOf(cx, js_obj, &JavaMember_class, 0)) { if (!JS_ConvertValue(cx, v, JSTYPE_OBJECT, &v)) return JS_FALSE; return jsj_ConvertJSValueToJavaObject(cx, jEnv, v, signature, cost, java_value, is_local_refp); /* JS Arrays are converted, element by element, to Java arrays */ } else if (JS_IsArrayObject(cx, js_obj) && (signature->type == JAVA_SIGNATURE_ARRAY)) { if (convert_js_array_to_java_array(cx, jEnv, js_obj, signature, java_value)) { if (java_value && *java_value) *is_local_refp = JS_TRUE; return JS_TRUE; } return JS_FALSE; } else { /* Otherwise, see if the target type is the netscape.javascript.JSObject wrapper class or one of its subclasses, in which case a reference is passed to the original JS object by wrapping it inside an instance of netscape.javascript.JSObject */ if (convert_js_obj_to_JSObject_wrapper(cx, jEnv, js_obj, signature, cost, java_value)) { if (java_value && *java_value) *is_local_refp = JS_TRUE; return JS_TRUE; } /* Fall through, to attempt conversion to a Java string */ } } else if (JSVAL_IS_NUMBER(v)) { /* JS numbers, integral or not, can be converted to instances of java.lang.Double */ if ((*jEnv)->IsAssignableFrom(jEnv, jlDouble, target_java_class)) { if (java_value) { jsdouble d; if (!JS_ValueToNumber(cx, v, &d)) goto conversion_error; *java_value = (*jEnv)->NewObject(jEnv, jlDouble, jlDouble_Double, d); if (*java_value) { *is_local_refp = JS_TRUE; } else { jsj_UnexpectedJavaError(cx, jEnv, "Couldn't construct instance of java.lang.Double"); return JS_FALSE; } } return JS_TRUE; } /* Fall through, to attempt conversion to a java.lang.String ... */ } else if (JSVAL_IS_BOOLEAN(v)) { /* JS boolean values can be converted to instances of java.lang.Boolean */ if ((*jEnv)->IsAssignableFrom(jEnv, jlBoolean, target_java_class)) { if (java_value) { JSBool b; if (!JS_ValueToBoolean(cx, v, &b)) goto conversion_error; *java_value = (*jEnv)->NewObject(jEnv, jlBoolean, jlBoolean_Boolean, b); if (*java_value) { *is_local_refp = JS_TRUE; } else { jsj_UnexpectedJavaError(cx, jEnv, "Couldn't construct instance " "of java.lang.Boolean"); return JS_FALSE; } } return JS_TRUE; } /* Fall through, to attempt conversion to a java.lang.String ... */ } /* If the source JS type is either a string or undefined, or if no conversion is possible from a number, boolean or JS object, see if the target type is java.lang.String */ if ((*jEnv)->IsAssignableFrom(jEnv, jlString, target_java_class)) { /* Convert to JS string, if necessary, and then to a Java Unicode string */ jsstr = JS_ValueToString(cx, v); if (jsstr) { if (java_value) { *java_value = jsj_ConvertJSStringToJavaString(cx, jEnv, jsstr); if (*java_value) { *is_local_refp = JS_TRUE; } else { return JS_FALSE; } } return JS_TRUE; } } conversion_error: return JS_FALSE; }
uintptr_t tableOffset() const { JS_ASSERT(frameClass() != FrameSizeClass::None()); return tableOffset_; }
JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::upRecursion() { JS_ASSERT((JSOp)*cx->fp->down->regs->pc == JSOP_CALL); JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, cx->fp->down->script, cx->fp->down->regs->pc)].length == JSOP_CALL_LENGTH); JS_ASSERT(callDepth == 0); /* * If some operation involving interpreter frame slurping failed, go to * that code right away, and don't bother with emitting the up-recursive * guards again. */ if (anchor && (anchor->exitType == RECURSIVE_EMPTY_RP_EXIT || anchor->exitType == RECURSIVE_SLURP_MISMATCH_EXIT || anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT)) { return slurpDownFrames(cx->fp->down->regs->pc); } jsbytecode* return_pc = cx->fp->down->regs->pc; jsbytecode* recursive_pc = return_pc + JSOP_CALL_LENGTH; /* * It is possible that the down frame isn't the same at runtime. It's not * enough to guard on the PC, since the typemap could be different as well. * To deal with this, guard that the FrameInfo on the callstack is 100% * identical. * * Note that though the counted slots is called "downPostSlots", this is * the number of slots after the CALL instruction has theoretically popped * callee/this/argv, but before the return value is pushed. This is * intended since the FrameInfo pushed by down recursion would not have * the return value yet. Instead, when closing the loop, the return value * becomes the sole stack type that deduces type stability. */ unsigned totalSlots = NativeStackSlots(cx, 1); unsigned downPostSlots = totalSlots - NativeStackSlots(cx, 0); FrameInfo* fi = (FrameInfo*)alloca(sizeof(FrameInfo) + totalSlots * sizeof(TraceType)); fi->block = NULL; fi->pc = (jsbytecode*)return_pc; fi->imacpc = NULL; /* * Need to compute this from the down frame, since the stack could have * moved on this one. */ fi->spdist = cx->fp->down->regs->sp - cx->fp->down->slots; JS_ASSERT(cx->fp->argc == cx->fp->down->argc); fi->set_argc(cx->fp->argc, false); fi->callerHeight = downPostSlots; fi->callerArgc = cx->fp->down->argc; if (anchor && anchor->exitType == RECURSIVE_MISMATCH_EXIT) { /* * Case 0: Anchoring off a RECURSIVE_MISMATCH guard. Guard on this FrameInfo. * This is always safe because this point is only reached on simple "call myself" * recursive functions. */ #if defined DEBUG AssertDownFrameIsConsistent(cx, anchor, fi); #endif fi = anchor->recursive_down; } else if (recursive_pc != fragment->root->ip) { /* * Case 1: Guess that down-recursion has to started back out, infer types * from the down frame. */ CaptureStackTypes(cx, 1, fi->get_typemap()); } else { /* Case 2: Guess that up-recursion is backing out, infer types from our Tree. */ JS_ASSERT(tree->nStackTypes == downPostSlots + 1); TraceType* typeMap = fi->get_typemap(); for (unsigned i = 0; i < downPostSlots; i++) typeMap[i] = tree->typeMap[i]; } fi = traceMonitor->frameCache->memoize(fi); /* * Guard that there are more recursive frames. If coming from an anchor * where this was already computed, don't bother doing it again. */ if (!anchor || anchor->exitType != RECURSIVE_MISMATCH_EXIT) { VMSideExit* exit = snapshot(RECURSIVE_EMPTY_RP_EXIT); /* Guard that rp >= sr + 1 */ guard(true, lir->ins2(LIR_pge, lirbuf->rp, lir->ins2(LIR_piadd, lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, sor)), INS_CONSTWORD(sizeof(FrameInfo*)))), exit); } debug_only_printf(LC_TMRecorder, "guardUpRecursive fragment->root=%p fi=%p\n", (void*)fragment->root, (void*)fi); /* Guard that the FrameInfo above is the same FrameInfo pointer. */ VMSideExit* exit = snapshot(RECURSIVE_MISMATCH_EXIT); LIns* prev_rp = lir->insLoad(LIR_ldp, lirbuf->rp, -int32_t(sizeof(FrameInfo*))); guard(true, lir->ins2(LIR_peq, prev_rp, INS_CONSTPTR(fi)), exit); /* * Now it's time to try and close the loop. Get a special exit that points * at the down frame, after the return has been propagated up. */ exit = downSnapshot(fi); LIns* rval_ins = (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) ? get(&stackval(-1)) : NULL; JS_ASSERT(rval_ins != NULL); TraceType returnType = exit->stackTypeMap()[downPostSlots]; if (returnType == TT_INT32) { JS_ASSERT(determineSlotType(&stackval(-1)) == TT_INT32); JS_ASSERT(isPromoteInt(rval_ins)); rval_ins = demote(lir, rval_ins); } UpRecursiveSlotMap slotMap(*this, downPostSlots, rval_ins); for (unsigned i = 0; i < downPostSlots; i++) slotMap.addSlot(exit->stackType(i)); slotMap.addSlot(&stackval(-1)); VisitGlobalSlots(slotMap, cx, *tree->globalSlots); if (recursive_pc == (jsbytecode*)fragment->root->ip) { debug_only_print0(LC_TMTracer, "Compiling up-recursive loop...\n"); } else { debug_only_print0(LC_TMTracer, "Compiling up-recursive branch...\n"); exit->exitType = RECURSIVE_UNLINKED_EXIT; exit->recursive_pc = recursive_pc; } JS_ASSERT(tree->recursion != Recursion_Disallowed); if (tree->recursion != Recursion_Detected) tree->recursion = Recursion_Unwinds; return closeLoop(slotMap, exit); }