Bug 502914 - JIT code can call js_AddProperty when the property already exists (Asser...
[mozilla-central.git] / js / src / jsbuiltins.cpp
blob12b2b1bc0f1aad84d094ad6e3485d5818d412234
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 * May 28, 2008.
20 * The Initial Developer of the Original Code is
21 * Andreas Gal <gal@mozilla.com>
23 * Contributor(s):
24 * Brendan Eich <brendan@mozilla.org>
25 * Mike Shaver <shaver@mozilla.org>
26 * David Anderson <danderson@mozilla.com>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #include <math.h>
44 #include "jsapi.h"
45 #include "jsstdint.h"
46 #include "jsarray.h"
47 #include "jsbool.h"
48 #include "jscntxt.h"
49 #include "jsgc.h"
50 #include "jsiter.h"
51 #include "jslibmath.h"
52 #include "jsmath.h"
53 #include "jsnum.h"
54 #include "prmjtime.h"
55 #include "jsdate.h"
56 #include "jsscope.h"
57 #include "jsstr.h"
58 #include "jsbuiltins.h"
59 #include "jstracer.h"
61 using namespace avmplus;
62 using namespace nanojit;
64 extern jsdouble js_NaN;
66 JS_FRIEND_API(void)
67 js_SetTraceableNativeFailed(JSContext *cx)
69 js_SetBuiltinError(cx);
73 * NB: bool FASTCALL is not compatible with Nanojit's calling convention usage.
74 * Do not use bool FASTCALL, use JSBool only!
77 jsdouble FASTCALL
78 js_dmod(jsdouble a, jsdouble b)
80 if (b == 0.0) {
81 jsdpun u;
82 u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK;
83 u.s.lo = 0xffffffff;
84 return u.d;
86 jsdouble r;
87 #ifdef XP_WIN
88 /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
89 if (JSDOUBLE_IS_FINITE(a) && JSDOUBLE_IS_INFINITE(b))
90 r = a;
91 else
92 #endif
93 r = fmod(a, b);
94 return r;
96 JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_dmod, DOUBLE, DOUBLE, 1, 1)
98 int32 FASTCALL
99 js_imod(int32 a, int32 b)
101 if (a < 0 || b <= 0)
102 return -1;
103 int r = a % b;
104 return r;
106 JS_DEFINE_CALLINFO_2(extern, INT32, js_imod, INT32, INT32, 1, 1)
108 /* The following boxing/unboxing primitives we can't emit inline because
109 they either interact with the GC and depend on Spidermonkey's 32-bit
110 integer representation. */
112 jsval FASTCALL
113 js_BoxDouble(JSContext* cx, jsdouble d)
115 int32 i;
116 if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i))
117 return INT_TO_JSVAL(i);
118 JS_ASSERT(JS_ON_TRACE(cx));
119 jsval v; /* not rooted but ok here because we know GC won't run */
120 if (!js_NewDoubleInRootedValue(cx, d, &v))
121 return JSVAL_ERROR_COOKIE;
122 return v;
124 JS_DEFINE_CALLINFO_2(extern, JSVAL, js_BoxDouble, CONTEXT, DOUBLE, 1, 1)
126 jsval FASTCALL
127 js_BoxInt32(JSContext* cx, int32 i)
129 if (JS_LIKELY(INT_FITS_IN_JSVAL(i)))
130 return INT_TO_JSVAL(i);
131 JS_ASSERT(JS_ON_TRACE(cx));
132 jsval v; /* not rooted but ok here because we know GC won't run */
133 jsdouble d = (jsdouble)i;
134 if (!js_NewDoubleInRootedValue(cx, d, &v))
135 return JSVAL_ERROR_COOKIE;
136 return v;
138 JS_DEFINE_CALLINFO_2(extern, JSVAL, js_BoxInt32, CONTEXT, INT32, 1, 1)
140 jsdouble FASTCALL
141 js_UnboxDouble(jsval v)
143 if (JS_LIKELY(JSVAL_IS_INT(v)))
144 return (jsdouble)JSVAL_TO_INT(v);
145 return *JSVAL_TO_DOUBLE(v);
147 JS_DEFINE_CALLINFO_1(extern, DOUBLE, js_UnboxDouble, JSVAL, 1, 1)
149 int32 FASTCALL
150 js_UnboxInt32(jsval v)
152 if (JS_LIKELY(JSVAL_IS_INT(v)))
153 return JSVAL_TO_INT(v);
154 return js_DoubleToECMAInt32(*JSVAL_TO_DOUBLE(v));
156 JS_DEFINE_CALLINFO_1(extern, INT32, js_UnboxInt32, JSVAL, 1, 1)
158 int32 FASTCALL
159 js_DoubleToInt32(jsdouble d)
161 return js_DoubleToECMAInt32(d);
163 JS_DEFINE_CALLINFO_1(extern, INT32, js_DoubleToInt32, DOUBLE, 1, 1)
165 uint32 FASTCALL
166 js_DoubleToUint32(jsdouble d)
168 return js_DoubleToECMAUint32(d);
170 JS_DEFINE_CALLINFO_1(extern, UINT32, js_DoubleToUint32, DOUBLE, 1, 1)
172 jsdouble FASTCALL
173 js_StringToNumber(JSContext* cx, JSString* str)
175 const jschar* bp;
176 const jschar* end;
177 const jschar* ep;
178 jsdouble d;
180 str->getCharsAndEnd(bp, end);
181 if ((!js_strtod(cx, bp, end, &ep, &d) ||
182 js_SkipWhiteSpace(ep, end) != end) &&
183 (!js_strtointeger(cx, bp, end, &ep, 0, &d) ||
184 js_SkipWhiteSpace(ep, end) != end)) {
185 return js_NaN;
187 return d;
189 JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_StringToNumber, CONTEXT, STRING, 1, 1)
191 int32 FASTCALL
192 js_StringToInt32(JSContext* cx, JSString* str)
194 const jschar* bp;
195 const jschar* end;
196 const jschar* ep;
197 jsdouble d;
199 str->getCharsAndEnd(bp, end);
200 if ((!js_strtod(cx, bp, end, &ep, &d) ||
201 js_SkipWhiteSpace(ep, end) != end) &&
202 (!js_strtointeger(cx, bp, end, &ep, 0, &d) ||
203 js_SkipWhiteSpace(ep, end) != end)) {
204 return 0;
206 return js_DoubleToECMAInt32(d);
208 JS_DEFINE_CALLINFO_2(extern, INT32, js_StringToInt32, CONTEXT, STRING, 1, 1)
210 SideExit* FASTCALL
211 js_CallTree(InterpState* state, Fragment* f)
213 union { NIns *code; GuardRecord* (FASTCALL *func)(InterpState*, Fragment*); } u;
215 u.code = f->code();
216 JS_ASSERT(u.code);
218 GuardRecord* rec;
219 #if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32)
220 SIMULATE_FASTCALL(rec, state, NULL, u.func);
221 #else
222 rec = u.func(state, NULL);
223 #endif
224 VMSideExit* lr = (VMSideExit*)rec->exit;
226 if (lr->exitType == NESTED_EXIT) {
227 /* This only occurs once a tree call guard mismatches and we unwind the tree call stack.
228 We store the first (innermost) tree call guard in state and we will try to grow
229 the outer tree the failing call was in starting at that guard. */
230 if (!state->lastTreeCallGuard) {
231 state->lastTreeCallGuard = lr;
232 FrameInfo** rp = (FrameInfo**)state->rp;
233 state->rpAtLastTreeCall = rp + lr->calldepth;
235 } else {
236 /* If the tree exits on a regular (non-nested) guard, keep updating lastTreeExitGuard
237 with that guard. If we mismatch on a tree call guard, this will contain the last
238 non-nested guard we encountered, which is the innermost loop or branch guard. */
239 state->lastTreeExitGuard = lr;
242 return lr;
244 JS_DEFINE_CALLINFO_2(extern, SIDEEXIT, js_CallTree, INTERPSTATE, FRAGMENT, 0, 0)
246 JSBool FASTCALL
247 js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
249 JS_ASSERT(OBJ_IS_NATIVE(obj));
250 JS_ASSERT(SPROP_HAS_STUB_SETTER(sprop));
252 JS_LOCK_OBJ(cx, obj);
254 JSScope* scope = OBJ_SCOPE(obj);
255 uint32 slot;
256 if (scope->object == obj) {
257 if (sprop == scope->lastProp || SCOPE_HAS_PROPERTY(scope, sprop))
258 goto exit_trace;
259 } else {
260 scope = js_GetMutableScope(cx, obj);
261 if (!scope)
262 goto exit_trace;
265 slot = sprop->slot;
266 if (!scope->table && sprop->parent == scope->lastProp && slot == scope->freeslot) {
267 if (slot < STOBJ_NSLOTS(obj) && !OBJ_GET_CLASS(cx, obj)->reserveSlots) {
268 JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, scope->freeslot)));
269 ++scope->freeslot;
270 } else {
271 if (!js_AllocSlot(cx, obj, &slot))
272 goto exit_trace;
274 if (slot != sprop->slot) {
275 js_FreeSlot(cx, obj, slot);
276 goto exit_trace;
280 js_ExtendScopeShape(cx, scope, sprop);
281 ++scope->entryCount;
282 scope->lastProp = sprop;
283 } else {
284 JSScopeProperty *sprop2 = js_AddScopeProperty(cx, scope, sprop->id,
285 sprop->getter,
286 sprop->setter,
287 SPROP_INVALID_SLOT,
288 sprop->attrs,
289 sprop->flags,
290 sprop->shortid);
291 if (sprop2 != sprop)
292 goto exit_trace;
295 if (js_IsPropertyCacheDisabled(cx))
296 goto exit_trace;
298 JS_UNLOCK_SCOPE(cx, scope);
299 return JS_TRUE;
301 exit_trace:
302 JS_UNLOCK_SCOPE(cx, scope);
303 return JS_FALSE;
305 JS_DEFINE_CALLINFO_3(extern, BOOL, js_AddProperty, CONTEXT, OBJECT, SCOPEPROP, 0, 0)
307 static JSBool
308 HasProperty(JSContext* cx, JSObject* obj, jsid id)
310 // Check that we know how the lookup op will behave.
311 for (JSObject* pobj = obj; pobj; pobj = OBJ_GET_PROTO(cx, pobj)) {
312 if (pobj->map->ops->lookupProperty != js_LookupProperty)
313 return JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID);
314 JSClass* clasp = OBJ_GET_CLASS(cx, pobj);
315 if (clasp->resolve != JS_ResolveStub && clasp != &js_StringClass)
316 return JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID);
319 JSObject* obj2;
320 JSProperty* prop;
321 if (!js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop))
322 return JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID);
323 if (prop)
324 OBJ_DROP_PROPERTY(cx, obj2, prop);
325 return prop != NULL;
328 JSBool FASTCALL
329 js_HasNamedProperty(JSContext* cx, JSObject* obj, JSString* idstr)
331 jsid id;
332 if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id))
333 return JSVAL_TO_BOOLEAN(JSVAL_VOID);
335 return HasProperty(cx, obj, id);
337 JS_DEFINE_CALLINFO_3(extern, BOOL, js_HasNamedProperty, CONTEXT, OBJECT, STRING, 0, 0)
339 JSBool FASTCALL
340 js_HasNamedPropertyInt32(JSContext* cx, JSObject* obj, int32 index)
342 jsid id;
343 if (!js_Int32ToId(cx, index, &id))
344 return JSVAL_TO_BOOLEAN(JSVAL_VOID);
346 return HasProperty(cx, obj, id);
348 JS_DEFINE_CALLINFO_3(extern, BOOL, js_HasNamedPropertyInt32, CONTEXT, OBJECT, INT32, 0, 0)
350 jsval FASTCALL
351 js_CallGetter(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
353 JS_ASSERT(!SPROP_HAS_STUB_GETTER(sprop));
354 jsval v;
355 if (!js_GetSprop(cx, sprop, obj, &v))
356 return JSVAL_ERROR_COOKIE;
357 return v;
359 JS_DEFINE_CALLINFO_3(extern, JSVAL, js_CallGetter, CONTEXT, OBJECT, SCOPEPROP, 0, 0)
361 JSString* FASTCALL
362 js_TypeOfObject(JSContext* cx, JSObject* obj)
364 JSType type = JS_TypeOfValue(cx, OBJECT_TO_JSVAL(obj));
365 return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]);
367 JS_DEFINE_CALLINFO_2(extern, STRING, js_TypeOfObject, CONTEXT, OBJECT, 1, 1)
369 JSString* FASTCALL
370 js_TypeOfBoolean(JSContext* cx, int32 unboxed)
372 /* Watch out for pseudo-booleans. */
373 jsval boxed = PSEUDO_BOOLEAN_TO_JSVAL(unboxed);
374 JS_ASSERT(JSVAL_IS_VOID(boxed) || JSVAL_IS_BOOLEAN(boxed));
375 JSType type = JS_TypeOfValue(cx, boxed);
376 return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]);
378 JS_DEFINE_CALLINFO_2(extern, STRING, js_TypeOfBoolean, CONTEXT, INT32, 1, 1)
380 jsdouble FASTCALL
381 js_BooleanOrUndefinedToNumber(JSContext* cx, int32 unboxed)
383 if (unboxed == JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID))
384 return js_NaN;
385 JS_ASSERT(unboxed == JS_TRUE || unboxed == JS_FALSE);
386 return unboxed;
388 JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_BooleanOrUndefinedToNumber, CONTEXT, INT32, 1, 1)
390 JSString* FASTCALL
391 js_BooleanOrUndefinedToString(JSContext *cx, int32 unboxed)
393 JS_ASSERT(uint32(unboxed) <= 2);
394 return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[unboxed]);
396 JS_DEFINE_CALLINFO_2(extern, STRING, js_BooleanOrUndefinedToString, CONTEXT, INT32, 1, 1)
398 JSObject* FASTCALL
399 js_Arguments(JSContext* cx, JSObject* parent, JSObject* cached)
401 if (cached)
402 return cached;
403 return js_NewObject(cx, &js_ArgumentsClass, NULL, NULL, 0);
405 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_Arguments, CONTEXT, OBJECT, OBJECT, 0, 0)
407 JSObject* FASTCALL
408 js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject* parent)
410 JS_ASSERT(HAS_FUNCTION_CLASS(funobj));
411 JS_ASSERT(HAS_FUNCTION_CLASS(proto));
412 JS_ASSERT(JS_ON_TRACE(cx));
414 JSFunction *fun = (JSFunction*) funobj;
415 JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun);
417 JSObject* closure = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject));
418 if (!closure)
419 return NULL;
421 js_HoldScope(OBJ_SCOPE(proto));
422 closure->map = proto->map;
423 closure->classword = jsuword(&js_FunctionClass);
424 closure->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
425 closure->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
426 closure->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun);
427 for (unsigned i = JSSLOT_PRIVATE + 1; i != JS_INITIAL_NSLOTS; ++i)
428 closure->fslots[i] = JSVAL_VOID;
429 closure->dslots = NULL;
430 return closure;
432 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_NewNullClosure, CONTEXT, OBJECT, OBJECT, OBJECT, 0, 0)