/* * Compile a new |RegExpShared| for the |RegExpObject|. * * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as * arguments: * * RegExp, undefined => flags := pattern.flags * RegExp, _ => throw TypeError * _ => pattern := ToString(pattern) if defined(pattern) else '' * flags := ToString(flags) if defined(flags) else '' */ static bool CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args) { if (args.length() == 0) { RegExpStatics *res = cx->regExpStatics(); Rooted<JSAtom*> empty(cx, cx->runtime->emptyString); RegExpObject *reobj = builder.build(empty, res->getFlags()); if (!reobj) return false; args.rval().setObject(*reobj); return true; } Value sourceValue = args[0]; /* * If we get passed in an object whose internal [[Class]] property is * "RegExp", return a new object with the same source/flags. */ if (IsObjectWithClass(sourceValue, ESClass_RegExp, cx)) { /* * Beware, sourceObj may be a (transparent) proxy to a RegExp, so only * use generic (proxyable) operations on sourceObj that do not assume * sourceObj.isRegExp(). */ JSObject &sourceObj = sourceValue.toObject(); if (args.hasDefined(1)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED); return false; } /* * Only extract the 'flags' out of sourceObj; do not reuse the * RegExpShared since it may be from a different compartment. */ RegExpFlag flags; { RegExpGuard g; if (!RegExpToShared(cx, sourceObj, &g)) return false; flags = g->getFlags(); } /* * 'toSource' is a permanent read-only property, so this is equivalent * to executing RegExpObject::getSource on the unwrapped object. */ RootedValue v(cx); if (!sourceObj.getProperty(cx, cx->runtime->atomState.sourceAtom, &v)) return false; Rooted<JSAtom*> sourceAtom(cx, &v.toString()->asAtom()); RegExpObject *reobj = builder.build(sourceAtom, flags); if (!reobj) return false; args.rval().setObject(*reobj); return true; } RootedAtom source(cx); if (sourceValue.isUndefined()) { source = cx->runtime->emptyString; } else { /* Coerce to string and compile. */ JSString *str = ToString(cx, sourceValue); if (!str) return false; source = js_AtomizeString(cx, str); if (!source) return false; } RegExpFlag flags = RegExpFlag(0); if (args.hasDefined(1)) { JSString *flagStr = ToString(cx, args[1]); if (!flagStr) return false; args[1].setString(flagStr); if (!ParseRegExpFlags(cx, flagStr, &flags)) return false; } RootedAtom escapedSourceStr(cx, EscapeNakedForwardSlashes(cx, source)); if (!escapedSourceStr) return false; if (!js::detail::RegExpCode::checkSyntax(cx, NULL, escapedSourceStr)) return false; RegExpStatics *res = cx->regExpStatics(); RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags())); if (!reobj) return false; args.rval().setObject(*reobj); return true; }
/* * Determines whether a value which has passed by ES5 150.2.3 Str steps 1-4's * gauntlet will result in Str returning |undefined|. This function is used to * properly omit properties resulting in such values when stringifying objects, * while properly stringifying such properties as null when they're encountered * in arrays. */ static inline bool IsFilteredValue(const Value &v) { return v.isUndefined() || js_IsCallable(v) || VALUE_IS_XML(v); }
bool js::SetElement(JSContext *cx, ObjectImpl *obj, ObjectImpl *receiver, uint32_t index, const Value &v, bool *succeeded) { NEW_OBJECT_REPRESENTATION_ONLY(); do { MOZ_ASSERT(obj); if (static_cast<JSObject *>(obj)->isProxy()) { // XXX MOZ_NOT_REACHED("NYI: proxy [[SetP]]"); return false; } PropDesc ownDesc; if (!GetOwnElement(cx, obj, index, &ownDesc)) return false; if (!ownDesc.isUndefined()) { if (ownDesc.isDataDescriptor()) { if (!ownDesc.writable()) { *succeeded = false; return true; } if (receiver == obj) { PropDesc updateDesc = PropDesc::valueOnly(v); return DefineElement(cx, receiver, index, updateDesc, false, succeeded); } PropDesc newDesc; return DefineElement(cx, receiver, index, newDesc, false, succeeded); } if (ownDesc.isAccessorDescriptor()) { Value setter = ownDesc.setterValue(); if (setter.isUndefined()) { *succeeded = false; return true; } InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, 1, &args)) return false; /* Push set, receiver, and v as the sole argument. */ args.calleev() = setter; args.thisv() = ObjectValue(*receiver); args[0] = v; *succeeded = true; return Invoke(cx, args); } MOZ_NOT_REACHED("NYI: setting PropertyOp-based property"); return false; } obj = obj->getProto(); if (obj) continue; PropDesc newDesc(v, PropDesc::Writable, PropDesc::Enumerable, PropDesc::Configurable); return DefineElement(cx, receiver, index, newDesc, false, succeeded); } while (false); MOZ_NOT_REACHED("buggy control flow"); return false; }
static JSBool CountHeap(JSContext *cx, unsigned argc, jsval *vp) { jsval v; int32_t traceKind; JSString *str; JSCountHeapTracer countTracer; JSCountHeapNode *node; size_t counter; Value startValue = UndefinedValue(); if (argc > 0) { v = JS_ARGV(cx, vp)[0]; if (JSVAL_IS_TRACEABLE(v)) { startValue = v; } else if (!JSVAL_IS_NULL(v)) { JS_ReportError(cx, "the first argument is not null or a heap-allocated " "thing"); return JS_FALSE; } } traceKind = -1; if (argc > 1) { str = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]); if (!str) return JS_FALSE; JSFlatString *flatStr = JS_FlattenString(cx, str); if (!flatStr) return JS_FALSE; for (size_t i = 0; ;) { if (JS_FlatStringEqualsAscii(flatStr, traceKindNames[i].name)) { traceKind = traceKindNames[i].kind; break; } if (++i == ArrayLength(traceKindNames)) { JSAutoByteString bytes(cx, str); if (!!bytes) JS_ReportError(cx, "trace kind name '%s' is unknown", bytes.ptr()); return JS_FALSE; } } } JS_TracerInit(&countTracer.base, JS_GetRuntime(cx), CountHeapNotify); if (!countTracer.visited.init()) { JS_ReportOutOfMemory(cx); return JS_FALSE; } countTracer.ok = true; countTracer.traceList = NULL; countTracer.recycleList = NULL; if (startValue.isUndefined()) { JS_TraceRuntime(&countTracer.base); } else { JS_CallValueTracer(&countTracer.base, startValue, "root"); } counter = 0; while ((node = countTracer.traceList) != NULL) { if (traceKind == -1 || node->kind == traceKind) counter++; countTracer.traceList = node->next; node->next = countTracer.recycleList; countTracer.recycleList = node; JS_TraceChildren(&countTracer.base, node->thing, node->kind); } while ((node = countTracer.recycleList) != NULL) { countTracer.recycleList = node->next; js_free(node); } if (!countTracer.ok) { JS_ReportOutOfMemory(cx); return false; } *vp = JS_NumberValue((double) counter); return true; }
/* * Compile a new |RegExpPrivate| for the |RegExpObject|. * * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as * arguments: * * RegExp, undefined => flags := pattern.flags * RegExp, _ => throw TypeError * _ => pattern := ToString(pattern) if defined(pattern) else '' * flags := ToString(flags) if defined(flags) else '' */ static bool CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, Value *rval) { if (argc == 0) { if (!ResetRegExpObjectWithStatics(cx, obj, cx->runtime->emptyString)) return false; *rval = ObjectValue(*obj); return true; } Value sourceValue = argv[0]; if (ValueIsRegExp(sourceValue)) { /* * If we get passed in a |RegExpObject| source we return a new * object with the same |RegExpPrivate|. * * Note: the regexp static flags are not taken into consideration here. */ JSObject &sourceObj = sourceValue.toObject(); if (argc >= 2 && !argv[1].isUndefined()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED); return false; } RegExpPrivate *rep = sourceObj.asRegExp()->getPrivate(); if (!rep) return false; rep->incref(cx); if (!ResetRegExpObject(cx, obj, AlreadyIncRefed<RegExpPrivate>(rep))) return false; *rval = ObjectValue(*obj); return true; } JSString *sourceStr; if (sourceValue.isUndefined()) { sourceStr = cx->runtime->emptyString; } else { /* Coerce to string and compile. */ sourceStr = js_ValueToString(cx, sourceValue); if (!sourceStr) return false; } RegExpFlag flags = RegExpFlag(0); if (argc > 1 && !argv[1].isUndefined()) { JSString *flagStr = js_ValueToString(cx, argv[1]); if (!flagStr) return false; argv[1].setString(flagStr); if (!ParseRegExpFlags(cx, flagStr, &flags)) return false; } JSString *escapedSourceStr = EscapeNakedForwardSlashes(cx, sourceStr); if (!escapedSourceStr) return false; if (!ResetRegExpObjectWithStatics(cx, obj, escapedSourceStr, flags)) return false; *rval = ObjectValue(*obj); return true; }
bool js::GetElement(JSContext *cx, ObjectImpl *obj, ObjectImpl *receiver, uint32_t index, Value *vp) { NEW_OBJECT_REPRESENTATION_ONLY(); do { MOZ_ASSERT(obj); if (static_cast<JSObject *>(obj)->isProxy()) { // XXX MOZ_NOT_REACHED("NYI: proxy [[GetP]]"); return false; } PropDesc desc; if (!GetOwnElement(cx, obj, index, &desc)) return false; /* No property? Recur or bottom out. */ if (desc.isUndefined()) { obj = obj->getProto(); if (obj) continue; vp->setUndefined(); return true; } /* If it's a data property, return the value. */ if (desc.isDataDescriptor()) { *vp = desc.value(); return true; } /* If it's an accessor property, call its [[Get]] with the receiver. */ if (desc.isAccessorDescriptor()) { Value get = desc.getterValue(); if (get.isUndefined()) { vp->setUndefined(); return true; } InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, 0, &args)) return false; /* Push get, receiver, and no args. */ args.calleev() = get; args.thisv() = ObjectValue(*receiver); bool ok = Invoke(cx, args); *vp = args.rval(); return ok; } /* Otherwise it's a PropertyOp-based property. XXX handle this! */ MOZ_NOT_REACHED("NYI: handle PropertyOp'd properties here"); return false; } while (false); MOZ_NOT_REACHED("buggy control flow"); return false; }
// Common code implementing direct and indirect eval. // // Evaluate call.argv[2], if it is a string, in the context of the given calling // frame, with the provided scope chain, with the semantics of either a direct // or indirect eval (see ES5 10.4.2). If this is an indirect eval, scopeobj // must be a global object. // // On success, store the completion value in call.rval and return true. static bool EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *caller, HandleObject scopeobj) { JS_ASSERT((evalType == INDIRECT_EVAL) == (caller == NULL)); JS_ASSERT_IF(evalType == INDIRECT_EVAL, scopeobj->isGlobal()); AssertInnerizedScopeChain(cx, *scopeobj); if (!scopeobj->global().isRuntimeCodeGenEnabled(cx)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_EVAL); return false; } // ES5 15.1.2.1 step 1. if (args.length() < 1) { args.rval().setUndefined(); return true; } if (!args[0].isString()) { args.rval() = args[0]; return true; } JSString *str = args[0].toString(); // ES5 15.1.2.1 steps 2-8. // Per ES5, indirect eval runs in the global scope. (eval is specified this // way so that the compiler can make assumptions about what bindings may or // may not exist in the current frame if it doesn't see 'eval'.) unsigned staticLevel; RootedValue thisv(cx); if (evalType == DIRECT_EVAL) { staticLevel = caller->script()->staticLevel + 1; // Direct calls to eval are supposed to see the caller's |this|. If we // haven't wrapped that yet, do so now, before we make a copy of it for // the eval code to use. if (!ComputeThis(cx, caller)) return false; thisv = caller->thisValue(); } else { JS_ASSERT(args.callee().global() == *scopeobj); staticLevel = 0; // Use the global as 'this', modulo outerization. JSObject *thisobj = scopeobj->thisObject(cx); if (!thisobj) return false; thisv = ObjectValue(*thisobj); } Rooted<JSLinearString*> linearStr(cx, str->ensureLinear(cx)); if (!linearStr) return false; const jschar *chars = linearStr->chars(); size_t length = linearStr->length(); SkipRoot skip(cx, &chars); // If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON. // Try the JSON parser first because it's much faster. If the eval string // isn't JSON, JSON parsing will probably fail quickly, so little time // will be lost. // // Don't use the JSON parser if the caller is strict mode code, because in // strict mode object literals must not have repeated properties, and the // JSON parser cheerfully (and correctly) accepts them. If you're parsing // JSON with eval and using strict mode, you deserve to be slow. if (length > 2 && ((chars[0] == '[' && chars[length - 1] == ']') || (chars[0] == '(' && chars[length - 1] == ')')) && (!caller || !caller->script()->strictModeCode)) { // Remarkably, JavaScript syntax is not a superset of JSON syntax: // strings in JavaScript cannot contain the Unicode line and paragraph // terminator characters U+2028 and U+2029, but strings in JSON can. // Rather than force the JSON parser to handle this quirk when used by // eval, we simply don't use the JSON parser when either character // appears in the provided string. See bug 657367. for (const jschar *cp = &chars[1], *end = &chars[length - 2]; ; cp++) { if (*cp == 0x2028 || *cp == 0x2029) break; if (cp == end) { bool isArray = (chars[0] == '['); JSONParser parser(cx, isArray ? chars : chars + 1, isArray ? length : length - 2, JSONParser::StrictJSON, JSONParser::NoError); Value tmp; if (!parser.parse(&tmp)) return false; if (tmp.isUndefined()) break; args.rval() = tmp; return true; } } } EvalScriptGuard esg(cx); JSPrincipals *principals = PrincipalsForCompiledCode(args, cx); if (evalType == DIRECT_EVAL && caller->isNonEvalFunctionFrame()) esg.lookupInEvalCache(linearStr, caller->fun(), staticLevel); if (!esg.foundScript()) { unsigned lineno; const char *filename; JSPrincipals *originPrincipals; CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals, evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL : NOT_CALLED_FROM_JSOP_EVAL); CompileOptions options(cx); options.setFileAndLine(filename, lineno) .setCompileAndGo(true) .setNoScriptRval(false) .setPrincipals(principals) .setOriginPrincipals(originPrincipals); JSScript *compiled = frontend::CompileScript(cx, scopeobj, caller, options, chars, length, linearStr, staticLevel); if (!compiled) return false; esg.setNewScript(compiled); } return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType), NULL /* evalInFrame */, &args.rval()); }
/* * Compile a new |RegExpPrivate| for the |RegExpObject|. * * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as * arguments: * * RegExp, undefined => flags := pattern.flags * RegExp, _ => throw TypeError * _ => pattern := ToString(pattern) if defined(pattern) else '' * flags := ToString(flags) if defined(flags) else '' */ static bool CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, uintN argc, Value *argv, Value *rval) { if (argc == 0) { RegExpStatics *res = cx->regExpStatics(); RegExpObject *reobj = builder.build(cx->runtime->emptyString, res->getFlags()); if (!reobj) return false; *rval = ObjectValue(*reobj); return true; } Value sourceValue = argv[0]; if (ValueIsRegExp(sourceValue)) { /* * If we get passed in a |RegExpObject| source we return a new * object with the same source/flags. * * Note: the regexp static flags are not taken into consideration here. */ JSObject &sourceObj = sourceValue.toObject(); if (argc >= 2 && !argv[1].isUndefined()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED); return false; } RegExpObject *reobj = builder.build(sourceObj.asRegExp()); if (!reobj) return false; *rval = ObjectValue(*reobj); return true; } JSLinearString *sourceStr; if (sourceValue.isUndefined()) { sourceStr = cx->runtime->emptyString; } else { /* Coerce to string and compile. */ JSString *str = ToString(cx, sourceValue); if (!str) return false; sourceStr = str->ensureLinear(cx); if (!sourceStr) return false; } RegExpFlag flags = RegExpFlag(0); if (argc > 1 && !argv[1].isUndefined()) { JSString *flagStr = ToString(cx, argv[1]); if (!flagStr) return false; argv[1].setString(flagStr); if (!ParseRegExpFlags(cx, flagStr, &flags)) return false; } JSLinearString *escapedSourceStr = EscapeNakedForwardSlashes(cx, sourceStr); if (!escapedSourceStr) return false; if (!CheckRegExpSyntax(cx, escapedSourceStr)) return false; RegExpStatics *res = cx->regExpStatics(); RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags())); if (!reobj) return NULL; *rval = ObjectValue(*reobj); return true; }
static char * FormatFrame(JSContext *cx, const ScriptFrameIter &iter, char *buf, int num, JSBool showArgs, JSBool showLocals, JSBool showThisProps) { JSScript* script = iter.script(); jsbytecode* pc = iter.pc(); JSAutoCompartment ac(cx, iter.fp()->scopeChain()); const char *filename = script->filename; unsigned lineno = PCToLineNumber(script, pc); JSFunction *fun = iter.fp()->maybeFun(); JSString *funname = NULL; if (fun) funname = fun->atom(); JSObject *callObj = NULL; AutoPropertyDescArray callProps(cx); if (showArgs || showLocals) { callObj = JS_GetFrameCallObject(cx, Jsvalify(iter.fp())); if (callObj) callProps.fetch(callObj); } Value thisVal = UndefinedValue(); AutoPropertyDescArray thisProps(cx); if (ComputeThis(cx, iter.fp())) { thisVal = iter.fp()->thisValue(); if (showThisProps && !thisVal.isPrimitive()) thisProps.fetch(&thisVal.toObject()); } // print the frame number and function name if (funname) { JSAutoByteString funbytes; buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encode(cx, funname)); } else if (fun) { buf = JS_sprintf_append(buf, "%d anonymous(", num); } else { buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num); } if (!buf) return buf; // print the function arguments if (showArgs && callObj) { uint32_t namedArgCount = 0; for (uint32_t i = 0; i < callProps->length; i++) { JSPropertyDesc* desc = &callProps->array[i]; JSAutoByteString nameBytes; const char *name = NULL; if (JSVAL_IS_STRING(desc->id)) name = FormatValue(cx, desc->id, nameBytes); JSAutoByteString valueBytes; const char *value = FormatValue(cx, desc->value, valueBytes); buf = JS_sprintf_append(buf, "%s%s%s%s%s%s", namedArgCount ? ", " : "", name ? name :"", name ? " = " : "", desc->value.isString() ? "\"" : "", value ? value : "?unknown?", desc->value.isString() ? "\"" : ""); if (!buf) return buf; namedArgCount++; } // print any unnamed trailing args (found in 'arguments' object) Value val; if (JS_GetProperty(cx, callObj, "arguments", &val) && val.isObject()) { uint32_t argCount; JSObject* argsObj = &val.toObject(); if (JS_GetProperty(cx, argsObj, "length", &val) && ToUint32(cx, val, &argCount) && argCount > namedArgCount) { for (uint32_t k = namedArgCount; k < argCount; k++) { char number[8]; JS_snprintf(number, 8, "%d", (int) k); if (JS_GetProperty(cx, argsObj, number, &val)) { JSAutoByteString valueBytes; const char *value = FormatValue(cx, val, valueBytes); buf = JS_sprintf_append(buf, "%s%s%s%s", k ? ", " : "", val.isString() ? "\"" : "", value ? value : "?unknown?", val.isString() ? "\"" : ""); if (!buf) return buf; } } } } } // print filename and line number buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n", fun ? ")" : "", filename ? filename : "<unknown>", lineno); if (!buf) return buf; // print local variables if (showLocals && callProps->array) { for (uint32_t i = 0; i < callProps->length; i++) { JSPropertyDesc* desc = &callProps->array[i]; JSAutoByteString nameBytes; JSAutoByteString valueBytes; const char *name = FormatValue(cx, desc->id, nameBytes); const char *value = FormatValue(cx, desc->value, valueBytes); if (name && value) { buf = JS_sprintf_append(buf, " %s = %s%s%s\n", name, desc->value.isString() ? "\"" : "", value, desc->value.isString() ? "\"" : ""); if (!buf) return buf; } } } // print the value of 'this' if (showLocals) { if (!thisVal.isUndefined()) { JSAutoByteString thisValBytes; if (JSString* thisValStr = ToString(cx, thisVal)) { if (const char *str = thisValBytes.encode(cx, thisValStr)) { buf = JS_sprintf_append(buf, " this = %s\n", str); if (!buf) return buf; } } } else { buf = JS_sprintf_append(buf, " <failed to get 'this' value>\n"); } } // print the properties of 'this', if it is an object if (showThisProps && thisProps->array) { for (uint32_t i = 0; i < thisProps->length; i++) { JSPropertyDesc* desc = &thisProps->array[i]; if (desc->flags & JSPD_ENUMERATE) { JSAutoByteString nameBytes; JSAutoByteString valueBytes; const char *name = FormatValue(cx, desc->id, nameBytes); const char *value = FormatValue(cx, desc->value, valueBytes); if (name && value) { buf = JS_sprintf_append(buf, " this.%s = %s%s%s\n", name, desc->value.isString() ? "\"" : "", value, desc->value.isString() ? "\"" : ""); if (!buf) return buf; } } } } return buf; }
/* * Determines whether a value which has passed by ES5 150.2.3 Str steps 1-4's * gauntlet will result in Str returning |undefined|. This function is used to * properly omit properties resulting in such values when stringifying objects, * while properly stringifying such properties as null when they're encountered * in arrays. */ static inline bool IsFilteredValue(const Value &v) { return v.isUndefined() || js_IsCallable(v) || (v.isObject() && v.toObject().isXML()); }
bool js::SetElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver, uint32_t index, const Value &v, unsigned resolveFlags, bool *succeeded) { NEW_OBJECT_REPRESENTATION_ONLY(); Rooted<ObjectImpl*> current(cx, obj); MOZ_ASSERT(receiver); do { MOZ_ASSERT(current); if (Downcast(current)->isProxy()) { MOZ_NOT_REACHED("NYI: proxy [[SetP]]"); return false; } PropDesc ownDesc; if (!GetOwnElement(cx, current, index, resolveFlags, &ownDesc)) return false; if (!ownDesc.isUndefined()) { if (ownDesc.isDataDescriptor()) { if (!ownDesc.writable()) { *succeeded = false; return true; } if (receiver == current) { PropDesc updateDesc = PropDesc::valueOnly(v); return DefineElement(cx, receiver, index, updateDesc, false, resolveFlags, succeeded); } PropDesc newDesc; return DefineElement(cx, receiver, index, newDesc, false, resolveFlags, succeeded); } if (ownDesc.isAccessorDescriptor()) { Value setter = ownDesc.setterValue(); if (setter.isUndefined()) { *succeeded = false; return true; } InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, 1, &args)) return false; /* Push set, receiver, and v as the sole argument. */ args.setCallee(setter); args.setThis(ObjectValue(*current)); args[0] = v; *succeeded = true; return Invoke(cx, args); } MOZ_NOT_REACHED("NYI: setting PropertyOp-based property"); return false; } current = current->getProto(); if (current) continue; PropDesc newDesc(v, PropDesc::Writable, PropDesc::Enumerable, PropDesc::Configurable); return DefineElement(cx, receiver, index, newDesc, false, resolveFlags, succeeded); } while (false); MOZ_NOT_REACHED("buggy control flow"); return false; }
bool js::GetElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver, uint32_t index, unsigned resolveFlags, Value *vp) { NEW_OBJECT_REPRESENTATION_ONLY(); Rooted<ObjectImpl*> current(cx, obj); do { MOZ_ASSERT(current); if (Downcast(current)->isProxy()) { MOZ_NOT_REACHED("NYI: proxy [[GetP]]"); return false; } PropDesc desc; if (!GetOwnElement(cx, current, index, resolveFlags, &desc)) return false; /* No property? Recur or bottom out. */ if (desc.isUndefined()) { current = current->getProto(); if (current) continue; vp->setUndefined(); return true; } /* If it's a data property, return the value. */ if (desc.isDataDescriptor()) { *vp = desc.value(); return true; } /* If it's an accessor property, call its [[Get]] with the receiver. */ if (desc.isAccessorDescriptor()) { Value get = desc.getterValue(); if (get.isUndefined()) { vp->setUndefined(); return true; } InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, 0, &args)) return false; /* Push get, receiver, and no args. */ args.setCallee(get); args.setThis(ObjectValue(*current)); bool ok = Invoke(cx, args); *vp = args.rval(); return ok; } /* Otherwise it's a PropertyOp-based property. XXX handle this! */ MOZ_NOT_REACHED("NYI: handle PropertyOp'd properties here"); return false; } while (false); MOZ_NOT_REACHED("buggy control flow"); return false; }
bool JSStructuredCloneWriter::startWrite(const Value &v) { assertSameCompartment(context(), v); if (v.isString()) { return writeString(SCTAG_STRING, v.toString()); } else if (v.isNumber()) { return out.writeDouble(v.toNumber()); } else if (v.isBoolean()) { return out.writePair(SCTAG_BOOLEAN, v.toBoolean()); } else if (v.isNull()) { return out.writePair(SCTAG_NULL, 0); } else if (v.isUndefined()) { return out.writePair(SCTAG_UNDEFINED, 0); } else if (v.isObject()) { RootedObject obj(context(), &v.toObject()); // The object might be a security wrapper. See if we can clone what's // behind it. If we can, unwrap the object. obj = CheckedUnwrap(obj); if (!obj) { JS_ReportError(context(), "Permission denied to access object"); return false; } AutoCompartment ac(context(), obj); bool backref; if (!startObject(obj, &backref)) return false; if (backref) return true; if (obj->isRegExp()) { RegExpObject &reobj = obj->asRegExp(); return out.writePair(SCTAG_REGEXP_OBJECT, reobj.getFlags()) && writeString(SCTAG_STRING, reobj.getSource()); } else if (obj->isDate()) { double d = js_DateGetMsecSinceEpoch(obj); return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(d); } else if (obj->isTypedArray()) { return writeTypedArray(obj); } else if (obj->isArrayBuffer() && obj->asArrayBuffer().hasData()) { return writeArrayBuffer(obj); } else if (obj->isObject() || obj->isArray()) { return traverseObject(obj); } else if (obj->isBoolean()) { return out.writePair(SCTAG_BOOLEAN_OBJECT, obj->asBoolean().unbox()); } else if (obj->isNumber()) { return out.writePair(SCTAG_NUMBER_OBJECT, 0) && out.writeDouble(obj->asNumber().unbox()); } else if (obj->isString()) { return writeString(SCTAG_STRING_OBJECT, obj->asString().unbox()); } if (callbacks && callbacks->write) return callbacks->write(context(), this, obj, closure); /* else fall through */ } JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_UNSUPPORTED_TYPE); return false; }
CompletionType evaluate() { Register<Value> value; double number; std::string s; const char* p; char* end; int base; long integer; Value* x; CompletionType result; SourceElements* program; switch (method) { case Eval: x = getScopeChain()->get("x"); if (!x->isString()) { value = x; break; } s = x->toString(); setSource(s.c_str()); yyparse(); program = getProgram(); #ifdef VERBOSE program->print(); #endif try { result = program->evaluate(); } catch (Value* value) { result.setType(CompletionType::Throw); result.setValue(value); } delete program; if (result.isNormal()) { if (result.getValue()) { value = result.getValue(); } else { value = UndefinedValue::getInstance(); } Value::sweep(true); } else { ASSERT(result.isThrow()); value = result.getValue(); Value::sweep(true); throw result.getValue(); } break; case ParseInt: x = getScopeChain()->get("radix"); if (x->isUndefined()) { base = 0; } else { base = (int) (x->toNumber()); if (base < 2 || 36 < base) { value = new NumberValue(NAN); break; } } s = getScopeChain()->get("string")->toString(); p = s.c_str(); integer = strtol(p, &end, base); if (p == end) { value = new NumberValue(NAN); } else { value = new NumberValue(integer); } break; case ParseFloat: s = getScopeChain()->get("string")->toString(); p = s.c_str(); p = skipSpace(p); number = strtod(p, &end); if (number == 0.0 && p == end) { value = new NumberValue(NAN); } else { value = new NumberValue(number); } break; case IsNaN: number = getScopeChain()->get("number")->toNumber(); value = BoolValue::getInstance(isnan(number) ? true : false); break; case IsFinite: number = getScopeChain()->get("number")->toNumber(); value = BoolValue::getInstance(isfinite(number) ? true : false); break; case DecodeURI: s = getScopeChain()->get("encodedURI")->toString(); value = new StringValue(decode(s, "#" + reservedURISet)); break; case EncodeURI: s = getScopeChain()->get("uri")->toString(); value = new StringValue(encode(s, reservedURISet + unescapedURIComponentSet + "#")); break; case DecodeURIComponent: s = getScopeChain()->get("encodedURIComponent")->toString(); value = new StringValue(decode(s, "")); break; case EncodeURIComponent: s = getScopeChain()->get("uriComponent")->toString(); value = new StringValue(encode(s, unescapedURIComponentSet)); break; case GetOrderedMap: // TODO(ishibashi.kenichi): remove later. Temporary test code. value = createOrderedMap(); break; } return CompletionType(CompletionType::Return, value, ""); }
/* * Determines whether a value which has passed by ES5 150.2.3 Str steps 1-4's * gauntlet will result in Str returning |undefined|. This function is used to * properly omit properties resulting in such values when stringifying objects, * while properly stringifying such properties as null when they're encountered * in arrays. */ static inline bool IsFilteredValue(const Value &v) { return v.isUndefined() || v.isSymbol() || IsCallable(v); }
void StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter) { uint32 exprStackSlots = iter.slots() - script()->nfixed; #ifdef TRACK_SNAPSHOTS iter.spewBailingFrom(); #endif IonSpew(IonSpew_Bailouts, " expr stack slots %u, is function frame %u", exprStackSlots, isFunctionFrame()); if (iter.bailoutKind() == Bailout_ArgumentCheck) { // Temporary hack -- skip the (unused) scopeChain, because it could be // bogus (we can fail before the scope chain slot is set). Strip the // hasScopeChain flag and we'll check this later to run prologue(). iter.skip(); flags_ &= ~StackFrame::HAS_SCOPECHAIN; } else { Value v = iter.read(); if (v.isObject()) { scopeChain_ = &v.toObject(); flags_ |= StackFrame::HAS_SCOPECHAIN; if (isFunctionFrame() && fun()->isHeavyweight()) flags_ |= StackFrame::HAS_CALL_OBJ; } else { JS_ASSERT(v.isUndefined()); } } // Assume that all new stack frames have had their entry flag set if // profiling has been turned on. This will be corrected if necessary // elsewhere. if (cx->runtime->spsProfiler.enabled()) setPushedSPSFrame(); if (isFunctionFrame()) { Value thisv = iter.read(); formals()[-1] = thisv; // The new |this| must have already been constructed prior to an Ion // constructor running. if (isConstructing()) JS_ASSERT(!thisv.isPrimitive()); JS_ASSERT(iter.slots() >= CountArgSlots(fun())); IonSpew(IonSpew_Bailouts, " frame slots %u, nargs %u, nfixed %u", iter.slots(), fun()->nargs, script()->nfixed); for (uint32 i = 0; i < fun()->nargs; i++) { Value arg = iter.read(); formals()[i] = arg; } } exprStackSlots -= CountArgSlots(maybeFun()); for (uint32 i = 0; i < script()->nfixed; i++) { Value slot = iter.read(); slots()[i] = slot; } IonSpew(IonSpew_Bailouts, " pushing %u expression stack slots", exprStackSlots); FrameRegs ®s = cx->regs(); for (uint32 i = 0; i < exprStackSlots; i++) { Value v; // If coming from an invalidation bailout, and this is the topmost // value, and a value override has been specified, don't read from the // iterator. Otherwise, we risk using a garbage value. if (!iter.moreFrames() && i == exprStackSlots - 1 && cx->runtime->hasIonReturnOverride()) v = iter.skip(); else v = iter.read(); *regs.sp++ = v; } unsigned pcOff = iter.pcOffset(); regs.pc = script()->code + pcOff; if (iter.resumeAfter()) regs.pc = GetNextPc(regs.pc); IonSpew(IonSpew_Bailouts, " new PC is offset %u within script %p (line %d)", pcOff, (void *)script(), PCToLineNumber(script(), regs.pc)); JS_ASSERT(exprStackSlots == js_ReconstructStackDepth(cx, script(), regs.pc)); }
void StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter) { uint32_t exprStackSlots = iter.slots() - script()->nfixed; #ifdef TRACK_SNAPSHOTS iter.spewBailingFrom(); #endif IonSpew(IonSpew_Bailouts, " expr stack slots %u, is function frame %u", exprStackSlots, isFunctionFrame()); if (iter.bailoutKind() == Bailout_ArgumentCheck) { // Temporary hack -- skip the (unused) scopeChain, because it could be // bogus (we can fail before the scope chain slot is set). Strip the // hasScopeChain flag. If a call object is needed, it will get handled later // by |ThunkToInterpreter| which call |EnsureHasScopeObjects|. iter.skip(); flags_ &= ~StackFrame::HAS_SCOPECHAIN; // If the script binds arguments, then skip the snapshot slot reserved to hold // its value. if (script()->argumentsHasVarBinding()) iter.skip(); flags_ &= ~StackFrame::HAS_ARGS_OBJ; } else { Value scopeChain = iter.read(); JS_ASSERT(scopeChain.isObject() || scopeChain.isUndefined()); if (scopeChain.isObject()) { scopeChain_ = &scopeChain.toObject(); flags_ |= StackFrame::HAS_SCOPECHAIN; if (isFunctionFrame() && fun()->isHeavyweight()) flags_ |= StackFrame::HAS_CALL_OBJ; } // The second slot will be an arguments object if the script needs one. if (script()->argumentsHasVarBinding()) { Value argsObj = iter.read(); JS_ASSERT(argsObj.isObject() || argsObj.isUndefined()); if (argsObj.isObject()) initArgsObj(argsObj.toObject().asArguments()); } } // Assume that all new stack frames have had their entry flag set if // profiling has been turned on. This will be corrected if necessary // elsewhere. if (cx->runtime->spsProfiler.enabled()) setPushedSPSFrame(); if (isFunctionFrame()) { Value thisv = iter.read(); formals()[-1] = thisv; // The new |this| must have already been constructed prior to an Ion // constructor running. if (isConstructing()) JS_ASSERT(!thisv.isPrimitive()); JS_ASSERT(iter.slots() >= CountArgSlots(script(), fun())); IonSpew(IonSpew_Bailouts, " frame slots %u, nargs %u, nfixed %u", iter.slots(), fun()->nargs, script()->nfixed); for (uint32_t i = 0; i < fun()->nargs; i++) { Value arg = iter.read(); formals()[i] = arg; } } exprStackSlots -= CountArgSlots(script(), maybeFun()); for (uint32_t i = 0; i < script()->nfixed; i++) { Value slot = iter.read(); slots()[i] = slot; } IonSpew(IonSpew_Bailouts, " pushing %u expression stack slots", exprStackSlots); FrameRegs ®s = cx->regs(); for (uint32_t i = 0; i < exprStackSlots; i++) { Value v; // If coming from an invalidation bailout, and this is the topmost // value, and a value override has been specified, don't read from the // iterator. Otherwise, we risk using a garbage value. if (!iter.moreFrames() && i == exprStackSlots - 1 && cx->runtime->hasIonReturnOverride()) v = iter.skip(); else v = iter.read(); *regs.sp++ = v; } unsigned pcOff = iter.pcOffset(); regs.pc = script()->code + pcOff; if (iter.resumeAfter()) regs.pc = GetNextPc(regs.pc); IonSpew(IonSpew_Bailouts, " new PC is offset %u within script %p (line %d)", pcOff, (void *)script(), PCToLineNumber(script(), regs.pc)); // For fun.apply({}, arguments) the reconstructStackDepth will be atleast 4, // but it could be that we inlined the funapply. In that case exprStackSlots, // will have the real arguments in the slots and not always be equal. JS_ASSERT_IF(JSOp(*regs.pc) != JSOP_FUNAPPLY, exprStackSlots == js_ReconstructStackDepth(cx, script(), regs.pc)); }