Bug 843462 (part 3) - Use a Vector to build the bytecode. r=jorendorff.
authorNicholas Nethercote <nnethercote@mozilla.com>
Wed, 20 Feb 2013 21:13:28 -0800
changeset 122883 10fad54535e7ea7b58b1d896cde09a8a9a68af43
parent 122882 edd316fee02bef69738c5d4d38224c7e0de188f6
child 122884 0fc2a36c23d8aaa057ad9ae1c7f2c343e1e7d7cd
push id1387
push userphilringnalda@gmail.com
push dateTue, 26 Feb 2013 22:32:56 +0000
treeherderfx-team@ad4cc4e97774 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs843462
milestone22.0a1
Bug 843462 (part 3) - Use a Vector to build the bytecode. r=jorendorff.
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/jsscript.cpp
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -41,22 +41,16 @@
 #include "jsatominlines.h"
 #include "jsscriptinlines.h"
 
 #include "frontend/ParseMaps-inl.h"
 #include "frontend/ParseNode-inl.h"
 #include "frontend/SharedContext-inl.h"
 #include "vm/Shape-inl.h"
 
-/* Allocation chunk counts, must be powers of two in general. */
-#define BYTECODE_CHUNK_LENGTH  1024    /* initial bytecode chunk length */
-
-/* Macros to compute byte sizes from typed element counts. */
-#define BYTECODE_SIZE(n)        ((n) * sizeof(jsbytecode))
-
 using namespace js;
 using namespace js::gc;
 using namespace js::frontend;
 
 using mozilla::DebugOnly;
 
 static bool
 SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which, ptrdiff_t offset);
@@ -125,53 +119,27 @@ BytecodeEmitter::BytecodeEmitter(Bytecod
 bool
 BytecodeEmitter::init()
 {
     return atomIndices.ensureMap(sc->context);
 }
 
 BytecodeEmitter::~BytecodeEmitter()
 {
-    js_free(prolog.base);
-    js_free(main.base);
 }
 
 static ptrdiff_t
 EmitCheck(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t delta)
 {
-    jsbytecode *base = bce->base();
-    jsbytecode *newbase;
-    jsbytecode *next = bce->next();
-    jsbytecode *limit = bce->limit();
-    ptrdiff_t offset = next - base;
-    size_t minlength = offset + delta;
-
-    if (next + delta > limit) {
-        size_t newlength;
-        if (!base) {
-            JS_ASSERT(!next && !limit);
-            newlength = BYTECODE_CHUNK_LENGTH;
-            if (newlength < minlength)     /* make it bigger if necessary */
-                newlength = RoundUpPow2(minlength);
-            newbase = (jsbytecode *) cx->malloc_(BYTECODE_SIZE(newlength));
-        } else {
-            JS_ASSERT(base <= next && next <= limit);
-            newlength = (limit - base) * 2;
-            if (newlength < minlength)     /* make it bigger if necessary */
-                newlength = RoundUpPow2(minlength);
-            newbase = (jsbytecode *) cx->realloc_(base, BYTECODE_SIZE(newlength));
-        }
-        if (!newbase) {
-            js_ReportOutOfMemory(cx);
-            return -1;
-        }
-        JS_ASSERT(newlength >= size_t(offset + delta));
-        bce->current->base = newbase;
-        bce->current->limit = newbase + newlength;
-        bce->current->next = newbase + offset;
+    ptrdiff_t offset = bce->code().length();
+
+    jsbytecode dummy = 0;
+    if (!bce->code().appendN(dummy, delta)) {
+        js_ReportOutOfMemory(cx);
+        return -1;
     }
     return offset;
 }
 
 static StaticBlockObject &
 CurrentBlock(StmtInfoBCE *topStmt)
 {
     JS_ASSERT(topStmt->type == STMT_BLOCK || topStmt->type == STMT_SWITCH);
@@ -181,17 +149,16 @@ CurrentBlock(StmtInfoBCE *topStmt)
 
 static void
 UpdateDepth(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
 {
     jsbytecode *pc = bce->code(target);
     JSOp op = (JSOp) *pc;
     const JSCodeSpec *cs = &js_CodeSpec[op];
 
-
     if (cs->format & JOF_TMPSLOT_MASK) {
         /*
          * An opcode may temporarily consume stack space during execution.
          * Account for this in maxStackDepth separately from uses/defs here.
          */
         unsigned depth = (unsigned) bce->stackDepth +
                       ((cs->format & JOF_TMPSLOT_MASK) >> JOF_TMPSLOT_SHIFT);
         if (depth > bce->maxStackDepth)
@@ -230,94 +197,92 @@ UpdateDecomposeLength(BytecodeEmitter *b
     JS_ASSERT(unsigned(end - start) < 256);
     bce->code(start)[-1] = end - start;
 }
 
 ptrdiff_t
 frontend::Emit1(JSContext *cx, BytecodeEmitter *bce, JSOp op)
 {
     ptrdiff_t offset = EmitCheck(cx, bce, 1);
-
-    if (offset >= 0) {
-        *bce->current->next++ = (jsbytecode)op;
-        UpdateDepth(cx, bce, offset);
-    }
+    if (offset < 0)
+        return -1;
+
+    jsbytecode *code = bce->code(offset);
+    code[0] = jsbytecode(op);
+    UpdateDepth(cx, bce, offset);
     return offset;
 }
 
 ptrdiff_t
 frontend::Emit2(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1)
 {
     ptrdiff_t offset = EmitCheck(cx, bce, 2);
-
-    if (offset >= 0) {
-        jsbytecode *next = bce->next();
-        next[0] = (jsbytecode)op;
-        next[1] = op1;
-        bce->current->next = next + 2;
-        UpdateDepth(cx, bce, offset);
-    }
+    if (offset < 0)
+        return -1;
+
+    jsbytecode *code = bce->code(offset);
+    code[0] = jsbytecode(op);
+    code[1] = op1;
+    UpdateDepth(cx, bce, offset);
     return offset;
 }
 
 ptrdiff_t
 frontend::Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1,
                     jsbytecode op2)
 {
     /* These should filter through EmitVarOp. */
     JS_ASSERT(!IsArgOp(op));
     JS_ASSERT(!IsLocalOp(op));
 
     ptrdiff_t offset = EmitCheck(cx, bce, 3);
-
-    if (offset >= 0) {
-        jsbytecode *next = bce->next();
-        next[0] = (jsbytecode)op;
-        next[1] = op1;
-        next[2] = op2;
-        bce->current->next = next + 3;
-        UpdateDepth(cx, bce, offset);
-    }
+    if (offset < 0)
+        return -1;
+
+    jsbytecode *code = bce->code(offset);
+    code[0] = jsbytecode(op);
+    code[1] = op1;
+    code[2] = op2;
+    UpdateDepth(cx, bce, offset);
     return offset;
 }
 
 ptrdiff_t
 frontend::EmitN(JSContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra)
 {
     ptrdiff_t length = 1 + (ptrdiff_t)extra;
     ptrdiff_t offset = EmitCheck(cx, bce, length);
-
-    if (offset >= 0) {
-        jsbytecode *next = bce->next();
-        *next = (jsbytecode)op;
-        memset(next + 1, 0, BYTECODE_SIZE(extra));
-        bce->current->next = next + length;
-
-        /*
-         * Don't UpdateDepth if op's use-count comes from the immediate
-         * operand yet to be stored in the extra bytes after op.
-         */
-        if (js_CodeSpec[op].nuses >= 0)
-            UpdateDepth(cx, bce, offset);
-    }
+    if (offset < 0)
+        return -1;
+
+    jsbytecode *code = bce->code(offset);
+    code[0] = jsbytecode(op);
+    /* The remaining |extra| bytes are set by the caller */
+
+    /*
+     * Don't UpdateDepth if op's use-count comes from the immediate
+     * operand yet to be stored in the extra bytes after op.
+     */
+    if (js_CodeSpec[op].nuses >= 0)
+        UpdateDepth(cx, bce, offset);
+
     return offset;
 }
 
 static ptrdiff_t
 EmitJump(JSContext *cx, BytecodeEmitter *bce, JSOp op, ptrdiff_t off)
 {
     ptrdiff_t offset = EmitCheck(cx, bce, 5);
-
-    if (offset >= 0) {
-        jsbytecode *next = bce->next();
-        next[0] = (jsbytecode)op;
-        SET_JUMP_OFFSET(next, off);
-        bce->current->next = next + 5;
-        UpdateDepth(cx, bce, offset);
-    }
+    if (offset < 0)
+        return -1;
+
+    jsbytecode *code = bce->code(offset);
+    code[0] = jsbytecode(op);
+    SET_JUMP_OFFSET(code, off);
+    UpdateDepth(cx, bce, offset);
     return offset;
 }
 
 /* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */
 const char js_with_statement_str[] = "with statement";
 const char js_finally_block_str[]  = "finally block";
 const char js_script_str[]         = "script";
 
@@ -705,17 +670,17 @@ PushBlockScopeBCE(BytecodeEmitter *bce, 
 
 // Patches |breaks| and |continues| unless the top statement info record
 // represents a try-catch-finally suite. May fail if a jump offset overflows.
 static bool
 PopStatementBCE(JSContext *cx, BytecodeEmitter *bce)
 {
     StmtInfoBCE *stmt = bce->topStmt;
     if (!stmt->isTrying() &&
-        (!BackPatch(cx, bce, stmt->breaks, bce->next(), JSOP_GOTO) ||
+        (!BackPatch(cx, bce, stmt->breaks, bce->code().end(), JSOP_GOTO) ||
          !BackPatch(cx, bce, stmt->continues, bce->code(stmt->update), JSOP_GOTO)))
     {
         return false;
     }
     FinishPopStatement(bce);
     return true;
 }
 
@@ -723,38 +688,36 @@ static bool
 EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
 {
     const size_t len = 1 + UINT32_INDEX_LEN;
     JS_ASSERT(len == size_t(js_CodeSpec[op].length));
     ptrdiff_t offset = EmitCheck(cx, bce, len);
     if (offset < 0)
         return false;
 
-    jsbytecode *next = bce->next();
-    next[0] = jsbytecode(op);
-    SET_UINT32_INDEX(next, index);
-    bce->current->next = next + len;
+    jsbytecode *code = bce->code(offset);
+    code[0] = jsbytecode(op);
+    SET_UINT32_INDEX(code, index);
     UpdateDepth(cx, bce, offset);
     CheckTypeSet(cx, bce, op);
     return true;
 }
 
 static bool
 EmitIndexOp(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
 {
     const size_t len = js_CodeSpec[op].length;
     JS_ASSERT(len >= 1 + UINT32_INDEX_LEN);
     ptrdiff_t offset = EmitCheck(cx, bce, len);
     if (offset < 0)
         return false;
 
-    jsbytecode *next = bce->next();
-    next[0] = jsbytecode(op);
-    SET_UINT32_INDEX(next, index);
-    bce->current->next = next + len;
+    jsbytecode *code = bce->code(offset);
+    code[0] = jsbytecode(op);
+    SET_UINT32_INDEX(code, index);
     UpdateDepth(cx, bce, offset);
     CheckTypeSet(cx, bce, op);
     return true;
 }
 
 static bool
 EmitAtomOp(JSContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce)
 {
@@ -790,20 +753,19 @@ EmitAtomIncDec(JSContext *cx, JSAtom *at
         return false;
 
     const size_t len = 1 + UINT32_INDEX_LEN + 1;
     JS_ASSERT(size_t(js_CodeSpec[op].length) == len);
     ptrdiff_t offset = EmitCheck(cx, bce, len);
     if (offset < 0)
         return false;
 
-    jsbytecode *next = bce->next();
-    next[0] = jsbytecode(op);
-    SET_UINT32_INDEX(next, index);
-    bce->current->next = next + len;
+    jsbytecode *code = bce->code(offset);
+    code[0] = jsbytecode(op);
+    SET_UINT32_INDEX(code, index);
     UpdateDepth(cx, bce, offset);
     CheckTypeSet(cx, bce, op);
     return true;
 }
 
 static bool
 EmitObjectOp(JSContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce)
 {
@@ -2497,17 +2459,17 @@ frontend::EmitFunctionScript(JSContext *
      * The decompiler has assumptions about what may occur immediately after
      * script->main (e.g., in the case of destructuring params). Thus, put the
      * following ops into the range [script->code, script->main). Note:
      * execution starts from script->code, so this has no semantic effect.
      */
 
     FunctionBox *funbox = bce->sc->asFunctionBox();
     if (funbox->argumentsHasLocalBinding()) {
-        JS_ASSERT(bce->next() == bce->base());  /* See JSScript::argumentsBytecode. */
+        JS_ASSERT(bce->offset() == 0);  /* See JSScript::argumentsBytecode. */
         bce->switchToProlog();
         if (Emit1(cx, bce, JSOP_ARGUMENTS) < 0)
             return false;
         InternalBindingsHandle bindings(bce->script, &bce->script->bindings);
         unsigned varIndex = Bindings::argumentsVarIndex(cx, bindings);
         if (bce->script->varIsAliased(varIndex)) {
             ScopeCoordinate sc;
             sc.hops = 0;
@@ -3413,23 +3375,22 @@ EmitAssignment(JSContext *cx, BytecodeEm
 static bool
 EmitNewInit(JSContext *cx, BytecodeEmitter *bce, JSProtoKey key, ParseNode *pn)
 {
     const size_t len = 1 + UINT32_INDEX_LEN;
     ptrdiff_t offset = EmitCheck(cx, bce, len);
     if (offset < 0)
         return false;
 
-    jsbytecode *next = bce->next();
-    next[0] = JSOP_NEWINIT;
-    next[1] = jsbytecode(key);
-    next[2] = 0;
-    next[3] = 0;
-    next[4] = 0;
-    bce->current->next = next + len;
+    jsbytecode *code = bce->code(offset);
+    code[0] = JSOP_NEWINIT;
+    code[1] = jsbytecode(key);
+    code[2] = 0;
+    code[3] = 0;
+    code[4] = 0;
     UpdateDepth(cx, bce, offset);
     CheckTypeSet(cx, bce, JSOP_NEWINIT);
     return true;
 }
 
 bool
 ParseNode::getConstantValue(JSContext *cx, bool strictChecks, MutableHandleValue vp)
 {
@@ -3799,17 +3760,17 @@ EmitTry(JSContext *cx, BytecodeEmitter *
 
     /* Emit finally handler if any. */
     ptrdiff_t finallyStart = 0;   /* to quell GCC uninitialized warnings */
     if (pn->pn_kid3) {
         /*
          * Fix up the gosubs that might have been emitted before non-local
          * jumps to the finally code.
          */
-        if (!BackPatch(cx, bce, stmtInfo.gosubs(), bce->next(), JSOP_GOSUB))
+        if (!BackPatch(cx, bce, stmtInfo.gosubs(), bce->code().end(), JSOP_GOSUB))
             return false;
 
         finallyStart = bce->offset();
 
         /* Indicate that we're emitting a subroutine body. */
         stmtInfo.type = STMT_SUBROUTINE;
         if (!UpdateSourceCoordNotes(cx, bce, pn->pn_kid3->pn_pos.begin))
             return false;
@@ -3824,17 +3785,17 @@ EmitTry(JSContext *cx, BytecodeEmitter *
     if (!PopStatementBCE(cx, bce))
         return false;
 
     /* ReconstructPCStack needs a NOP here to mark the end of the last catch block. */
     if (Emit1(cx, bce, JSOP_NOP) < 0)
         return false;
 
     /* Fix up the end-of-try/catch jumps to come here. */
-    if (!BackPatch(cx, bce, catchJump, bce->next(), JSOP_GOTO))
+    if (!BackPatch(cx, bce, catchJump, bce->code().end(), JSOP_GOTO))
         return false;
 
     /*
      * Add the try note last, to let post-order give us the right ordering
      * (first to last for a given nesting level, inner to outer by level).
      */
     if (pn->pn_kid2 && !bce->tryNoteList.append(JSTRY_CATCH, depth, tryStart, tryEnd))
         return false;
@@ -4644,17 +4605,17 @@ EmitReturn(JSContext *cx, BytecodeEmitte
      */
     ptrdiff_t top = bce->offset();
 
     if (Emit1(cx, bce, JSOP_RETURN) < 0)
         return false;
     if (!EmitNonLocalJumpFixup(cx, bce, NULL))
         return false;
     if (top + JSOP_RETURN_LENGTH != bce->offset()) {
-        bce->base()[top] = JSOP_SETRVAL;
+        bce->code()[top] = JSOP_SETRVAL;
         if (Emit1(cx, bce, JSOP_RETRVAL) < 0)
             return false;
     }
 
     return true;
 }
 
 static bool
@@ -5222,17 +5183,17 @@ EmitObject(JSContext *cx, BytecodeEmitte
     /*
      * Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing
      * a new object and in source order evaluating each property value and
      * adding the property to the object, without invoking latent setters.
      * We use the JSOP_NEWINIT and JSOP_INITELEM/JSOP_INITPROP bytecodes to
      * ignore setters and to avoid dup'ing and popping the object as each
      * property is added, as JSOP_SETELEM/JSOP_SETPROP would do.
      */
-    ptrdiff_t offset = bce->next() - bce->base();
+    ptrdiff_t offset = bce->offset();
     if (!EmitNewInit(cx, bce, JSProto_Object, pn))
         return false;
 
     /*
      * Try to construct the shape of the object as we go, so we can emit a
      * JSOP_NEWOBJECT with the final shape instead.
      */
     RootedObject obj(cx);
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -56,42 +56,43 @@ class CGConstList {
     size_t length() const { return list.length(); }
     void finish(ConstArray *array);
 };
 
 struct StmtInfoBCE;
 
 // Use zero inline elements because these go on the stack and affect how many
 // nested functions are possible.
+typedef Vector<jsbytecode, 0> BytecodeVector;
 typedef Vector<jssrcnote, 0> SrcNotesVector;
 
 struct BytecodeEmitter
 {
     typedef StmtInfoBCE StmtInfo;
 
     SharedContext   *const sc;      /* context shared between parsing and bytecode generation */
 
     BytecodeEmitter *const parent;  /* enclosing function or global context */
 
     Rooted<JSScript*> script;       /* the JSScript we're ultimately producing */
 
     struct EmitSection {
-        jsbytecode  *base;          /* base of JS bytecode vector */
-        jsbytecode  *limit;         /* one byte beyond end of bytecode */
-        jsbytecode  *next;          /* pointer to next free bytecode */
+        BytecodeVector code;        /* bytecode */
         SrcNotesVector notes;       /* source notes, see below */
         ptrdiff_t   lastNoteOffset; /* code offset for last source note */
         unsigned    currentLine;    /* line number for tree-based srcnote gen */
         unsigned    lastColumn;     /* zero-based column index on currentLine of
                                        last SRC_COLSPAN-annotated opcode */
 
         EmitSection(JSContext *cx, unsigned lineno)
-          : base(NULL), limit(NULL), next(NULL), notes(cx), lastNoteOffset(0),
-            currentLine(lineno), lastColumn(0)
+          : code(cx), notes(cx), lastNoteOffset(0), currentLine(lineno), lastColumn(0)
         {
+            // Start them off moderately large, to avoid repeated resizings
+            // early on.
+            code.reserve(1024);
             notes.reserve(1024);
         }
     };
     EmitSection prolog, main, *current;
 
     Parser          *const parser;  /* the parser */
 
     HandleScript    evalCaller;     /* scripted caller info for eval and dbgapi */
@@ -131,27 +132,27 @@ struct BytecodeEmitter
     const bool      hasGlobalScope:1;   /* frontend::CompileScript's scope chain is the
                                            global object */
 
     const bool      selfHostingMode:1;  /* Emit JSOP_CALLINTRINSIC instead of JSOP_NAME
                                            and assert that JSOP_NAME and JSOP_*GNAME
                                            don't ever get emitted. See the comment for
                                            the field |selfHostingMode| in Parser.h for details. */
 
+    /*
+     * Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
+     * space above their tempMark points. This means that you cannot alloc from
+     * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
+     * destruction.
+     */
     BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, SharedContext *sc,
                     HandleScript script, HandleScript evalCaller, bool hasGlobalScope,
                     unsigned lineno, bool selfHostingMode = false);
     bool init();
 
-    /*
-     * Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
-     * space above their tempMark points. This means that you cannot alloc from
-     * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
-     * destructor call.
-     */
     ~BytecodeEmitter();
 
     bool isAliasedName(ParseNode *pn);
 
     JS_ALWAYS_INLINE
     bool makeAtomIndex(JSAtom *atom, jsatomid *indexp) {
         AtomIndexAddPtr p = atomIndices->lookupForAdd(atom);
         if (p) {
@@ -171,23 +172,20 @@ struct BytecodeEmitter
     bool checkSingletonContext();
 
     bool needsImplicitThis();
 
     void tellDebuggerAboutCompiledScript(JSContext *cx);
 
     TokenStream *tokenStream() { return &parser->tokenStream; }
 
-    jsbytecode *base() const { return current->base; }
-    jsbytecode *limit() const { return current->limit; }
-    jsbytecode *next() const { return current->next; }
-    jsbytecode *code(ptrdiff_t offset) const { return base() + offset; }
-    ptrdiff_t offset() const { return next() - base(); }
-    jsbytecode *prologBase() const { return prolog.base; }
-    ptrdiff_t prologOffset() const { return prolog.next - prolog.base; }
+    BytecodeVector &code() const { return current->code; }
+    jsbytecode *code(ptrdiff_t offset) const { return current->code.begin() + offset; }
+    ptrdiff_t offset() const { return current->code.end() - current->code.begin(); }
+    ptrdiff_t prologOffset() const { return prolog.code.end() - prolog.code.begin(); }
     void switchToMain() { current = &main; }
     void switchToProlog() { current = &prolog; }
 
     SrcNotesVector &notes() const { return current->notes; }
     ptrdiff_t lastNoteOffset() const { return current->lastNoteOffset; }
     unsigned currentLine() const { return current->currentLine; }
     unsigned lastColumn() const { return current->lastColumn; }
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1869,18 +1869,18 @@ JSScript::fullyInitFromEmitter(JSContext
 
     script->length = prologLength + mainLength;
     script->natoms = natoms;
     SharedScriptData *ssd = SharedScriptData::new_(cx, script->length, nsrcnotes, natoms);
     if (!ssd)
         return false;
 
     jsbytecode *code = ssd->data;
-    PodCopy<jsbytecode>(code, bce->prologBase(), prologLength);
-    PodCopy<jsbytecode>(code + prologLength, bce->base(), mainLength);
+    PodCopy<jsbytecode>(code, bce->prolog.code.begin(), prologLength);
+    PodCopy<jsbytecode>(code + prologLength, bce->code().begin(), mainLength);
     if (!FinishTakingSrcNotes(cx, bce, (jssrcnote *)(code + script->length)))
         return false;
     InitAtomMap(cx, bce->atomIndices.getMap(), ssd->atoms(script->length, nsrcnotes));
 
     if (!SaveSharedScriptData(cx, script, ssd))
         return false;
 
     uint32_t nfixed = bce->sc->isFunctionBox() ? script->bindings.numVars() : 0;