📄 jsfun.c
字号:
JSMSG_INCOMPATIBLE_PROTO, js_Function_str, "apply", JS_GetStringBytes(str)); } return JS_FALSE; } /* Quell GCC overwarnings. */ aobj = NULL; length = 0; if (argc >= 2) { /* If the 2nd arg is null or void, call the function with 0 args. */ if (JSVAL_IS_NULL(argv[1]) || JSVAL_IS_VOID(argv[1])) { argc = 0; } else { /* The second arg must be an array (or arguments object). */ arraylike = JS_FALSE; if (!JSVAL_IS_PRIMITIVE(argv[1])) { aobj = JSVAL_TO_OBJECT(argv[1]); if (!js_IsArrayLike(cx, aobj, &arraylike, &length)) return JS_FALSE; } if (!arraylike) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS, "apply"); return JS_FALSE; } } } /* Convert the first arg to 'this' and skip over it. */ if (!js_ValueToObject(cx, argv[0], &obj)) return JS_FALSE; /* Allocate stack space for fval, obj, and the args. */ argc = (uintN)JS_MIN(length, ARRAY_INIT_LIMIT - 1); sp = js_AllocStack(cx, 2 + argc, &mark); if (!sp) return JS_FALSE; /* Push fval, obj, and aobj's elements as args. */ *sp++ = fval; *sp++ = OBJECT_TO_JSVAL(obj); for (i = 0; i < argc; i++) { ok = JS_GetElement(cx, aobj, (jsint)i, sp); if (!ok) goto out; sp++; } /* Lift current frame to include the args and do the call. */ fp = cx->fp; oldsp = fp->sp; fp->sp = sp; ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL | JSINVOKE_SKIP_CALLER); /* Store rval and pop stack back to our frame's sp. */ *rval = fp->sp[-1]; fp->sp = oldsp;out: js_FreeStack(cx, mark); return ok;}#ifdef NARCISSUSstatic JSBoolfun_applyConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ JSObject *aobj; uintN length, i; void *mark; jsval *sp, *newsp, *oldsp; JSStackFrame *fp; JSBool ok; if (JSVAL_IS_PRIMITIVE(argv[0]) || (aobj = JSVAL_TO_OBJECT(argv[0]), OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass && OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS, "__applyConstruct__"); return JS_FALSE; } if (!js_GetLengthProperty(cx, aobj, &length)) return JS_FALSE; if (length >= ARRAY_INIT_LIMIT) length = ARRAY_INIT_LIMIT - 1; newsp = sp = js_AllocStack(cx, 2 + length, &mark); if (!sp) return JS_FALSE; fp = cx->fp; oldsp = fp->sp; *sp++ = OBJECT_TO_JSVAL(obj); *sp++ = JSVAL_NULL; /* This is filled automagically. */ for (i = 0; i < length; i++) { ok = JS_GetElement(cx, aobj, (jsint)i, sp); if (!ok) goto out; sp++; } oldsp = fp->sp; fp->sp = sp; ok = js_InvokeConstructor(cx, newsp, length); *rval = fp->sp[-1]; fp->sp = oldsp;out: js_FreeStack(cx, mark); return ok;}#endifstatic JSFunctionSpec function_methods[] = {#if JS_HAS_TOSOURCE {js_toSource_str, fun_toSource, 0,0,0},#endif {js_toString_str, fun_toString, 1,0,0}, {"apply", fun_apply, 2,0,0}, {call_str, fun_call, 1,0,0},#ifdef NARCISSUS {"__applyConstructor__", fun_applyConstructor, 1,0,0},#endif {0,0,0,0,0}};JSBooljs_IsIdentifier(JSString *str){ size_t length; jschar c, *chars, *end, *s; length = JSSTRING_LENGTH(str); if (length == 0) return JS_FALSE; chars = JSSTRING_CHARS(str); c = *chars; if (!JS_ISIDSTART(c)) return JS_FALSE; end = chars + length; for (s = chars + 1; s != end; ++s) { c = *s; if (!JS_ISIDENT(c)) return JS_FALSE; } return !js_IsKeyword(chars, length);}static JSBoolFunction(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ JSStackFrame *fp, *caller; JSFunction *fun; JSObject *parent; uintN i, n, lineno, dupflag; JSAtom *atom; const char *filename; JSObject *obj2; JSProperty *prop; JSScopeProperty *sprop; JSString *str, *arg; void *mark; JSTokenStream *ts; JSPrincipals *principals; jschar *collected_args, *cp; size_t arg_length, args_length, old_args_length; JSTokenType tt; JSBool ok; fp = cx->fp; if (!(fp->flags & JSFRAME_CONSTRUCTING)) { obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); } fun = (JSFunction *) JS_GetPrivate(cx, obj); if (fun) return JS_TRUE; /* * NB: (new Function) is not lexically closed by its caller, it's just an * anonymous function in the top-level scope that its constructor inhabits. * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42, * and so would a call to f from another top-level's script or function. * * In older versions, before call objects, a new Function was adopted by * its running context's globalObject, which might be different from the * top-level reachable from scopeChain (in HTML frames, e.g.). */ parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2])); fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA, parent, cx->runtime->atomState.anonymousAtom); if (!fun) return JS_FALSE; /* * Function is static and not called directly by other functions in this * file, therefore it is callable only as a native function by js_Invoke. * Find the scripted caller, possibly skipping other native frames such as * are built for Function.prototype.call or .apply activations that invoke * Function indirectly from a script. */ JS_ASSERT(!fp->script && fp->fun && fp->fun->u.n.native == Function); caller = JS_GetScriptedCaller(cx, fp); if (caller) { filename = caller->script->filename; lineno = js_PCToLineNumber(cx, caller->script, caller->pc); principals = JS_EvalFramePrincipals(cx, fp, caller); } else { filename = NULL; lineno = 0; principals = NULL; } /* Belt-and-braces: check that the caller has access to parent. */ if (!js_CheckPrincipalsAccess(cx, parent, principals, CLASS_ATOM(cx, Function))) { return JS_FALSE; } n = argc ? argc - 1 : 0; if (n > 0) { /* * Collect the function-argument arguments into one string, separated * by commas, then make a tokenstream from that string, and scan it to * get the arguments. We need to throw the full scanner at the * problem, because the argument string can legitimately contain * comments and linefeeds. XXX It might be better to concatenate * everything up into a function definition and pass it to the * compiler, but doing it this way is less of a delta from the old * code. See ECMA 15.3.2.1. */ args_length = 0; for (i = 0; i < n; i++) { /* Collect the lengths for all the function-argument arguments. */ arg = js_ValueToString(cx, argv[i]); if (!arg) return JS_FALSE; argv[i] = STRING_TO_JSVAL(arg); /* * Check for overflow. The < test works because the maximum * JSString length fits in 2 fewer bits than size_t has. */ old_args_length = args_length; args_length = old_args_length + JSSTRING_LENGTH(arg); if (args_length < old_args_length) { JS_ReportOutOfMemory(cx); return JS_FALSE; } } /* Add 1 for each joining comma and check for overflow (two ways). */ old_args_length = args_length; args_length = old_args_length + n - 1; if (args_length < old_args_length || args_length >= ~(size_t)0 / sizeof(jschar)) { JS_ReportOutOfMemory(cx); return JS_FALSE; } /* * Allocate a string to hold the concatenated arguments, including room * for a terminating 0. Mark cx->tempPool for later release, to free * collected_args and its tokenstream in one swoop. */ mark = JS_ARENA_MARK(&cx->tempPool); JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool, (args_length+1) * sizeof(jschar)); if (!cp) { JS_ReportOutOfMemory(cx); return JS_FALSE; } collected_args = cp; /* * Concatenate the arguments into the new string, separated by commas. */ for (i = 0; i < n; i++) { arg = JSVAL_TO_STRING(argv[i]); arg_length = JSSTRING_LENGTH(arg); (void) js_strncpy(cp, JSSTRING_CHARS(arg), arg_length); cp += arg_length; /* Add separating comma or terminating 0. */ *cp++ = (i + 1 < n) ? ',' : 0; } /* * Make a tokenstream (allocated from cx->tempPool) that reads from * the given string. */ ts = js_NewTokenStream(cx, collected_args, args_length, filename, lineno, principals); if (!ts) { JS_ARENA_RELEASE(&cx->tempPool, mark); return JS_FALSE; } /* The argument string may be empty or contain no tokens. */ tt = js_GetToken(cx, ts); if (tt != TOK_EOF) { for (;;) { /* * Check that it's a name. This also implicitly guards against * TOK_ERROR, which was already reported. */ if (tt != TOK_NAME) goto bad_formal; /* * Get the atom corresponding to the name from the tokenstream; * we're assured at this point that it's a valid identifier. */ atom = CURRENT_TOKEN(ts).t_atom; if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) { goto bad_formal; } sprop = (JSScopeProperty *) prop; dupflag = 0; if (sprop) { ok = JS_TRUE; if (obj2 == obj) { const char *name = js_AtomToPrintableString(cx, atom); /* * A duplicate parameter name. We force a duplicate * node on the SCOPE_LAST_PROP(scope) list with the * same id, distinguished by the SPROP_IS_DUPLICATE * flag, and not mapped by an entry in scope. */ JS_ASSERT(sprop->getter == js_GetArgument); ok = name && js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DUPLICATE_FORMAL, name); dupflag = SPROP_IS_DUPLICATE; } OBJ_DROP_PROPERTY(cx, obj2, prop); if (!ok) goto bad_formal; sprop = NULL; } if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(atom), js_GetArgument, js_SetArgument, SPROP_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, dupflag | SPROP_HAS_SHORTID, fun->nargs)) { goto bad_formal; } if (fun->nargs == JS_BITMASK(16)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_ARGS); goto bad; } fun->nargs++; /* * Get the next token. Stop on end of stream. Otherwise * insist on a comma, get another name, and iterate. */ tt = js_GetToken(cx, ts); if (tt == TOK_EOF) break; if (tt != TOK_COMMA) goto bad_formal; tt = js_GetToken(cx, ts); } } /* Clean up. */ ok = js_CloseTokenStream(cx, ts); JS_ARENA_RELEASE(&cx->tempPool, mark); if (!ok) return JS_FALSE; } if (argc) { str = js_ValueToString(cx, argv[argc-1]); } else { /* Can't use cx->runtime->emptyString because we're called too early. */ str = js_NewStringCopyZ(cx, js_empty_ucstr, 0); } if (!str) return JS_FALSE; if (argv) { /* Use the last arg (or this if argc == 0) as a local GC root. */ argv[(intN)(argc-1)] = STRING_TO_JSVAL(str); } mark = JS_ARENA_MARK(&cx->tempPool); ts =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -