📄 jsinterp.c
字号:
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** *//* * JavaScript bytecode interpreter. */#include "jsstddef.h"#include <stdio.h>#include <string.h>#include <math.h>#include "jstypes.h"#include "jsarena.h" /* Added by JSIFY */#include "jsutil.h" /* Added by JSIFY */#include "jsprf.h"#include "jsapi.h"#include "jsarray.h"#include "jsatom.h"#include "jsbool.h"#include "jscntxt.h"#include "jsconfig.h"#include "jsdbgapi.h"#include "jsfun.h"#include "jsgc.h"#include "jsinterp.h"#include "jsiter.h"#include "jslock.h"#include "jsnum.h"#include "jsobj.h"#include "jsopcode.h"#include "jsscan.h"#include "jsscope.h"#include "jsscript.h"#include "jsstr.h"#if JS_HAS_XML_SUPPORT#include "jsxml.h"#endif#ifdef DEBUG#define ASSERT_CACHE_IS_EMPTY(cache) \ JS_BEGIN_MACRO \ JSPropertyCacheEntry *end_, *pce_, entry_; \ JSPropertyCache *cache_ = (cache); \ JS_ASSERT(cache_->empty); \ end_ = &cache_->table[PROPERTY_CACHE_SIZE]; \ for (pce_ = &cache_->table[0]; pce_ < end_; pce_++) { \ PCE_LOAD(cache_, pce_, entry_); \ JS_ASSERT(!PCE_OBJECT(entry_)); \ JS_ASSERT(!PCE_PROPERTY(entry_)); \ } \ JS_END_MACRO#else#define ASSERT_CACHE_IS_EMPTY(cache) ((void)0)#endifvoidjs_FlushPropertyCache(JSContext *cx){ JSPropertyCache *cache; cache = &cx->runtime->propertyCache; if (cache->empty) { ASSERT_CACHE_IS_EMPTY(cache); return; } memset(cache->table, 0, sizeof cache->table); cache->empty = JS_TRUE;#ifdef JS_PROPERTY_CACHE_METERING cache->flushes++;#endif}voidjs_DisablePropertyCache(JSContext *cx){ JS_ASSERT(!cx->runtime->propertyCache.disabled); cx->runtime->propertyCache.disabled = JS_TRUE;}voidjs_EnablePropertyCache(JSContext *cx){ JS_ASSERT(cx->runtime->propertyCache.disabled); ASSERT_CACHE_IS_EMPTY(&cx->runtime->propertyCache); cx->runtime->propertyCache.disabled = JS_FALSE;}/* * Stack macros and functions. These all use a local variable, jsval *sp, to * point to the next free stack slot. SAVE_SP must be called before any call * to a function that may invoke the interpreter. RESTORE_SP must be called * only after return from js_Invoke, because only js_Invoke changes fp->sp. */#define PUSH(v) (*sp++ = (v))#define POP() (*--sp)#ifdef DEBUG#define SAVE_SP(fp) \ (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase), \ (fp)->sp = sp)#else#define SAVE_SP(fp) ((fp)->sp = sp)#endif#define RESTORE_SP(fp) (sp = (fp)->sp)/* * SAVE_SP_AND_PC commits deferred stores of interpreter registers to their * homes in fp, when calling out of the interpreter loop or threaded code. * RESTORE_SP_AND_PC copies the other way, to update registers after a call * to a subroutine that interprets a piece of the current script. */#define SAVE_SP_AND_PC(fp) (SAVE_SP(fp), (fp)->pc = pc)#define RESTORE_SP_AND_PC(fp) (RESTORE_SP(fp), pc = (fp)->pc)/* * Push the generating bytecode's pc onto the parallel pc stack that runs * depth slots below the operands. * * NB: PUSH_OPND uses sp, depth, and pc from its lexical environment. See * js_Interpret for these local variables' declarations and uses. */#define PUSH_OPND(v) (sp[-depth] = (jsval)pc, PUSH(v))#define STORE_OPND(n,v) (sp[(n)-depth] = (jsval)pc, sp[n] = (v))#define POP_OPND() POP()#define FETCH_OPND(n) (sp[n])/* * Push the jsdouble d using sp, depth, and pc from the lexical environment. * Try to convert d to a jsint that fits in a jsval, otherwise GC-alloc space * for it and push a reference. */#define STORE_NUMBER(cx, n, d) \ JS_BEGIN_MACRO \ jsint i_; \ jsval v_; \ \ if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) { \ v_ = INT_TO_JSVAL(i_); \ } else { \ ok = js_NewDoubleValue(cx, d, &v_); \ if (!ok) \ goto out; \ } \ STORE_OPND(n, v_); \ JS_END_MACRO#define STORE_INT(cx, n, i) \ JS_BEGIN_MACRO \ jsval v_; \ \ if (INT_FITS_IN_JSVAL(i)) { \ v_ = INT_TO_JSVAL(i); \ } else { \ ok = js_NewDoubleValue(cx, (jsdouble)(i), &v_); \ if (!ok) \ goto out; \ } \ STORE_OPND(n, v_); \ JS_END_MACRO#define STORE_UINT(cx, n, u) \ JS_BEGIN_MACRO \ jsval v_; \ \ if ((u) <= JSVAL_INT_MAX) { \ v_ = INT_TO_JSVAL(u); \ } else { \ ok = js_NewDoubleValue(cx, (jsdouble)(u), &v_); \ if (!ok) \ goto out; \ } \ STORE_OPND(n, v_); \ JS_END_MACRO#define FETCH_NUMBER(cx, n, d) \ JS_BEGIN_MACRO \ jsval v_; \ \ v_ = FETCH_OPND(n); \ VALUE_TO_NUMBER(cx, v_, d); \ JS_END_MACRO#define FETCH_INT(cx, n, i) \ JS_BEGIN_MACRO \ jsval v_ = FETCH_OPND(n); \ if (JSVAL_IS_INT(v_)) { \ i = JSVAL_TO_INT(v_); \ } else { \ SAVE_SP_AND_PC(fp); \ ok = js_ValueToECMAInt32(cx, v_, &i); \ if (!ok) \ goto out; \ } \ JS_END_MACRO#define FETCH_UINT(cx, n, ui) \ JS_BEGIN_MACRO \ jsval v_ = FETCH_OPND(n); \ jsint i_; \ if (JSVAL_IS_INT(v_) && (i_ = JSVAL_TO_INT(v_)) >= 0) { \ ui = (uint32) i_; \ } else { \ SAVE_SP_AND_PC(fp); \ ok = js_ValueToECMAUint32(cx, v_, &ui); \ if (!ok) \ goto out; \ } \ JS_END_MACRO/* * Optimized conversion macros that test for the desired type in v before * homing sp and calling a conversion function. */#define VALUE_TO_NUMBER(cx, v, d) \ JS_BEGIN_MACRO \ if (JSVAL_IS_INT(v)) { \ d = (jsdouble)JSVAL_TO_INT(v); \ } else if (JSVAL_IS_DOUBLE(v)) { \ d = *JSVAL_TO_DOUBLE(v); \ } else { \ SAVE_SP_AND_PC(fp); \ ok = js_ValueToNumber(cx, v, &d); \ if (!ok) \ goto out; \ } \ JS_END_MACRO#define POP_BOOLEAN(cx, v, b) \ JS_BEGIN_MACRO \ v = FETCH_OPND(-1); \ if (v == JSVAL_NULL) { \ b = JS_FALSE; \ } else if (JSVAL_IS_BOOLEAN(v)) { \ b = JSVAL_TO_BOOLEAN(v); \ } else { \ SAVE_SP_AND_PC(fp); \ ok = js_ValueToBoolean(cx, v, &b); \ if (!ok) \ goto out; \ } \ sp--; \ JS_END_MACRO/* * Convert a primitive string, number or boolean to a corresponding object. * v must not be an object, null or undefined when using this macro. */#define PRIMITIVE_TO_OBJECT(cx, v, obj) \ JS_BEGIN_MACRO \ SAVE_SP(fp); \ if (JSVAL_IS_STRING(v)) { \ obj = js_StringToObject(cx, JSVAL_TO_STRING(v)); \ } else if (JSVAL_IS_INT(v)) { \ obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v)); \ } else if (JSVAL_IS_DOUBLE(v)) { \ obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v)); \ } else { \ JS_ASSERT(JSVAL_IS_BOOLEAN(v)); \ obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v)); \ } \ JS_END_MACRO#define VALUE_TO_OBJECT(cx, v, obj) \ JS_BEGIN_MACRO \ if (!JSVAL_IS_PRIMITIVE(v)) { \ obj = JSVAL_TO_OBJECT(v); \ } else { \ SAVE_SP_AND_PC(fp); \ obj = js_ValueToNonNullObject(cx, v); \ if (!obj) { \ ok = JS_FALSE; \ goto out; \ } \ } \ JS_END_MACRO#define FETCH_OBJECT(cx, n, v, obj) \ JS_BEGIN_MACRO \ v = FETCH_OPND(n); \ VALUE_TO_OBJECT(cx, v, obj); \ STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \ JS_END_MACRO#define VALUE_TO_PRIMITIVE(cx, v, hint, vp) \ JS_BEGIN_MACRO \ if (JSVAL_IS_PRIMITIVE(v)) { \ *vp = v; \ } else { \ SAVE_SP_AND_PC(fp); \ ok = OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, vp); \ if (!ok) \ goto out; \ } \ JS_END_MACROJS_FRIEND_API(jsval *)js_AllocRawStack(JSContext *cx, uintN nslots, void **markp){ jsval *sp; if (markp) *markp = JS_ARENA_MARK(&cx->stackPool); JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval)); if (!sp) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_STACK_OVERFLOW, (cx->fp && cx->fp->fun) ? JS_GetFunctionName(cx->fp->fun) : "script"); } return sp;}JS_FRIEND_API(void)js_FreeRawStack(JSContext *cx, void *mark){ JS_ARENA_RELEASE(&cx->stackPool, mark);}JS_FRIEND_API(jsval *)js_AllocStack(JSContext *cx, uintN nslots, void **markp){ jsval *sp, *vp, *end; JSArena *a; JSStackHeader *sh; JSStackFrame *fp; /* Callers don't check for zero nslots: we do to avoid empty segments. */ if (nslots == 0) { *markp = NULL; return JS_ARENA_MARK(&cx->stackPool); } /* Allocate 2 extra slots for the stack segment header we'll likely need. */ sp = js_AllocRawStack(cx, 2 + nslots, markp); if (!sp) return NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -