From 2164382abe078ea2024b9dff7fe416a78e3a668f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 001/111] Test262 chapter07 TokenStream: - remove check for isJSFormatChar() in getChar[IgnoreLineEnd](), no longer neccessary in ES5 - amend readRegExp() to catch some additional invalid regular expression literals - update keyword list in stringToKeyword() to match ES5 - getToken(): -- unicode escaped identifiers still need to comply to identifier naming rules -- save string for Token.RESERVED (needed in Parser) -- hex-integer needs at least one hex-digit -- invalid escape sequences in string literals now result in an error Parser: - remove unused second argument of propertyName() to facilitate code reuse - no longer check for isReservedKeywordAsIdentifier() in convertToName() since ES5 allows identifier names in object literals and property access - handle Token.RESERVED in convertToName() --- src/org/mozilla/javascript/Parser.java | 38 ++-- src/org/mozilla/javascript/TokenStream.java | 201 +++++++++----------- 2 files changed, 104 insertions(+), 135 deletions(-) diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index 63abac2118..898bd20e88 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -2617,18 +2617,18 @@ private AstNode propertyAccess(int tt, AstNode pn) case Token.THROW: // needed for generator.throw(); saveNameTokenData(ts.tokenBeg, "throw", ts.lineno); - ref = propertyName(-1, "throw", memberTypeFlags); + ref = propertyName(-1, memberTypeFlags); break; case Token.NAME: // handles: name, ns::name, ns::*, ns::[expr] - ref = propertyName(-1, ts.getString(), memberTypeFlags); + ref = propertyName(-1, memberTypeFlags); break; case Token.MUL: // handles: *, *::name, *::*, *::[expr] saveNameTokenData(ts.tokenBeg, "*", ts.lineno); - ref = propertyName(-1, "*", memberTypeFlags); + ref = propertyName(-1, memberTypeFlags); break; case Token.XMLATTR: @@ -2638,14 +2638,10 @@ private AstNode propertyAccess(int tt, AstNode pn) break; default: - if (compilerEnv.isReservedKeywordAsIdentifier()) { + if (convertToName(token)) { // allow keywords as property names, e.g. ({if: 1}) - String name = Token.keywordToName(token); - if (name != null) { - saveNameTokenData(ts.tokenBeg, name, ts.lineno); - ref = propertyName(-1, name, memberTypeFlags); - break; - } + ref = propertyName(-1, memberTypeFlags); + break; } reportError("msg.no.name.after.dot"); return makeErrorNode(); @@ -2680,12 +2676,12 @@ private AstNode attributeAccess() switch (tt) { // handles: @name, @ns::name, @ns::*, @ns::[expr] case Token.NAME: - return propertyName(atPos, ts.getString(), 0); + return propertyName(atPos, 0); // handles: @*, @*::name, @*::*, @*::[expr] case Token.MUL: saveNameTokenData(ts.tokenBeg, "*", ts.lineno); - return propertyName(atPos, "*", 0); + return propertyName(atPos, 0); // handles @[expr] case Token.LB: @@ -2712,7 +2708,7 @@ private AstNode attributeAccess() * returns a Name node. Returns an ErrorNode for malformed XML * expressions. (For now - might change to return a partial XmlRef.) */ - private AstNode propertyName(int atPos, String s, int memberTypeFlags) + private AstNode propertyName(int atPos, int memberTypeFlags) throws IOException { int pos = atPos != -1 ? atPos : ts.tokenBeg, lineno = ts.lineno; @@ -2916,7 +2912,7 @@ private AstNode name(int ttFlagged, int tt) throws IOException { saveNameTokenData(namePos, nameString, nameLineno); if (compilerEnv.isXmlAvailable()) { - return propertyName(-1, nameString, 0); + return propertyName(-1, 0); } else { return createNameNode(true, Token.NAME); } @@ -3432,12 +3428,14 @@ private void saveNameTokenData(int pos, String name, int lineno) { // Check whether token is a reserved keyword that is allowed as property id. private boolean convertToName(int token) { - if (compilerEnv.isReservedKeywordAsIdentifier()) { - String conv = Token.keywordToName(token); - if (conv != null) { - saveNameTokenData(ts.tokenBeg, conv, ts.lineno); - return true; - } + if (token == Token.RESERVED) { + saveNameTokenData(ts.tokenBeg, ts.getString(), ts.lineno); + return true; + } + String conv = Token.keywordToName(token); + if (conv != null) { + saveNameTokenData(ts.tokenBeg, conv, ts.lineno); + return true; } return false; } diff --git a/src/org/mozilla/javascript/TokenStream.java b/src/org/mozilla/javascript/TokenStream.java index 101fbe786c..7d0b58af46 100644 --- a/src/org/mozilla/javascript/TokenStream.java +++ b/src/org/mozilla/javascript/TokenStream.java @@ -123,73 +123,59 @@ private static int stringToKeyword(String name) // #string_id_map# // The following assumes that Token.EOF == 0 final int + // NullLiteral, BooleanLiteral + Id_null = Token.NULL, + Id_false = Token.FALSE, + Id_true = Token.TRUE, + // Keyword Id_break = Token.BREAK, Id_case = Token.CASE, + Id_catch = Token.CATCH, Id_continue = Token.CONTINUE, + Id_debugger = Token.DEBUGGER, Id_default = Token.DEFAULT, Id_delete = Token.DELPROP, Id_do = Token.DO, Id_else = Token.ELSE, - Id_export = Token.RESERVED, - Id_false = Token.FALSE, + Id_finally = Token.FINALLY, Id_for = Token.FOR, Id_function = Token.FUNCTION, Id_if = Token.IF, Id_in = Token.IN, - Id_let = Token.LET, + Id_instanceof = Token.INSTANCEOF, Id_new = Token.NEW, - Id_null = Token.NULL, Id_return = Token.RETURN, Id_switch = Token.SWITCH, Id_this = Token.THIS, - Id_true = Token.TRUE, + Id_throw = Token.THROW, + Id_try = Token.TRY, Id_typeof = Token.TYPEOF, Id_var = Token.VAR, Id_void = Token.VOID, Id_while = Token.WHILE, Id_with = Token.WITH, - Id_yield = Token.YIELD, - - // the following are #ifdef RESERVE_JAVA_KEYWORDS in jsscan.c - Id_abstract = Token.RESERVED, - Id_boolean = Token.RESERVED, - Id_byte = Token.RESERVED, - Id_catch = Token.CATCH, - Id_char = Token.RESERVED, + // FutureReservedWord Id_class = Token.RESERVED, Id_const = Token.CONST, - Id_debugger = Token.DEBUGGER, - Id_double = Token.RESERVED, Id_enum = Token.RESERVED, + Id_export = Token.RESERVED, Id_extends = Token.RESERVED, - Id_final = Token.RESERVED, - Id_finally = Token.FINALLY, - Id_float = Token.RESERVED, - Id_goto = Token.RESERVED, - Id_implements = Token.RESERVED, Id_import = Token.RESERVED, - Id_instanceof = Token.INSTANCEOF, - Id_int = Token.RESERVED, + Id_super = Token.RESERVED, + // FutureReservedWord (strict-mode) + Id_implements = Token.RESERVED, Id_interface = Token.RESERVED, - Id_long = Token.RESERVED, - Id_native = Token.RESERVED, + Id_let = Token.LET, Id_package = Token.RESERVED, Id_private = Token.RESERVED, Id_protected = Token.RESERVED, Id_public = Token.RESERVED, - Id_short = Token.RESERVED, Id_static = Token.RESERVED, - Id_super = Token.RESERVED, - Id_synchronized = Token.RESERVED, - Id_throw = Token.THROW, - Id_throws = Token.RESERVED, - Id_transient = Token.RESERVED, - Id_try = Token.TRY, - Id_volatile = Token.RESERVED; + Id_yield = Token.YIELD; int id; String s = name; -// #generated# Last update: 2007-04-18 13:53:30 PDT +// #generated# Last update: 2012-02-20 23:50:10 MEZ L0: { id = 0; String X = null; int c; L: switch (s.length()) { case 2: c=s.charAt(1); @@ -199,31 +185,20 @@ private static int stringToKeyword(String name) break L; case 3: switch (s.charAt(0)) { case 'f': if (s.charAt(2)=='r' && s.charAt(1)=='o') {id=Id_for; break L0;} break L; - case 'i': if (s.charAt(2)=='t' && s.charAt(1)=='n') {id=Id_int; break L0;} break L; case 'l': if (s.charAt(2)=='t' && s.charAt(1)=='e') {id=Id_let; break L0;} break L; case 'n': if (s.charAt(2)=='w' && s.charAt(1)=='e') {id=Id_new; break L0;} break L; case 't': if (s.charAt(2)=='y' && s.charAt(1)=='r') {id=Id_try; break L0;} break L; case 'v': if (s.charAt(2)=='r' && s.charAt(1)=='a') {id=Id_var; break L0;} break L; } break L; - case 4: switch (s.charAt(0)) { - case 'b': X="byte";id=Id_byte; break L; - case 'c': c=s.charAt(3); - if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='a') {id=Id_case; break L0;} } - else if (c=='r') { if (s.charAt(2)=='a' && s.charAt(1)=='h') {id=Id_char; break L0;} } - break L; - case 'e': c=s.charAt(3); - if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='l') {id=Id_else; break L0;} } - else if (c=='m') { if (s.charAt(2)=='u' && s.charAt(1)=='n') {id=Id_enum; break L0;} } - break L; - case 'g': X="goto";id=Id_goto; break L; - case 'l': X="long";id=Id_long; break L; - case 'n': X="null";id=Id_null; break L; - case 't': c=s.charAt(3); - if (c=='e') { if (s.charAt(2)=='u' && s.charAt(1)=='r') {id=Id_true; break L0;} } - else if (c=='s') { if (s.charAt(2)=='i' && s.charAt(1)=='h') {id=Id_this; break L0;} } - break L; - case 'v': X="void";id=Id_void; break L; - case 'w': X="with";id=Id_with; break L; + case 4: switch (s.charAt(1)) { + case 'a': X="case";id=Id_case; break L; + case 'h': X="this";id=Id_this; break L; + case 'i': X="with";id=Id_with; break L; + case 'l': X="else";id=Id_else; break L; + case 'n': X="enum";id=Id_enum; break L; + case 'o': X="void";id=Id_void; break L; + case 'r': X="true";id=Id_true; break L; + case 'u': X="null";id=Id_null; break L; } break L; case 5: switch (s.charAt(2)) { case 'a': X="class";id=Id_class; break L; @@ -233,60 +208,46 @@ private static int stringToKeyword(String name) break L; case 'i': X="while";id=Id_while; break L; case 'l': X="false";id=Id_false; break L; - case 'n': c=s.charAt(0); - if (c=='c') { X="const";id=Id_const; } - else if (c=='f') { X="final";id=Id_final; } - break L; - case 'o': c=s.charAt(0); - if (c=='f') { X="float";id=Id_float; } - else if (c=='s') { X="short";id=Id_short; } - break L; + case 'n': X="const";id=Id_const; break L; case 'p': X="super";id=Id_super; break L; case 'r': X="throw";id=Id_throw; break L; case 't': X="catch";id=Id_catch; break L; } break L; - case 6: switch (s.charAt(1)) { - case 'a': X="native";id=Id_native; break L; - case 'e': c=s.charAt(0); - if (c=='d') { X="delete";id=Id_delete; } - else if (c=='r') { X="return";id=Id_return; } + case 6: switch (s.charAt(0)) { + case 'd': X="delete";id=Id_delete; break L; + case 'e': X="export";id=Id_export; break L; + case 'i': X="import";id=Id_import; break L; + case 'p': X="public";id=Id_public; break L; + case 'r': X="return";id=Id_return; break L; + case 's': c=s.charAt(5); + if (c=='c') { X="static";id=Id_static; } + else if (c=='h') { X="switch";id=Id_switch; } break L; - case 'h': X="throws";id=Id_throws; break L; - case 'm': X="import";id=Id_import; break L; - case 'o': X="double";id=Id_double; break L; - case 't': X="static";id=Id_static; break L; - case 'u': X="public";id=Id_public; break L; - case 'w': X="switch";id=Id_switch; break L; - case 'x': X="export";id=Id_export; break L; - case 'y': X="typeof";id=Id_typeof; break L; + case 't': X="typeof";id=Id_typeof; break L; } break L; case 7: switch (s.charAt(1)) { case 'a': X="package";id=Id_package; break L; case 'e': X="default";id=Id_default; break L; case 'i': X="finally";id=Id_finally; break L; - case 'o': X="boolean";id=Id_boolean; break L; case 'r': X="private";id=Id_private; break L; case 'x': X="extends";id=Id_extends; break L; } break L; - case 8: switch (s.charAt(0)) { - case 'a': X="abstract";id=Id_abstract; break L; - case 'c': X="continue";id=Id_continue; break L; - case 'd': X="debugger";id=Id_debugger; break L; - case 'f': X="function";id=Id_function; break L; - case 'v': X="volatile";id=Id_volatile; break L; - } break L; + case 8: c=s.charAt(0); + if (c=='c') { X="continue";id=Id_continue; } + else if (c=='d') { X="debugger";id=Id_debugger; } + else if (c=='f') { X="function";id=Id_function; } + break L; case 9: c=s.charAt(0); if (c=='i') { X="interface";id=Id_interface; } else if (c=='p') { X="protected";id=Id_protected; } - else if (c=='t') { X="transient";id=Id_transient; } break L; case 10: c=s.charAt(1); if (c=='m') { X="implements";id=Id_implements; } else if (c=='n') { X="instanceof";id=Id_instanceof; } break L; - case 12: X="synchronized";id=Id_synchronized; break L; } if (X!=null && X!=s && !X.equals(s)) id = 0; + break L0; } // #/generated# // #/string_id_map# @@ -365,6 +326,7 @@ final int getToken() throws IOException } if (identifierStart) { + boolean checkIdentifierStart = isUnicodeEscapeStart; boolean containsEscape = isUnicodeEscapeStart; for (;;) { if (isUnicodeEscapeStart) { @@ -385,6 +347,18 @@ final int getToken() throws IOException parser.addError("msg.invalid.escape"); return Token.ERROR; } + if (checkIdentifierStart) { + checkIdentifierStart = false; + if (! Character.isJavaIdentifierStart((char)escapeVal)) { + parser.addError("msg.invalid.escape"); + return Token.ERROR; + } + } else { + if (! Character.isJavaIdentifierPart((char)escapeVal)) { + parser.addError("msg.invalid.escape"); + return Token.ERROR; + } + } addToString(escapeVal); isUnicodeEscapeStart = false; } else { @@ -431,6 +405,7 @@ final int getToken() throws IOException } else if (!parser.compilerEnv. isReservedKeywordAsIdentifier()) { + this.string = (String)allStrings.intern(str); return result; } } @@ -459,10 +434,14 @@ final int getToken() throws IOException } if (base == 16) { - while (0 <= Kit.xDigitToInt(c, 0)) { + if (0 > Kit.xDigitToInt(c, 0)) { + parser.addError("msg.caught.nfe"); + return Token.ERROR; + } + do { addToString(c); c = getChar(); - } + } while (0 <= Kit.xDigitToInt(c, 0)); } else { while ('0' <= c && c <= '9') { /* @@ -566,41 +545,31 @@ final int getToken() throws IOException case 'v': c = 0xb; break; case 'u': - // Get 4 hex digits; if the u escape is not - // followed by 4 hex digits, use 'u' + the - // literal character sequence that follows. - int escapeStart = stringBufferTop; - addToString('u'); + // Get 4 hex digits escapeVal = 0; for (int i = 0; i != 4; ++i) { c = getChar(); escapeVal = Kit.xDigitToInt(c, escapeVal); if (escapeVal < 0) { - continue strLoop; + parser.addError("msg.unterminated.string.lit"); + return Token.ERROR; } - addToString(c); } - // prepare for replace of stored 'u' sequence - // by escape value - stringBufferTop = escapeStart; c = escapeVal; break; case 'x': - // Get 2 hex digits, defaulting to 'x'+literal - // sequence, as above. + // Get 2 hex digits c = getChar(); escapeVal = Kit.xDigitToInt(c, 0); if (escapeVal < 0) { - addToString('x'); - continue strLoop; + parser.addError("msg.unterminated.string.lit"); + return Token.ERROR; } else { - int c1 = c; c = getChar(); escapeVal = Kit.xDigitToInt(c, escapeVal); if (escapeVal < 0) { - addToString('x'); - addToString(c1); - continue strLoop; + parser.addError("msg.unterminated.string.lit"); + return Token.ERROR; } else { // got 2 hex digits c = escapeVal; @@ -893,11 +862,6 @@ static boolean isJSSpace(int c) } } - private static boolean isJSFormatChar(int c) - { - return c > 127 && Character.getType((char)c) == Character.FORMAT; - } - /** * Parser calls the method when it gets / or /= in literal context. */ @@ -911,6 +875,12 @@ void readRegExp(int startToken) addToString('='); } else { if (startToken != Token.DIV) Kit.codeBug(); + if (peekChar() == '*') { + tokenEnd = cursor - 1; + this.string = new String(stringBuffer, 0, stringBufferTop); + parser.reportError("msg.unterminated.re.lit"); + return; + } } boolean inCharSet = false; // true if inside a '['..']' pair @@ -926,6 +896,13 @@ void readRegExp(int startToken) if (c == '\\') { addToString(c); c = getChar(); + if (c == '\n' || c == EOF_CHAR) { + ungetChar(c); + tokenEnd = cursor - 1; + this.string = new String(stringBuffer, 0, stringBufferTop); + parser.reportError("msg.unterminated.re.lit"); + return; + } } else if (c == '[') { inCharSet = true; } else if (c == ']') { @@ -1341,9 +1318,6 @@ private int getChar() throws IOException } } else { if (c == BYTE_ORDER_MARK) return c; // BOM is considered whitespace - if (isJSFormatChar(c)) { - continue; - } if (ScriptRuntime.isJSLineTerminator(c)) { lineEndChar = c; c = '\n'; @@ -1387,9 +1361,6 @@ private int getCharIgnoreLineEnd() throws IOException } } else { if (c == BYTE_ORDER_MARK) return c; // BOM is considered whitespace - if (isJSFormatChar(c)) { - continue; - } if (ScriptRuntime.isJSLineTerminator(c)) { lineEndChar = c; c = '\n'; From 6267a13de8c2123604f1b7ff5b9b6d9e516f805c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 002/111] Test262 chapter08 - prevent changing [[Prototype]] on non-extensible objects, cf. ch08/8.6/8.6.2/S8.6.2_A8.js --- src/org/mozilla/javascript/ScriptableObject.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 74d6c029e6..6c8e6be77c 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -866,6 +866,7 @@ public Scriptable getPrototype() */ public void setPrototype(Scriptable m) { + if (!isExtensible()) throw ScriptRuntime.typeError0("msg.not.extensible"); prototypeObject = m; } From 1366936a3f471500048556467c2198f1b61eddd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 003/111] Test262 - chapter11 Scriptable: - hasInstance(): change argument from Scriptable to Object to comply with specification, which defines [[HasInstance]] as SpecOp(any) -> Boolean - this is a public API change! - update the following classes to conform to the new interface: -- Matrix, BaseFunction, BoundFunction, Delegator, NativeIterator, NativeJavaArray, NativeJavaClass, NativeJavaObject, NativeWith, XMLCtor, XMLObjectImpl ScriptableObject: - default implementation of hasInstance() now throws a 'TypeError' to conform to the spec ScriptRuntime: - move primitive check from instanceOf() to jsDelegatesTo() - add updated version of the "Abstract Relational Comparison Algorithm" with flags to control evaluation order - deprecate old cmp_LT() and cmp_LE() methods Interpreter: - ensure proper left-to-right evaluation order for SUB, MUL, DIV, MOD - ensure proper evaluation order for GE, LE, GT, LT Parser: - track property-type in addition to property-name to detect duplicate properties (code adapted from SpiderMonkey parser) - always warn for duplicate getter/setter independent of strict-mode (SpiderMonkey behaviour) - add check for parameter count in case of getter/setter functions --- examples/Matrix.java | 7 ++- src/org/mozilla/javascript/BaseFunction.java | 2 +- src/org/mozilla/javascript/BoundFunction.java | 2 +- src/org/mozilla/javascript/Delegator.java | 2 +- src/org/mozilla/javascript/Interpreter.java | 13 ++-- .../mozilla/javascript/NativeIterator.java | 2 +- .../mozilla/javascript/NativeJavaArray.java | 2 +- .../mozilla/javascript/NativeJavaClass.java | 2 +- .../mozilla/javascript/NativeJavaObject.java | 2 +- src/org/mozilla/javascript/NativeWith.java | 2 +- src/org/mozilla/javascript/Parser.java | 28 ++++++--- src/org/mozilla/javascript/ScriptRuntime.java | 61 ++++++++++++++++--- src/org/mozilla/javascript/Scriptable.java | 4 +- .../mozilla/javascript/ScriptableObject.java | 18 +++--- .../mozilla/javascript/xmlimpl/XMLCtor.java | 2 +- .../javascript/xmlimpl/XMLObjectImpl.java | 2 +- 16 files changed, 107 insertions(+), 44 deletions(-) diff --git a/examples/Matrix.java b/examples/Matrix.java index c60844e0bf..ad56709c5f 100644 --- a/examples/Matrix.java +++ b/examples/Matrix.java @@ -260,8 +260,11 @@ public Object getDefaultValue(Class typeHint) { * true if this appears in value's prototype * chain. */ - public boolean hasInstance(Scriptable value) { - Scriptable proto = value.getPrototype(); + public boolean hasInstance(Object value) { + if (! (value instanceof Scriptable)) { + return false; + } + Scriptable proto = ((Scriptable) value).getPrototype(); while (proto != null) { if (proto.equals(this)) return true; diff --git a/src/org/mozilla/javascript/BaseFunction.java b/src/org/mozilla/javascript/BaseFunction.java index 2d9110d32f..122dd4db95 100644 --- a/src/org/mozilla/javascript/BaseFunction.java +++ b/src/org/mozilla/javascript/BaseFunction.java @@ -101,7 +101,7 @@ public String getTypeOf() * */ @Override - public boolean hasInstance(Scriptable instance) + public boolean hasInstance(Object instance) { Object protoProp = ScriptableObject.getProperty(this, "prototype"); if (protoProp instanceof Scriptable) { diff --git a/src/org/mozilla/javascript/BoundFunction.java b/src/org/mozilla/javascript/BoundFunction.java index 1750669722..592d7738b1 100644 --- a/src/org/mozilla/javascript/BoundFunction.java +++ b/src/org/mozilla/javascript/BoundFunction.java @@ -95,7 +95,7 @@ public Scriptable construct(Context cx, Scriptable scope, Object[] extraArgs) { } @Override - public boolean hasInstance(Scriptable instance) { + public boolean hasInstance(Object instance) { if (targetFunction instanceof Function) { return ((Function) targetFunction).hasInstance(instance); } diff --git a/src/org/mozilla/javascript/Delegator.java b/src/org/mozilla/javascript/Delegator.java index 0056f8c612..462c0673ab 100644 --- a/src/org/mozilla/javascript/Delegator.java +++ b/src/org/mozilla/javascript/Delegator.java @@ -217,7 +217,7 @@ public Object getDefaultValue(Class hint) { /** * @see org.mozilla.javascript.Scriptable#hasInstance */ - public boolean hasInstance(Scriptable instance) { + public boolean hasInstance(Object instance) { return obj.hasInstance(instance); } /** diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index 8691f66e10..dfaa2e3694 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -1105,16 +1105,16 @@ private static Object interpretLoop(Context cx, CallFrame frame, } switch (op) { case Token.GE: - valBln = ScriptRuntime.cmp_LE(rhs, lhs); + valBln = !ScriptRuntime.cmp_LT(lhs, rhs, true, true); break; case Token.LE: - valBln = ScriptRuntime.cmp_LE(lhs, rhs); + valBln = !ScriptRuntime.cmp_LT(rhs, lhs, false, true); break; case Token.GT: - valBln = ScriptRuntime.cmp_LT(rhs, lhs); + valBln = ScriptRuntime.cmp_LT(rhs, lhs, false, false); break; case Token.LT: - valBln = ScriptRuntime.cmp_LT(lhs, rhs); + valBln = ScriptRuntime.cmp_LT(lhs, rhs, true, false); break; default: throw Kit.codeBug(); @@ -1328,10 +1328,9 @@ private static Object interpretLoop(Context cx, CallFrame frame, case Token.MUL : case Token.DIV : case Token.MOD : { + double lDbl = stack_double(frame, stackTop-1); double rDbl = stack_double(frame, stackTop); - --stackTop; - double lDbl = stack_double(frame, stackTop); - stack[stackTop] = DBL_MRK; + stack[--stackTop] = DBL_MRK; switch (op) { case Token.SUB: lDbl -= rDbl; diff --git a/src/org/mozilla/javascript/NativeIterator.java b/src/org/mozilla/javascript/NativeIterator.java index 0858104c3e..0e6364f35e 100644 --- a/src/org/mozilla/javascript/NativeIterator.java +++ b/src/org/mozilla/javascript/NativeIterator.java @@ -101,7 +101,7 @@ public String getClassName() { * doesn't have a constructor. */ @Override - public boolean hasInstance(Scriptable instance) { + public boolean hasInstance(Object instance) { return instance instanceof StopIteration; } } diff --git a/src/org/mozilla/javascript/NativeJavaArray.java b/src/org/mozilla/javascript/NativeJavaArray.java index 36d9f53e4b..2e07765b18 100644 --- a/src/org/mozilla/javascript/NativeJavaArray.java +++ b/src/org/mozilla/javascript/NativeJavaArray.java @@ -157,7 +157,7 @@ public Object[] getIds() { } @Override - public boolean hasInstance(Scriptable value) { + public boolean hasInstance(Object value) { if (!(value instanceof Wrapper)) return false; Object instance = ((Wrapper)value).unwrap(); diff --git a/src/org/mozilla/javascript/NativeJavaClass.java b/src/org/mozilla/javascript/NativeJavaClass.java index 723d948f86..4cacbd0275 100644 --- a/src/org/mozilla/javascript/NativeJavaClass.java +++ b/src/org/mozilla/javascript/NativeJavaClass.java @@ -315,7 +315,7 @@ public String toString() { * static methods exposed by a JavaNativeClass. */ @Override - public boolean hasInstance(Scriptable value) { + public boolean hasInstance(Object value) { if (value instanceof Wrapper && !(value instanceof NativeJavaClass)) { diff --git a/src/org/mozilla/javascript/NativeJavaObject.java b/src/org/mozilla/javascript/NativeJavaObject.java index 823464b799..52e946edf4 100644 --- a/src/org/mozilla/javascript/NativeJavaObject.java +++ b/src/org/mozilla/javascript/NativeJavaObject.java @@ -131,7 +131,7 @@ public void put(int index, Scriptable start, Object value) { throw members.reportMemberNotFound(Integer.toString(index)); } - public boolean hasInstance(Scriptable value) { + public boolean hasInstance(Object value) { // This is an instance of a Java class, so always return false return false; } diff --git a/src/org/mozilla/javascript/NativeWith.java b/src/org/mozilla/javascript/NativeWith.java index cfaa49dcfd..749f12a548 100644 --- a/src/org/mozilla/javascript/NativeWith.java +++ b/src/org/mozilla/javascript/NativeWith.java @@ -151,7 +151,7 @@ public Object getDefaultValue(Class typeHint) { return prototype.getDefaultValue(typeHint); } - public boolean hasInstance(Scriptable value) { + public boolean hasInstance(Object value) { return prototype.hasInstance(value); } diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index 898bd20e88..d34a88a7f2 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -3170,11 +3170,13 @@ private ObjectLiteral objectLiteral() int pos = ts.tokenBeg, lineno = ts.lineno; int afterComma = -1; List elems = new ArrayList(); - Set propertyNames = new HashSet(); + Map propertyNames = new HashMap(); + final int GET = 0x1, SET = 0x2, VALUE = 0x4 | GET | SET; commaLoop: for (;;) { String propertyName = null; + int propertyType = VALUE; int tt = peekToken(); String jsdoc = getAndResetJsDoc(); switch(tt) { @@ -3197,10 +3199,12 @@ private ObjectLiteral objectLiteral() consumeToken(); name = createNameNode(); name.setJsDoc(jsdoc); + boolean isGetter = "get".equals(propertyName); + propertyType = (isGetter ? GET : SET); ObjectProperty objectProp = getterSetterProperty(ppos, name, - "get".equals(propertyName)); + isGetter); elems.add(objectProp); - propertyName = objectProp.getLeft().getString(); + propertyName = name.getIdentifier(); } else { AstNode pname = stringProp != null ? stringProp : name; pname.setJsDoc(jsdoc); @@ -3226,8 +3230,9 @@ private ObjectLiteral objectLiteral() default: if (convertToName(tt)) { consumeToken(); - AstNode pname = createNameNode(); + Name pname = createNameNode(); pname.setJsDoc(jsdoc); + propertyName = pname.getIdentifier(); elems.add(plainProperty(pname, tt)); break; } @@ -3235,11 +3240,17 @@ private ObjectLiteral objectLiteral() break; } - if (this.inUseStrictDirective) { - if (propertyNames.contains(propertyName)) { + Integer oldType = propertyNames.get(propertyName); + if (oldType != null) { + int old = oldType.intValue(); + if ((old & propertyType) != 0 + && (old != VALUE || propertyType != VALUE + || this.inUseStrictDirective)) { addError("msg.dup.obj.lit.prop.strict", propertyName); } - propertyNames.add(propertyName); + propertyNames.put(propertyName, propertyType | old); + } else { + propertyNames.put(propertyName, propertyType); } // Eat any dangling jsdoc in the property. @@ -3293,6 +3304,9 @@ private ObjectProperty getterSetterProperty(int pos, AstNode propName, if (name != null && name.length() != 0) { reportError("msg.bad.prop"); } + if (fn.getParams().size() != (isGetter ? 0 : 1)) { + reportError("msg.bad.prop"); + } ObjectProperty pn = new ObjectProperty(pos); if (isGetter) { pn.setIsGetter(); diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index de76cb36ba..388bd2320d 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -2994,11 +2994,7 @@ public static boolean instanceOf(Object a, Object b, Context cx) throw typeError0("msg.instanceof.not.object"); } - // for primitive values on LHS, return false - if (! (a instanceof Scriptable)) - return false; - - return ((Scriptable)b).hasInstance((Scriptable)a); + return ((Scriptable)b).hasInstance(a); } /** @@ -3006,8 +3002,11 @@ public static boolean instanceOf(Object a, Object b, Context cx) * * @return true iff rhs appears in lhs' proto chain */ - public static boolean jsDelegatesTo(Scriptable lhs, Scriptable rhs) { - Scriptable proto = lhs.getPrototype(); + public static boolean jsDelegatesTo(Object lhs, Scriptable rhs) { + // for primitive values on LHS, return false + if (! (lhs instanceof Scriptable)) + return false; + Scriptable proto = ((Scriptable) lhs).getPrototype(); while (proto != null) { if (proto.equals(rhs)) return true; @@ -3040,6 +3039,50 @@ public static boolean in(Object a, Object b, Context cx) return hasObjectElem((Scriptable)b, a, cx); } + /** + * 11.8.5 The Abstract Relational Comparison Algorithm + * + * @param val1 left operand + * @param val2 right operand + * @param leftFirst evaluation order flag + * @param undef return value if either operand is NaN + * @return {@code val1 < val2} + */ + public static boolean cmp_LT(Object val1, Object val2, boolean leftFirst, boolean undef) + { + double d1, d2; + if (val1 instanceof Number && val2 instanceof Number) { + d1 = ((Number)val1).doubleValue(); + d2 = ((Number)val2).doubleValue(); + } else { + if (leftFirst) { + if (val1 instanceof Scriptable) + val1 = ((Scriptable) val1).getDefaultValue(NumberClass); + if (val2 instanceof Scriptable) + val2 = ((Scriptable) val2).getDefaultValue(NumberClass); + } else { + if (val2 instanceof Scriptable) + val2 = ((Scriptable) val2).getDefaultValue(NumberClass); + if (val1 instanceof Scriptable) + val1 = ((Scriptable) val1).getDefaultValue(NumberClass); + } + if (val1 instanceof CharSequence && val2 instanceof CharSequence) { + return val1.toString().compareTo(val2.toString()) < 0; + } + d1 = toNumber(val1); + d2 = toNumber(val2); + } + if (d1 != d1 || d2 != d2) { + // if either value is NaN return the default value for undefined + return undef; + } + return d1 < d2; + } + + /** + * @deprecated {@link #cmp_LT(Object, Object, boolean, boolean)} + */ + @Deprecated public static boolean cmp_LT(Object val1, Object val2) { double d1, d2; @@ -3060,6 +3103,10 @@ public static boolean cmp_LT(Object val1, Object val2) return d1 < d2; } + /** + * @deprecated {@link #cmp_LT(Object, Object, boolean, boolean)} + */ + @Deprecated public static boolean cmp_LE(Object val1, Object val2) { double d1, d2; diff --git a/src/org/mozilla/javascript/Scriptable.java b/src/org/mozilla/javascript/Scriptable.java index aa95d099a8..3f14601d06 100644 --- a/src/org/mozilla/javascript/Scriptable.java +++ b/src/org/mozilla/javascript/Scriptable.java @@ -330,13 +330,13 @@ public interface Scriptable { * return an appropriate value. See the JS 1.3 language documentation for more * detail. * - *

This operator corresponds to the proposed EMCA [[HasInstance]] operator. + *

This operator corresponds to the EMCA [[HasInstance]] operator. * * @param instance The value that appeared on the LHS of the instanceof * operator * * @return an implementation dependent value */ - public boolean hasInstance(Scriptable instance); + public boolean hasInstance(Object instance); } diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 6c8e6be77c..0a87a7b527 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -1017,21 +1017,21 @@ public static Object getDefaultValue(Scriptable object, Class typeHint) } /** - * Implements the instanceof operator. + *

Implements the instanceof operator.

* - *

This operator has been proposed to ECMA. + *

Sub-classes should override this method to provide custom 'instanceof' + * functionality. {@link ScriptRuntime#jsDelegatesTo(Scriptable, Scriptable)} + * provides a useful default implementation. The implementation in this + * class just throws a 'TypeError' exception.

* * @param instance The value that appeared on the LHS of the instanceof * operator * @return true if "this" appears in value's prototype chain - * + * @see ScriptRuntime#jsDelegatesTo(Scriptable, Scriptable) */ - public boolean hasInstance(Scriptable instance) { - // Default for JS objects (other than Function) is to do prototype - // chasing. This will be overridden in NativeFunction and non-JS - // objects. - - return ScriptRuntime.jsDelegatesTo(instance, this); + public boolean hasInstance(Object instance) { + // Default for JS objects (other than Function) is to throw a TypeError + throw ScriptRuntime.typeError0("msg.instanceof.not.object"); } /** diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java index a412452ee7..da0431c245 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java @@ -283,7 +283,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, hasInstance for XML objects works differently than other objects; see ECMA357 13.4.3.10. */ @Override - public boolean hasInstance(Scriptable instance) { + public boolean hasInstance(Object instance) { return (instance instanceof XML || instance instanceof XMLList); } } diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java index 09723abfc5..091d115e2b 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java @@ -146,7 +146,7 @@ public final Object getDefaultValue(Class hint) { } @Override - public final boolean hasInstance(Scriptable scriptable) { + public final boolean hasInstance(Object scriptable) { return super.hasInstance(scriptable); } From 523631cf641cd476f525ba94d67e146527203d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 004/111] Test262 - chapter 11: Missing update for Codegen Update generated code to use ScriptRuntime#cmp_LT(Object, Object, boolean, boolean) --- .../mozilla/javascript/optimizer/Codegen.java | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index 86b8120832..40cd113dac 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -4646,14 +4646,39 @@ private void visitIfJumpRelOp(Node node, Node child, generateExpression(rChild, node); } - if (type == Token.GE || type == Token.GT) { + if (type == Token.GE) { + // result = !cmp_LT(lhs, rhs, true, true) + cfw.add(ByteCode.ICONST_1); + cfw.add(ByteCode.ICONST_1); + // negate result of cmp_LT + int tmp = trueGOTO; + trueGOTO = falseGOTO; + falseGOTO = tmp; + } else if (type == Token.LE) { + // result = !cmp_LT(rhs, lhs, false, true) cfw.add(ByteCode.SWAP); + cfw.add(ByteCode.ICONST_0); + cfw.add(ByteCode.ICONST_1); + // negate result of cmp_LT + int tmp = trueGOTO; + trueGOTO = falseGOTO; + falseGOTO = tmp; + } else if (type == Token.GT) { + // result = cmp_LT(rhs, lhs, false, false) + cfw.add(ByteCode.SWAP); + cfw.add(ByteCode.ICONST_0); + cfw.add(ByteCode.ICONST_0); + } else if (type == Token.LT) { + // result = cmp_LT(lhs, rhs, true, false) + cfw.add(ByteCode.ICONST_1); + cfw.add(ByteCode.ICONST_0); + } else { + throw Codegen.badTree(); } - String routine = ((type == Token.LT) - || (type == Token.GT)) ? "cmp_LT" : "cmp_LE"; - addScriptRuntimeInvoke(routine, + addScriptRuntimeInvoke("cmp_LT", "(Ljava/lang/Object;" +"Ljava/lang/Object;" + +"ZZ" +")Z"); cfw.add(ByteCode.IFNE, trueGOTO); cfw.add(ByteCode.GOTO, falseGOTO); From 2f947b93ce84c00a63806adf5600751e9bb7b954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 005/111] Test262 - chapter 15 ECMAScript5 has changed "15.3.4.4 Function.prototype.call" to longer coerce the `thisObject` by means of ToObject. That requires a few API changes in Rhino, notably for Callable#call() and IdFunctionCall#execIdCall(). As both methods are fundamental aspects of any host object in Rhino, the API change triggered changes all over the place. In the rest of this commit message, file changes are not separately listed below if the changes are only updates for either call() or execIdCall(). Callable: - changed `thisObject` type from "Scriptable" to "Object" to conform to ES5 IdFunctionCall: - changed `thisObject` type from "Scriptable" to "Object" to conform to ES5 xmlbeans/XMLList: - add cast to XMLObject [short-cut instead of calling ScriptRuntime.toObject()] Arguments: - add initial default data-descriptor with {enumerable: true, writable: true, configurable: true} per 10.6 Arguments Object, step 11.b - required at this point otherwise ScriptableObject#defineOwnProperty() sets the attributes to {enumerable: false, writable: false, configurable: false} - handle case when only 'writable' is set - replace isFalse() call with additional check whether 'writable' is actually present in descriptor - also see [[DefineOwnProperty]] for Arguments step 5.b.i and 5.b.ii BaseFunction: - arity of toString() is 1 - realFunction() should default to use `thisObject` as function if valueOf() returns non-function object [valueOf() redefining the function seems to a rhino-specific implementation choice?] - left backward compatible call() method definition to help users move to new call() interface IdScriptableObject: - left backward compatible execIdCall() method definition to help users move to new execIdCall() interface InterprectedFunction: - user functions still coerce 'null' and 'undefined' to the global object Codegen: - update generated code to match InterprectedFunction NativeArray: - [[Put]] cannot add new entries if [[Extensible]] is false, cf. 8.12.4 [[CanPut]] - added 15.4.5.1 [[DefineOwnProperty]] for the 'length' property on Array - added 15.4.5.1 [[DefineOwnProperty]] for the index properties on Array - intercept 'length' and index properties and redirect to the methods listed above - 'length' must be intercepted for both defineOwnProperty() methods, see defineOwnProperty() in super-class - copied more or less from setLength(), but includes additional checks and always deletes index properties in descending order as specified in 15.4.5.1 [[DefineOwnProperty]] - helper method just like setElem(), but uses [[DefineOwnProperty]] instead of [[Put]] - follow spec for 15.4.4.11 Array.prototype.sort more closely in respect to undefined and not-defined properties - explicitely call deleteElem() because `thisObject` may not be an Array instance - always call setLengthProperty() to ensure update to 'length' property b/c it may be implicitely updated due to call to ToUint32 in step 3 of 15.4.4.13 Array.prototype.unshift - add missing calls to [[Delete]], see step 12.d of 15.4.4.12 Array.prototype.splice - 15.4.4.4 Array.prototype.concat uses [[DefineOwnProperty]] instead of [[Put]] - handle case when first argument is `undefined` - 15.4.4.10 Array.prototype.slice uses [[DefineOwnProperty]] instead of [[Put]] - removed method, indexOf and lastIndexOf now use seperate methods since the amount of shared lines was negliglible to justify the added non-uniformness compared to the other js_* methods - 15.4.4.14 Array.prototype.indexOf uses [[Get]], therefore add search for prototype values in the dense-array case - 15.4.4.15 Array.prototype.lastIndexOf uses [[Get]], therefore add search for prototype values in the dense-array case - use caution to follow algorithm steps more closely - 'filter' and 'map' use [[DefineOwnProperty]] instead of [[Put]] - use caution to follow algorithm steps more closely NativeDate: - length of 'constructor' and 'UTC' is 7 - handle case when Date.prototype.toISOString is redefined for Date objects - Date.prototype.toJSON uses [[GET]] to obtain the "toISOString" property - don't use Java's DateFormat to parse/format dates from/into ISO-8601 Extended Format strings since DateFormat doesn't follow the syntax from 15.9.1.15 Date Time String Format - add DaysInMonth method with same contract as DaysInMonth from jsdate.cpp - add parseISOString method with same contract as in jsdate.cpp, but use a simple state machine since Java doesn't support macros - parseISOString supports the following two deviations from the spec: 1) time-only format, e.g. "08:00:00Z" 2) timezone descriptor without ':' as separator, e.g. "08:00:00+0100" instead of "08:00:00+01:00" - parseISOString also supports the same input validation as in jsdata.cpp, the spec isn't really clear what to do with invalid input data - add js_toISOString method to format date values in ISO-8601 Extended Format with expanded year representation if necessary NativeError: - properly set default attributes for "name" and "message" - handle `undefined` value for message in constructor - follow spec for 15.11.4.4 Error.prototype.toString more closely NativeGlobal: - remove IE-support shim for "ConversionError" - no longer replace 0xFFFE and 0xFFFF with 0xFFFD when decoding URI's NativeNumber: - stick with the spec and don't allow non-standard precisions NativeObject: - Object.prototype.toLocaleString no longer alias for toString instead follow spec and retrieve current "toString" with [[Get]] - Object.prototype.valueOf calls ToObject on `thisObject` per spec - handle `undefined` arguments for Object.prototype.hasOwnProperty and Object.prototype.propertyIsEnumerable - Object.prototype.isPrototypeOf calls ToObject after argument validation per spec algorithm NativeString: - arity of String.prototype.replace is 2 - call CheckObjectCoercible on `thisObject` if required by spec for String.prototype methods - add more overriden methods to be compliant to 15.5.5.2 [[GetOwnProperty]] - handle `undefined` arguments in 15.5.4.13 String.prototype.slice ScriptRuntime: - add additional defaultObjectToString() method to support 15.2.4.2 Object.prototype.toString - #numberToString(): first check radix, then process other special values like Infinity etc. - add #checkObjectCoercible() per 9.10 CheckObjectCoercible -- don't map `null` or `undefined` to the global object if specified as `thisObject`, cf. 15.3.4.4 Function.prototype.call -- don't call ToObject for the `thisObject`, cf. 15.3.4.4 Function.prototype.call - use [[DefineOwnProperty]] rather than [[Put]] in #newArrayLiteral() when creating new Array objects ScriptableObject: - add helper methods for defineProperty() which work on integer indices instead of strings - default getter resp. setter to `undefined` when creating a new accessor property - 'enumerable' must be present before attempting to reject property change in #checkPropertyChange() SpecialRef: - handle the case when an object actually has an own property named "__proto__" NativeRegExp: - RegExp is no longer callable, also see https://bugzilla.mozilla.org/show_bug.cgi?id=582717 - change 'lastIndex' property from double to Object to allow lazy evaluation of the property - handle `undefined` argument when compiling a new RegExp - make RegExp.prototype.exec work with `undefined` argument - add lazy evaluation of 'lastIndex' in RegExp.prototype.exec - additional check to report an error if a RegExp flag appears more than once - don't allow unqualified '{', cf. `/a{1}{1}/` - arity of RegExp.prototype.compile is 2 NativeRegExpCtor: - arity of RegExp constructor is 2 RegExpImpl: - String.prototype.replace erroneously created a RegExp if the first argument was not already an instanceof RegExp - move NativeRegExp creation out of matchOrReplace into a new method - handle `undefined` arguments for String.prototype.{match, search, replace} - follow String.prototype.split algorithm steps more closely error.tostring.doctest: - update expected values, double-checked with SM shell --- .../xml/impl/xmlbeans/Namespace.java | 5 +- .../javascript/xml/impl/xmlbeans/QName.java | 5 +- .../javascript/xml/impl/xmlbeans/XMLCtor.java | 3 +- .../javascript/xml/impl/xmlbeans/XMLList.java | 6 +- .../xml/impl/xmlbeans/XMLObjectImpl.java | 3 +- src/org/mozilla/javascript/Arguments.java | 28 +- src/org/mozilla/javascript/BaseFunction.java | 29 +- src/org/mozilla/javascript/BoundFunction.java | 2 +- src/org/mozilla/javascript/Callable.java | 2 +- src/org/mozilla/javascript/Delegator.java | 2 +- src/org/mozilla/javascript/Function.java | 2 +- .../mozilla/javascript/FunctionObject.java | 2 +- .../mozilla/javascript/IdFunctionCall.java | 2 +- .../mozilla/javascript/IdFunctionObject.java | 2 +- .../javascript/IdScriptableObject.java | 12 +- .../mozilla/javascript/ImporterTopLevel.java | 4 +- .../javascript/InterpretedFunction.java | 6 +- src/org/mozilla/javascript/JavaAdapter.java | 2 +- src/org/mozilla/javascript/JavaMembers.java | 2 +- src/org/mozilla/javascript/NativeArray.java | 561 ++++++++++++------ src/org/mozilla/javascript/NativeBoolean.java | 2 +- src/org/mozilla/javascript/NativeCall.java | 2 +- .../javascript/NativeContinuation.java | 4 +- src/org/mozilla/javascript/NativeDate.java | 251 ++++++-- src/org/mozilla/javascript/NativeError.java | 36 +- .../mozilla/javascript/NativeGenerator.java | 4 +- src/org/mozilla/javascript/NativeGlobal.java | 5 +- .../mozilla/javascript/NativeIterator.java | 4 +- src/org/mozilla/javascript/NativeJSON.java | 2 +- .../mozilla/javascript/NativeJavaClass.java | 2 +- .../javascript/NativeJavaConstructor.java | 2 +- .../mozilla/javascript/NativeJavaMethod.java | 4 +- .../javascript/NativeJavaTopPackage.java | 4 +- src/org/mozilla/javascript/NativeMath.java | 2 +- src/org/mozilla/javascript/NativeNumber.java | 20 +- src/org/mozilla/javascript/NativeObject.java | 70 ++- src/org/mozilla/javascript/NativeScript.java | 6 +- src/org/mozilla/javascript/NativeString.java | 201 ++++++- src/org/mozilla/javascript/NativeWith.java | 2 +- src/org/mozilla/javascript/ScriptRuntime.java | 66 ++- .../mozilla/javascript/ScriptableObject.java | 49 +- src/org/mozilla/javascript/SpecialRef.java | 4 + src/org/mozilla/javascript/Synchronizer.java | 2 +- .../javascript/commonjs/module/Require.java | 4 +- .../mozilla/javascript/optimizer/Codegen.java | 42 +- .../javascript/regexp/NativeRegExp.java | 56 +- .../javascript/regexp/NativeRegExpCtor.java | 12 +- .../mozilla/javascript/regexp/RegExpImpl.java | 97 +-- testsrc/doctests/error.tostring.doctest | 16 +- .../mozilla/javascript/tests/TypeOfTest.java | 2 +- .../tests/commonjs/module/ComplianceTest.java | 2 +- .../mozilla/javascript/xmlimpl/Namespace.java | 4 +- .../org/mozilla/javascript/xmlimpl/QName.java | 4 +- .../mozilla/javascript/xmlimpl/XMLCtor.java | 2 +- .../mozilla/javascript/xmlimpl/XMLList.java | 6 +- .../javascript/xmlimpl/XMLObjectImpl.java | 2 +- 56 files changed, 1205 insertions(+), 468 deletions(-) diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/Namespace.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/Namespace.java index 2c1b8ad934..a01dca85ab 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/Namespace.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/Namespace.java @@ -269,10 +269,11 @@ protected void initPrototypeId(int id) initPrototypeMethod(NAMESPACE_TAG, id, s, arity); } + @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, + Object thisObj, Object[] args) { if (!f.hasTag(NAMESPACE_TAG)) { @@ -290,7 +291,7 @@ public Object execIdCall(IdFunctionObject f, throw new IllegalArgumentException(String.valueOf(id)); } - private Namespace realThis(Scriptable thisObj, IdFunctionObject f) + private Namespace realThis(Object thisObj, IdFunctionObject f) { if(!(thisObj instanceof Namespace)) throw incompatibleCallError(f); diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/QName.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/QName.java index d52becb323..836ab1418e 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/QName.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/QName.java @@ -255,10 +255,11 @@ protected void initPrototypeId(int id) initPrototypeMethod(QNAME_TAG, id, s, arity); } + @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, + Object thisObj, Object[] args) { if (!f.hasTag(QNAME_TAG)) { @@ -276,7 +277,7 @@ public Object execIdCall(IdFunctionObject f, throw new IllegalArgumentException(String.valueOf(id)); } - private QName realThis(Scriptable thisObj, IdFunctionObject f) + private QName realThis(Object thisObj, IdFunctionObject f) { if(!(thisObj instanceof QName)) throw incompatibleCallError(f); diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLCtor.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLCtor.java index d4a31f05f1..6c74f96cbe 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLCtor.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLCtor.java @@ -233,8 +233,9 @@ protected void initPrototypeId(int id) initPrototypeMethod(XMLCTOR_TAG, id, s, arity); } + @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(XMLCTOR_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java index c786854bbc..24152c3cc3 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java @@ -1528,7 +1528,7 @@ private XMLList getPropertyList(XMLName name) private Object applyOrCall(boolean isApply, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { String methodName = isApply ? "apply" : "call"; if(!(thisObj instanceof XMLList) || @@ -1574,7 +1574,7 @@ public Scriptable getExtraMethodSource(Context cx) return null; } - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { // This XMLList is being called as a Function. @@ -1592,7 +1592,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, throw ScriptRuntime.typeError1("msg.incompat.call", methodName); } Object func = null; - Scriptable sobj = thisObj; + Scriptable sobj = (XMLObject) thisObj; while (sobj instanceof XMLObject) { XMLObject xmlObject = (XMLObject) sobj; diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java index d549e9063c..fd65f97f25 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java @@ -579,8 +579,9 @@ protected void initPrototypeId(int id) * @param args * @return */ + @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(XMLOBJECT_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); diff --git a/src/org/mozilla/javascript/Arguments.java b/src/org/mozilla/javascript/Arguments.java index eb87eb07ed..9123cb6078 100644 --- a/src/org/mozilla/javascript/Arguments.java +++ b/src/org/mozilla/javascript/Arguments.java @@ -365,10 +365,25 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { protected void defineOwnProperty(Context cx, Object id, ScriptableObject desc, boolean checkValid) { - super.defineOwnProperty(cx, id, desc, checkValid); - double d = ScriptRuntime.toNumber(id); int index = (int) d; + if (d == index) { + Object value = arg(index); + if (value != NOT_FOUND && !super.has(index, this)) { + // set-up default descriptor + if (sharedWithActivation(index)) { + value = getFromActivation(index); + } + Scriptable scope = getParentScope(); + if (scope == null) scope = this; + // descriptor with {configurable: true, enumerable: true, writable: true} + ScriptableObject def = buildDataDescriptor(scope, value, EMPTY); + super.defineOwnProperty(cx, id, def, checkValid); + } + } + + super.defineOwnProperty(cx, id, desc, checkValid); + if (d != index) return; Object value = arg(index); @@ -380,11 +395,12 @@ protected void defineOwnProperty(Context cx, Object id, } Object newValue = getProperty(desc, "value"); - if (newValue == NOT_FOUND) return; - - replaceArg(index, newValue); + if (newValue != NOT_FOUND) { + replaceArg(index, newValue); + } - if (isFalse(getProperty(desc, "writable"))) { + Object writable = getProperty(desc, "writable"); + if (!(writable == NOT_FOUND || ScriptRuntime.toBoolean(writable))) { removeArg(index); } } diff --git a/src/org/mozilla/javascript/BaseFunction.java b/src/org/mozilla/javascript/BaseFunction.java index 122dd4db95..2c33208d55 100644 --- a/src/org/mozilla/javascript/BaseFunction.java +++ b/src/org/mozilla/javascript/BaseFunction.java @@ -241,7 +241,7 @@ protected void initPrototypeId(int id) int arity; switch (id) { case Id_constructor: arity=1; s="constructor"; break; - case Id_toString: arity=1; s="toString"; break; + case Id_toString: arity=0; s="toString"; break; case Id_toSource: arity=1; s="toSource"; break; case Id_apply: arity=2; s="apply"; break; case Id_call: arity=1; s="call"; break; @@ -268,7 +268,7 @@ static boolean isApplyOrCall(IdFunctionObject f) { @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(FUNCTION_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); @@ -279,13 +279,13 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, return jsConstructor(cx, scope, args); case Id_toString: { - BaseFunction realf = realFunction(thisObj, f); + BaseFunction realf = realFunction(cx, scope, thisObj, f); int indent = ScriptRuntime.toInt32(args, 0); return realf.decompile(indent, 0); } case Id_toSource: { - BaseFunction realf = realFunction(thisObj, f); + BaseFunction realf = realFunction(cx, scope, thisObj, f); int indent = 0; int flags = Decompiler.TO_SOURCE_FLAG; if (args.length != 0) { @@ -325,11 +325,15 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, throw new IllegalArgumentException(String.valueOf(id)); } - private BaseFunction realFunction(Scriptable thisObj, IdFunctionObject f) + private BaseFunction realFunction(Context cx, Scriptable scope, + Object thisObject, IdFunctionObject f) { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); Object x = thisObj.getDefaultValue(ScriptRuntime.FunctionClass); if (x instanceof BaseFunction) { return (BaseFunction)x; + } else if (thisObj instanceof BaseFunction) { + return (BaseFunction)thisObj; } throw ScriptRuntime.typeError1("msg.incompat.call", f.getFunctionName()); @@ -360,6 +364,17 @@ protected Scriptable getClassPrototype() /** * Should be overridden. */ + public Object call(Context cx, Scriptable scope, Object thisObject, + Object[] args) + { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); + return call(cx, scope, thisObj, args); + } + + /** + * @deprecated {@link #call(Context, Scriptable, Object, Object[])} + */ + @Deprecated public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { @@ -370,12 +385,12 @@ public Scriptable construct(Context cx, Scriptable scope, Object[] args) { Scriptable result = createObject(cx, scope); if (result != null) { - Object val = call(cx, scope, result, args); + Object val = call(cx, scope, (Object) result, args); if (val instanceof Scriptable) { result = (Scriptable)val; } } else { - Object val = call(cx, scope, null, args); + Object val = call(cx, scope, (Object) null, args); if (!(val instanceof Scriptable)) { // It is program error not to return Scriptable from // the call method if createObject returns null. diff --git a/src/org/mozilla/javascript/BoundFunction.java b/src/org/mozilla/javascript/BoundFunction.java index 592d7738b1..e7476d7b5e 100644 --- a/src/org/mozilla/javascript/BoundFunction.java +++ b/src/org/mozilla/javascript/BoundFunction.java @@ -80,7 +80,7 @@ public BoundFunction(Context cx, Scriptable scope, Callable targetFunction, Scri } @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] extraArgs) + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] extraArgs) { Scriptable callThis = boundThis != null ? boundThis : ScriptRuntime.getTopCallScope(cx); return targetFunction.call(cx, scope, callThis, concat(boundArgs, extraArgs)); diff --git a/src/org/mozilla/javascript/Callable.java b/src/org/mozilla/javascript/Callable.java index 03e0fce92f..ea9d7727ff 100644 --- a/src/org/mozilla/javascript/Callable.java +++ b/src/org/mozilla/javascript/Callable.java @@ -53,7 +53,7 @@ public interface Callable * @param args the array of arguments * @return the result of the call */ - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args); } diff --git a/src/org/mozilla/javascript/Delegator.java b/src/org/mozilla/javascript/Delegator.java index 462c0673ab..89841e6396 100644 --- a/src/org/mozilla/javascript/Delegator.java +++ b/src/org/mozilla/javascript/Delegator.java @@ -223,7 +223,7 @@ public boolean hasInstance(Object instance) { /** * @see org.mozilla.javascript.Function#call */ - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { return ((Function)obj).call(cx,scope,thisObj,args); diff --git a/src/org/mozilla/javascript/Function.java b/src/org/mozilla/javascript/Function.java index a4377e6662..ec489f4662 100644 --- a/src/org/mozilla/javascript/Function.java +++ b/src/org/mozilla/javascript/Function.java @@ -64,7 +64,7 @@ public interface Function extends Scriptable, Callable * @param args the array of arguments * @return the result of the call */ - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args); /** diff --git a/src/org/mozilla/javascript/FunctionObject.java b/src/org/mozilla/javascript/FunctionObject.java index 42b01c65d3..8c18a21b7d 100644 --- a/src/org/mozilla/javascript/FunctionObject.java +++ b/src/org/mozilla/javascript/FunctionObject.java @@ -401,7 +401,7 @@ public static Object convertArg(Context cx, Scriptable scope, * Context, Scriptable, Scriptable, Object[]) */ @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { Object result; diff --git a/src/org/mozilla/javascript/IdFunctionCall.java b/src/org/mozilla/javascript/IdFunctionCall.java index 713fabf765..6b56dbd9e2 100644 --- a/src/org/mozilla/javascript/IdFunctionCall.java +++ b/src/org/mozilla/javascript/IdFunctionCall.java @@ -49,7 +49,7 @@ public interface IdFunctionCall * instance of Scriptable should be returned */ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args); + Object thisObj, Object[] args); } diff --git a/src/org/mozilla/javascript/IdFunctionObject.java b/src/org/mozilla/javascript/IdFunctionObject.java index 2ec30edc7c..0930b7d615 100644 --- a/src/org/mozilla/javascript/IdFunctionObject.java +++ b/src/org/mozilla/javascript/IdFunctionObject.java @@ -123,7 +123,7 @@ public Scriptable getPrototype() } @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { return idcall.execIdCall(this, cx, scope, thisObj, args); diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index bee4e56b0b..67dd71a308 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -595,11 +595,21 @@ protected void setInstanceIdAttributes(int id, int attr) { /** 'thisObj' will be null if invoked as constructor, in which case ** instance of Scriptable should be returned. */ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { throw f.unknown(); } + /** + * @deprecated {@link #execIdCall(IdFunctionObject, Context, Scriptable, Object, Object[])} + */ + @Deprecated + public final Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + return execIdCall(f, cx, scope, (Object) thisObj, args); + } + public final IdFunctionObject exportAsJSClass(int maxPrototypeId, Scriptable scope, boolean sealed) diff --git a/src/org/mozilla/javascript/ImporterTopLevel.java b/src/org/mozilla/javascript/ImporterTopLevel.java index 216750e438..3a4a96560b 100644 --- a/src/org/mozilla/javascript/ImporterTopLevel.java +++ b/src/org/mozilla/javascript/ImporterTopLevel.java @@ -257,7 +257,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(IMPORTER_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); @@ -276,7 +276,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, throw new IllegalArgumentException(String.valueOf(id)); } - private ImporterTopLevel realThis(Scriptable thisObj, IdFunctionObject f) + private ImporterTopLevel realThis(Object thisObj, IdFunctionObject f) { if (topScopeFlag) { // when used as top scope importPackage and importClass are global diff --git a/src/org/mozilla/javascript/InterpretedFunction.java b/src/org/mozilla/javascript/InterpretedFunction.java index e1e2d6dad3..2a12c36535 100644 --- a/src/org/mozilla/javascript/InterpretedFunction.java +++ b/src/org/mozilla/javascript/InterpretedFunction.java @@ -155,9 +155,13 @@ public String getFunctionName() * @return the result of the function call. */ @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObject, Object[] args) { + Scriptable thisObj = ScriptRuntime.toObjectOrNull(cx, thisObject, scope); + if (thisObj == null) { + thisObj = ScriptRuntime.getTopCallScope(cx); + } if (!ScriptRuntime.hasTopCall(cx)) { return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args); } diff --git a/src/org/mozilla/javascript/JavaAdapter.java b/src/org/mozilla/javascript/JavaAdapter.java index 134cd84a9e..de381678c1 100644 --- a/src/org/mozilla/javascript/JavaAdapter.java +++ b/src/org/mozilla/javascript/JavaAdapter.java @@ -118,7 +118,7 @@ public static void init(Context cx, Scriptable scope, boolean sealed) } public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (f.hasTag(FTAG)) { if (f.methodId() == Id_JavaAdapter) { diff --git a/src/org/mozilla/javascript/JavaMembers.java b/src/org/mozilla/javascript/JavaMembers.java index a2cd9e9581..21ea5290be 100644 --- a/src/org/mozilla/javascript/JavaMembers.java +++ b/src/org/mozilla/javascript/JavaMembers.java @@ -167,7 +167,7 @@ void put(Scriptable scope, String name, Object javaObject, Object[] args = { value }; bp.setters.call(Context.getContext(), ScriptableObject.getTopLevelScope(scope), - scope, args); + (Object) scope, args); } } else { diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index 92ab4dc911..41d93882d4 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -247,7 +247,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(ARRAY_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); @@ -309,25 +309,25 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, return toStringHelper(cx, scope, thisObj, true, false); case Id_join: - return js_join(cx, thisObj, args); + return js_join(cx, scope, thisObj, args); case Id_reverse: - return js_reverse(cx, thisObj, args); + return js_reverse(cx, scope, thisObj, args); case Id_sort: return js_sort(cx, scope, thisObj, args); case Id_push: - return js_push(cx, thisObj, args); + return js_push(cx, scope, thisObj, args); case Id_pop: - return js_pop(cx, thisObj, args); + return js_pop(cx, scope, thisObj, args); case Id_shift: - return js_shift(cx, thisObj, args); + return js_shift(cx, scope, thisObj, args); case Id_unshift: - return js_unshift(cx, thisObj, args); + return js_unshift(cx, scope, thisObj, args); case Id_splice: return js_splice(cx, scope, thisObj, args); @@ -336,13 +336,13 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, return js_concat(cx, scope, thisObj, args); case Id_slice: - return js_slice(cx, thisObj, args); + return js_slice(cx, scope, thisObj, args); case Id_indexOf: - return indexOfHelper(cx, thisObj, args, false); + return js_indexOf(cx, scope, thisObj, args); case Id_lastIndexOf: - return indexOfHelper(cx, thisObj, args, true); + return js_lastIndexOf(cx, scope, thisObj, args); case Id_every: case Id_filter: @@ -452,7 +452,9 @@ public void put(int index, Scriptable start, Object value) if (start == this && !isSealed() && dense != null && 0 <= index && (denseOnly || !isGetterOrSetter(null, index, true))) { - if (index < dense.length) { + if (!isExtensible() && this.length <= index) { + return; + } else if (index < dense.length) { dense[index] = value; if (this.length <= index) this.length = (long)index + 1; @@ -586,25 +588,135 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { return super.getOwnPropertyDescriptor(cx, id); } + /** + * 15.4.5.1 [[DefineOwnProperty]] for Array 'length' + */ + private void defineOwnPropertyLength(ScriptableObject desc, + boolean checkValid) { + checkPropertyDefinition(desc); + Object value = getProperty(desc, "value"); + Object configurable = getProperty(desc, "configurable"); + Object enumerable = getProperty(desc, "enumerable"); + Object writable = getProperty(desc, "writable"); + + // 8.12.9 [[DefineOwnProperty]] checks for non-configurable properties, cf. + // 15.4.5.2 'length' has attributes {writable: true, enumerable: false, configurable: false} + if (isTrue(configurable)) { + if (! checkValid) return; + throw ScriptRuntime.typeError1( + "msg.change.configurable.false.to.true", "length"); + } else if (isTrue(enumerable)) { + if (! checkValid) return; + throw ScriptRuntime.typeError1( + "msg.change.enumerable.with.configurable.false", "length"); + } else if (isAccessorDescriptor(desc)) { + if (! checkValid) return; + throw ScriptRuntime.typeError1( + "msg.change.property.data.to.accessor.with.configurable.false", "length"); + } + + long newLen, oldLen = length; + if (value == NOT_FOUND) { + // generic-descriptor or data-descriptor with 'writable' + newLen = oldLen; + } else { + // data-descriptor with 'value' and possibly 'writable' + newLen = ScriptRuntime.toUint32(value); + if (newLen != ScriptRuntime.toNumber(value)) { + String msg = ScriptRuntime.getMessage0("msg.arraylength.bad"); + throw ScriptRuntime.constructError("RangeError", msg); + } + } + + if (newLen == oldLen) { + if (writable != NOT_FOUND) { + boolean w = ScriptRuntime.toBoolean(writable); + if ((lengthAttr & READONLY) != 0 && w) { + if (! checkValid) return; + throw ScriptRuntime.typeError1( + "msg.change.writable.false.to.true.with.configurable.false", "length"); + } + lengthAttr = w + ? lengthAttr & ~READONLY + : lengthAttr | READONLY; + } + } else { + if ((lengthAttr & READONLY) != 0) { + if (! checkValid) return; + throw ScriptRuntime.typeError1( + "msg.change.value.with.writable.false", "length"); + } + long entry = updateEntries(oldLen, newLen); + if (entry != -1) { + newLen = entry + 1; + } + length = newLen; + if (!(writable == NOT_FOUND || ScriptRuntime.toBoolean(writable))) { + lengthAttr = lengthAttr | READONLY; + } + if (entry != -1 && checkValid) { + throw ScriptRuntime.typeError("'missing error description'"); + } + } + } + + /** + * 15.4.5.1 [[DefineOwnProperty]] for Array 'index' + */ + private void defineOwnPropertyIndex(Context cx, Object id, + ScriptableObject desc, boolean checkValid, long index) { + // 15.4.5.1 - step 4 => property is array index + long oldLen = length; + if (index >= oldLen) { + if ((lengthAttr & READONLY) != 0) { + if (!checkValid) return; + throw ScriptRuntime.typeError1( + "msg.change.value.with.writable.false", "length"); + } + } + if (dense != null) { + Object[] values = dense; + dense = null; + denseOnly = false; + for (int i = 0; i < values.length; i++) { + if (values[i] != NOT_FOUND) { + put(i, this, values[i]); + } + } + } + if (index >= oldLen) { + length = index + 1; + } + checkPropertyDefinition(desc); + super.defineOwnProperty(cx, id, desc, checkValid); + } + + @Override + public void defineOwnProperty(Context cx, Object key, ScriptableObject desc) { + if ("length".equals(ScriptRuntime.toString(key))) { + // intercept 'length' property + defineOwnPropertyLength(desc, true); + return; + } + super.defineOwnProperty(cx, key, desc); + } + @Override protected void defineOwnProperty(Context cx, Object id, ScriptableObject desc, boolean checkValid) { - if (dense != null) { - Object[] values = dense; - dense = null; - denseOnly = false; - for (int i = 0; i < values.length; i++) { - if (values[i] != NOT_FOUND) { - put(i, this, values[i]); - } + if ("length".equals(ScriptRuntime.toString(id))) { + // intercept 'length' property + defineOwnPropertyLength(desc, checkValid); + return; } - } - long index = toArrayIndex(id); - if (index >= length) { - length = index + 1; - } - super.defineOwnProperty(cx, id, desc, checkValid); + long index = toArrayIndex(id); + if (index >= 0) { + // intercept index properties + defineOwnPropertyIndex(cx, id, desc, checkValid, index); + return; + } + super.defineOwnProperty(cx, id, desc, checkValid); } /** @@ -660,6 +772,86 @@ void setDenseOnly(boolean denseOnly) { this.denseOnly = denseOnly; } + private long updateEntries(long oldLength, long newLength) { + if (denseOnly) { + if (newLength < oldLength) { + // downcast okay because denseOnly + Arrays.fill(dense, (int) newLength, dense.length, NOT_FOUND); + return -1; + } else if (newLength < MAX_PRE_GROW_SIZE && + newLength < (oldLength * GROW_FACTOR) && + ensureCapacity((int)newLength)) + { + return -1; + } else { + denseOnly = false; + } + } + if (newLength < oldLength) { + // remove all properties between longVal and length + if (oldLength - newLength > 0x1000) { + // assume that the representation is sparse + Object[] e = getIds(); // will only find in object itself + int c = 0; + long[] ids = new long[e.length]; + for (int i = 0, len = e.length; i < len; i++) { + Object id = e[i]; + if (id instanceof String) { + // > MAXINT will appear as string + String strId = (String)id; + long index = toArrayIndex(strId); + if (index >= newLength) { + ids[c++] = index; + } + } else { + int index = ((Integer)id).intValue(); + if (index >= newLength) { + ids[c++] = index; + } + } + } + // sorted array required since need to delete elements starting + // from last to first + Arrays.sort(ids, 0, c); + for (int i = c - 1; i >= 0; --i) { + long id = ids[i]; + int index = (int) id; + if (id == index) { + delete(index); + if (has(index, this)) { + return index; + } + } else { + String strId = Long.toString(index); + delete(strId); + if (has(strId, this)) { + return index; + } + } + } + } else { + // assume a dense representation + // for (long i = newLength; i < oldLength; i++) + for (long i = oldLength - 1; i >= newLength; --i) { + int index = (int) i; + if (i == index) { + delete(index); + if (has(index, this)) { + return i; + } + } else { + String strId = Long.toString(i); + delete(strId); + if (has(strId, this)) { + return i; + } + } + } + } + } + return -1; + } + private void setLength(Object val) { /* XXX do we satisfy this? * 15.4.5.1 [[Put]](P, V): @@ -777,6 +969,17 @@ private static Object getRawElem(Scriptable target, long index) { } } + private static void defineElem(Context cx, Scriptable target, long index, + Object value) + { + if (index > Integer.MAX_VALUE) { + String id = Long.toString(index); + ScriptableObject.defineProperty(target, id, value, ScriptableObject.EMPTY); + } else { + ScriptableObject.defineProperty(target, (int)index, value, ScriptableObject.EMPTY); + } + } + private static void setElem(Context cx, Scriptable target, long index, Object value) { @@ -799,13 +1002,13 @@ private static void setRawElem(Context cx, Scriptable target, long index, } private static String toStringHelper(Context cx, Scriptable scope, - Scriptable thisObj, + Object thisObject, boolean toSource, boolean toLocale) { /* It's probably redundant to handle long lengths in this * function; StringBuilders are limited to 2^31 in java. */ - + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); long length = getLengthProperty(cx, thisObj); StringBuilder result = new StringBuilder(256); @@ -897,9 +1100,10 @@ private static String toStringHelper(Context cx, Scriptable scope, /** * See ECMA 15.4.4.3 */ - private static String js_join(Context cx, Scriptable thisObj, - Object[] args) + private static String js_join(Context cx, Scriptable scope, + Object thisObject, Object[] args) { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); long llength = getLengthProperty(cx, thisObj); int length = (int)llength; if (llength != length) { @@ -961,9 +1165,10 @@ private static String js_join(Context cx, Scriptable thisObj, /** * See ECMA 15.4.4.4 */ - private static Scriptable js_reverse(Context cx, Scriptable thisObj, - Object[] args) + private static Scriptable js_reverse(Context cx, Scriptable scope, + Object thisObject, Object[] args) { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); if (thisObj instanceof NativeArray) { NativeArray na = (NativeArray) thisObj; if (na.denseOnly) { @@ -992,8 +1197,9 @@ private static Scriptable js_reverse(Context cx, Scriptable thisObj, * See ECMA 15.4.4.5 */ private static Scriptable js_sort(final Context cx, final Scriptable scope, - final Scriptable thisObj, final Object[] args) + final Object thisObject, final Object[] args) { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); final Comparator comparator; if (args.length > 0 && Undefined.instance != args[0]) { final Callable jsCompareFunction = ScriptRuntime @@ -1003,14 +1209,14 @@ private static Scriptable js_sort(final Context cx, final Scriptable scope, comparator = new Comparator() { public int compare(final Object x, final Object y) { // sort undefined to end - if (x == y) { - return 0; - } else if (y == Undefined.instance - || y == Scriptable.NOT_FOUND) { + if (x == NOT_FOUND) { + return y == NOT_FOUND ? 0 : 1; + } else if (y == NOT_FOUND) { + return -1; + } else if (x == Undefined.instance) { + return y == Undefined.instance ? 0 : 1; + } else if (y == Undefined.instance) { return -1; - } else if (x == Undefined.instance - || x == Scriptable.NOT_FOUND) { - return 1; } cmpBuf[0] = x; @@ -1030,14 +1236,14 @@ public int compare(final Object x, final Object y) { comparator = new Comparator() { public int compare(final Object x, final Object y) { // sort undefined to end - if (x == y) - return 0; - else if (y == Undefined.instance - || y == Scriptable.NOT_FOUND) { + if (x == NOT_FOUND) { + return y == NOT_FOUND ? 0 : 1; + } else if (y == NOT_FOUND) { + return -1; + } else if (x == Undefined.instance) { + return y == Undefined.instance ? 0 : 1; + } else if (y == Undefined.instance) { return -1; - } else if (x == Undefined.instance - || x == Scriptable.NOT_FOUND) { - return 1; } final String a = ScriptRuntime.toString(x); @@ -1069,9 +1275,10 @@ else if (y == Undefined.instance * Non-ECMA methods. */ - private static Object js_push(Context cx, Scriptable thisObj, - Object[] args) + private static Object js_push(Context cx, Scriptable scope, + Object thisObject, Object[] args) { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); if (thisObj instanceof NativeArray) { NativeArray na = (NativeArray) thisObj; if (na.denseOnly && @@ -1105,9 +1312,10 @@ private static Object js_push(Context cx, Scriptable thisObj, return lengthObj; } - private static Object js_pop(Context cx, Scriptable thisObj, - Object[] args) + private static Object js_pop(Context cx, Scriptable scope, + Object thisObject, Object[] args) { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); Object result; if (thisObj instanceof NativeArray) { NativeArray na = (NativeArray) thisObj; @@ -1125,8 +1333,9 @@ private static Object js_pop(Context cx, Scriptable thisObj, // Get the to-be-deleted property's value. result = getElem(cx, thisObj, length); - // We don't need to delete the last property, because - // setLength does that for us. + // We need to delete the last property, because + // 'thisObj' may not have setLength which does that for us. + deleteElem(thisObj, length); } else { result = Undefined.instance; } @@ -1137,9 +1346,10 @@ private static Object js_pop(Context cx, Scriptable thisObj, return result; } - private static Object js_shift(Context cx, Scriptable thisObj, - Object[] args) + private static Object js_shift(Context cx, Scriptable scope, + Object thisObject, Object[] args) { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); if (thisObj instanceof NativeArray) { NativeArray na = (NativeArray) thisObj; if (na.denseOnly && na.length > 0) { @@ -1169,8 +1379,9 @@ private static Object js_shift(Context cx, Scriptable thisObj, setRawElem(cx, thisObj, i - 1, temp); } } - // We don't need to delete the last property, because - // setLength does that for us. + // We need to delete the last property, because + // 'thisObj' may not have setLength which does that for us. + deleteElem(thisObj, length); } else { result = Undefined.instance; } @@ -1178,9 +1389,10 @@ private static Object js_shift(Context cx, Scriptable thisObj, return result; } - private static Object js_unshift(Context cx, Scriptable thisObj, - Object[] args) + private static Object js_unshift(Context cx, Scriptable scope, + Object thisObject, Object[] args) { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); if (thisObj instanceof NativeArray) { NativeArray na = (NativeArray) thisObj; if (na.denseOnly && @@ -1211,17 +1423,16 @@ private static Object js_unshift(Context cx, Scriptable thisObj, for (int i = 0; i < args.length; i++) { setElem(cx, thisObj, i, args[i]); } - - /* Follow Perl by returning the new array length. */ - length += args.length; - return setLengthProperty(cx, thisObj, length); } - return ScriptRuntime.wrapNumber(length); + /* Follow Perl by returning the new array length. */ + length += args.length; + return setLengthProperty(cx, thisObj, length); } private static Object js_splice(Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObject, Object[] args) { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); NativeArray na = null; boolean denseMode = false; if (thisObj instanceof NativeArray) { @@ -1332,6 +1543,9 @@ private static Object js_splice(Context cx, Scriptable scope, Object temp = getRawElem(thisObj, last); setRawElem(cx, thisObj, last + delta, temp); } + for (long k = length + delta; k < length; ++k) { + deleteElem(thisObj, k); + } } /* Copy from argv into the hole to complete the splice. */ @@ -1349,9 +1563,10 @@ private static Object js_splice(Context cx, Scriptable scope, * See Ecma 262v3 15.4.4.4 */ private static Scriptable js_concat(Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObject, Object[] args) { // create an empty Array to return. + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); scope = getTopLevelScope(scope); Function ctor = ScriptRuntime.getExistingCtor(cx, scope, "Array"); Scriptable result = ctor.construct(cx, scope, ScriptRuntime.emptyArgs); @@ -1407,11 +1622,11 @@ private static Scriptable js_concat(Context cx, Scriptable scope, for (slot = 0; slot < length; slot++) { Object temp = getRawElem(thisObj, slot); if (temp != NOT_FOUND) { - setElem(cx, result, slot, temp); + defineElem(cx, result, slot, temp); } } } else { - setElem(cx, result, slot++, thisObj); + defineElem(cx, result, slot++, thisObj); } /* Copy from the arguments into the result. If any argument @@ -1426,22 +1641,23 @@ private static Scriptable js_concat(Context cx, Scriptable scope, for (long j = 0; j < length; j++, slot++) { Object temp = getRawElem(arg, j); if (temp != NOT_FOUND) { - setElem(cx, result, slot, temp); + defineElem(cx, result, slot, temp); } } } else { - setElem(cx, result, slot++, args[i]); + defineElem(cx, result, slot++, args[i]); } } setLengthProperty(cx, result, slot); return result; } - private Scriptable js_slice(Context cx, Scriptable thisObj, - Object[] args) + private Scriptable js_slice(Context cx, Scriptable scope, + Object thisObject, Object[] args) { - Scriptable scope = getTopLevelScope(this); - Scriptable result = cx.newArray(scope, 0); + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); + Scriptable topScope = getTopLevelScope(this); + Scriptable result = cx.newArray(topScope, 0); long length = getLengthProperty(cx, thisObj); long begin, end; @@ -1450,7 +1666,7 @@ private Scriptable js_slice(Context cx, Scriptable thisObj, end = length; } else { begin = toSliceIndex(ScriptRuntime.toInteger(args[0]), length); - if (args.length == 1) { + if (args.length == 1 || args[1] == Undefined.instance) { end = length; } else { end = toSliceIndex(ScriptRuntime.toInteger(args[1]), length); @@ -1460,7 +1676,7 @@ private Scriptable js_slice(Context cx, Scriptable thisObj, for (long slot = begin; slot < end; slot++) { Object temp = getRawElem(thisObj, slot); if (temp != NOT_FOUND) { - setElem(cx, result, slot - begin, temp); + defineElem(cx, result, slot - begin, temp); } } setLengthProperty(cx, result, Math.max(0, end - begin)); @@ -1484,101 +1700,112 @@ private static long toSliceIndex(double value, long length) { return result; } - /** - * Implements the methods "indexOf" and "lastIndexOf". - */ - private Object indexOfHelper(Context cx, Scriptable thisObj, - Object[] args, boolean isLast) - { + private static Object js_indexOf(Context cx, Scriptable scope, + Object thisObject, Object[] args) { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); Object compareTo = args.length > 0 ? args[0] : Undefined.instance; long length = getLengthProperty(cx, thisObj); long start; - if (isLast) { - // lastIndexOf - /* - * From http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:lastIndexOf - * The index at which to start searching backwards. Defaults to the - * array's length, i.e. the whole array will be searched. If the - * index is greater than or equal to the length of the array, the - * whole array will be searched. If negative, it is taken as the - * offset from the end of the array. Note that even when the index - * is negative, the array is still searched from back to front. If - * the calculated index is less than 0, -1 is returned, i.e. the - * array will not be searched. - */ - if (args.length < 2) { - // default - start = length-1; - } else { - start = (long)ScriptRuntime.toInteger(args[1]); - if (start >= length) - start = length-1; - else if (start < 0) - start += length; - if (start < 0) return NEGATIVE_ONE; - } + /* + * From http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:indexOf + * The index at which to begin the search. Defaults to 0, i.e. the + * whole array will be searched. If the index is greater than or + * equal to the length of the array, -1 is returned, i.e. the array + * will not be searched. If negative, it is taken as the offset from + * the end of the array. Note that even when the index is negative, + * the array is still searched from front to back. If the calculated + * index is less than 0, the whole array will be searched. + */ + if (args.length < 2) { + // default + start = 0; } else { - // indexOf - /* - * From http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:indexOf - * The index at which to begin the search. Defaults to 0, i.e. the - * whole array will be searched. If the index is greater than or - * equal to the length of the array, -1 is returned, i.e. the array - * will not be searched. If negative, it is taken as the offset from - * the end of the array. Note that even when the index is negative, - * the array is still searched from front to back. If the calculated - * index is less than 0, the whole array will be searched. - */ - if (args.length < 2) { - // default - start = 0; - } else { - start = (long)ScriptRuntime.toInteger(args[1]); - if (start < 0) { - start += length; - if (start < 0) - start = 0; - } - if (start > length - 1) return NEGATIVE_ONE; + start = (long)ScriptRuntime.toInteger(args[1]); + if (start < 0) { + start += length; + if (start < 0) + start = 0; } + if (start > length - 1) return NEGATIVE_ONE; } if (thisObj instanceof NativeArray) { NativeArray na = (NativeArray) thisObj; if (na.denseOnly) { - if (isLast) { - for (int i=(int)start; i >= 0; i--) { - if (na.dense[i] != Scriptable.NOT_FOUND && - ScriptRuntime.shallowEq(na.dense[i], compareTo)) - { - return Long.valueOf(i); - } - } - } else { - for (int i=(int)start; i < length; i++) { - if (na.dense[i] != Scriptable.NOT_FOUND && - ScriptRuntime.shallowEq(na.dense[i], compareTo)) - { - return Long.valueOf(i); - } - } + Scriptable proto = na.getPrototype(); + for (int i=(int)start; i < length; i++) { + Object val = na.dense[i]; + if (val == Scriptable.NOT_FOUND && proto != null) { + val = proto.get(i, proto); + } + if (val != Scriptable.NOT_FOUND && + ScriptRuntime.shallowEq(val, compareTo)) + { + return Long.valueOf(i); + } } return NEGATIVE_ONE; } } - if (isLast) { - for (long i=start; i >= 0; i--) { - Object val = getRawElem(thisObj, i); - if (val != NOT_FOUND && ScriptRuntime.shallowEq(val, compareTo)) { - return Long.valueOf(i); - } - } + for (long i=start; i < length; i++) { + Object val = getRawElem(thisObj, i); + if (val != NOT_FOUND && ScriptRuntime.shallowEq(val, compareTo)) { + return Long.valueOf(i); + } + } + return NEGATIVE_ONE; + } + + private static Object js_lastIndexOf(Context cx, Scriptable scope, + Object thisObject, Object[] args) { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); + Object compareTo = args.length > 0 ? args[0] : Undefined.instance; + long length = getLengthProperty(cx, thisObj); + long start; + /* + * From http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:lastIndexOf + * The index at which to start searching backwards. Defaults to the + * array's length, i.e. the whole array will be searched. If the + * index is greater than or equal to the length of the array, the + * whole array will be searched. If negative, it is taken as the + * offset from the end of the array. Note that even when the index + * is negative, the array is still searched from back to front. If + * the calculated index is less than 0, -1 is returned, i.e. the + * array will not be searched. + */ + if (args.length < 2) { + // default + start = length-1; } else { - for (long i=start; i < length; i++) { - Object val = getRawElem(thisObj, i); - if (val != NOT_FOUND && ScriptRuntime.shallowEq(val, compareTo)) { - return Long.valueOf(i); - } - } + start = (long)ScriptRuntime.toInteger(args[1]); + if (start >= length) + start = length-1; + else if (start < 0) + start += length; + if (start < 0) return NEGATIVE_ONE; + } + if (thisObj instanceof NativeArray) { + NativeArray na = (NativeArray) thisObj; + if (na.denseOnly) { + Scriptable proto = na.getPrototype(); + for (int i=(int)start; i >= 0; i--) { + Object val = na.dense[i]; + if (val == Scriptable.NOT_FOUND && proto != null) { + val = proto.get(i, proto); + } + if (val != Scriptable.NOT_FOUND && + ScriptRuntime.shallowEq(val, compareTo)) + { + return Long.valueOf(i); + } + } + return NEGATIVE_ONE; + } + } + for (long i=start; i >= 0; i--) { + Object val = getRawElem(thisObj, i); + if (val != NOT_FOUND && ScriptRuntime.shallowEq(val, compareTo)) { + return Long.valueOf(i); + } } return NEGATIVE_ONE; } @@ -1586,25 +1813,26 @@ else if (start < 0) /** * Implements the methods "every", "filter", "forEach", "map", and "some". */ - private Object iterativeMethod(Context cx, int id, Scriptable scope, - Scriptable thisObj, Object[] args) + private static Object iterativeMethod(Context cx, int id, Scriptable scope, + Object thisObject, Object[] args) { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); + long length = getLengthProperty(cx, thisObj); Object callbackArg = args.length > 0 ? args[0] : Undefined.instance; if (callbackArg == null || !(callbackArg instanceof Function)) { throw ScriptRuntime.notFunctionError(callbackArg); } Function f = (Function) callbackArg; Scriptable parent = ScriptableObject.getTopLevelScope(f); - Scriptable thisArg; + Object thisArg; if (args.length < 2 || args[1] == null || args[1] == Undefined.instance) { thisArg = parent; } else { thisArg = ScriptRuntime.toObject(cx, scope, args[1]); } - long length = getLengthProperty(cx, thisObj); int resultLength = id == Id_map ? (int) length : 0; - Scriptable array = cx.newArray(scope, resultLength); + Scriptable array = id != Id_every ? cx.newArray(scope, resultLength) : null; long j=0; for (long i=0; i < length; i++) { Object[] innerArgs = new Object[3]; @@ -1623,12 +1851,12 @@ private Object iterativeMethod(Context cx, int id, Scriptable scope, break; case Id_filter: if (ScriptRuntime.toBoolean(result)) - setElem(cx, array, j++, innerArgs[0]); + defineElem(cx, array, j++, innerArgs[0]); break; case Id_forEach: break; case Id_map: - setElem(cx, array, i, result); + defineElem(cx, array, i, result); break; case Id_some: if (ScriptRuntime.toBoolean(result)) @@ -1653,16 +1881,17 @@ private Object iterativeMethod(Context cx, int id, Scriptable scope, /** * Implements the methods "reduce" and "reduceRight". */ - private Object reduceMethod(Context cx, int id, Scriptable scope, - Scriptable thisObj, Object[] args) + private static Object reduceMethod(Context cx, int id, Scriptable scope, + Object thisObject, Object[] args) { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); + long length = getLengthProperty(cx, thisObj); Object callbackArg = args.length > 0 ? args[0] : Undefined.instance; if (callbackArg == null || !(callbackArg instanceof Function)) { throw ScriptRuntime.notFunctionError(callbackArg); } Function f = (Function) callbackArg; Scriptable parent = ScriptableObject.getTopLevelScope(f); - long length = getLengthProperty(cx, thisObj); // hack to serve both reduce and reduceRight with the same loop boolean movingLeft = id == Id_reduce; Object value = args.length > 1 ? args[1] : Scriptable.NOT_FOUND; diff --git a/src/org/mozilla/javascript/NativeBoolean.java b/src/org/mozilla/javascript/NativeBoolean.java index 35ab7d8c4c..f59fb88c49 100644 --- a/src/org/mozilla/javascript/NativeBoolean.java +++ b/src/org/mozilla/javascript/NativeBoolean.java @@ -94,7 +94,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(BOOLEAN_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); diff --git a/src/org/mozilla/javascript/NativeCall.java b/src/org/mozilla/javascript/NativeCall.java index f4e1949607..1b5868cf89 100644 --- a/src/org/mozilla/javascript/NativeCall.java +++ b/src/org/mozilla/javascript/NativeCall.java @@ -128,7 +128,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(CALL_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); diff --git a/src/org/mozilla/javascript/NativeContinuation.java b/src/org/mozilla/javascript/NativeContinuation.java index 5ed53d839d..5fdaaaa689 100644 --- a/src/org/mozilla/javascript/NativeContinuation.java +++ b/src/org/mozilla/javascript/NativeContinuation.java @@ -74,7 +74,7 @@ public Scriptable construct(Context cx, Scriptable scope, Object[] args) throw Context.reportRuntimeError("Direct call is not supported"); } - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { return Interpreter.restartContinuation(this, cx, scope, args); @@ -102,7 +102,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(FTAG)) { return super.execIdCall(f, cx, scope, thisObj, args); diff --git a/src/org/mozilla/javascript/NativeDate.java b/src/org/mozilla/javascript/NativeDate.java index 2e48b847f3..d9e55b003b 100644 --- a/src/org/mozilla/javascript/NativeDate.java +++ b/src/org/mozilla/javascript/NativeDate.java @@ -47,7 +47,6 @@ import java.text.SimpleDateFormat; import java.util.TimeZone; -import java.util.SimpleTimeZone; /** * This class implements the Date native object. @@ -62,13 +61,6 @@ final class NativeDate extends IdScriptableObject private static final String js_NaN_date_str = "Invalid Date"; - private static final DateFormat isoFormat; - static { - isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - isoFormat.setTimeZone(new SimpleTimeZone(0, "UTC")); - isoFormat.setLenient(false); - } - static void init(Scriptable scope, boolean sealed) { NativeDate obj = new NativeDate(); @@ -114,7 +106,7 @@ protected void fillConstructorProperties(IdFunctionObject ctor) addIdFunctionProperty(ctor, DATE_TAG, ConstructorId_parse, "parse", 1); addIdFunctionProperty(ctor, DATE_TAG, ConstructorId_UTC, - "UTC", 1); + "UTC", 7); super.fillConstructorProperties(ctor); } @@ -124,7 +116,7 @@ protected void initPrototypeId(int id) String s; int arity; switch (id) { - case Id_constructor: arity=1; s="constructor"; break; + case Id_constructor: arity=7; s="constructor"; break; case Id_toString: arity=0; s="toString"; break; case Id_toTimeString: arity=0; s="toTimeString"; break; case Id_toDateString: arity=0; s="toDateString"; break; @@ -178,7 +170,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(DATE_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); @@ -208,10 +200,6 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, case Id_toJSON: { - if (thisObj instanceof NativeDate) { - return ((NativeDate) thisObj).toISOString(); - } - final String toISOString = "toISOString"; Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); @@ -222,7 +210,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, return null; } } - Object toISO = o.get(toISOString, o); + Object toISO = ScriptableObject.getProperty(o, toISOString); if (toISO == NOT_FOUND) { throw ScriptRuntime.typeError2("msg.function.not.found.in", toISOString, @@ -417,23 +405,17 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, return ScriptRuntime.wrapNumber(t); case Id_toISOString: - return realThis.toISOString(); + if (t == t) { + return js_toISOString(t); + } + String msg = ScriptRuntime.getMessage0("msg.invalid.date"); + throw ScriptRuntime.constructError("RangeError", msg); default: throw new IllegalArgumentException(String.valueOf(id)); } } - private String toISOString() { - if (date == date) { - synchronized (isoFormat) { - return isoFormat.format(new Date((long) date)); - } - } - String msg = ScriptRuntime.getMessage0("msg.invalid.date"); - throw ScriptRuntime.constructError("RangeError", msg); - } - /* ECMA helper functions */ private static final double HalfTimeDomain = 8.64e15; @@ -527,6 +509,16 @@ private static double DayFromMonth(int m, int year) return day; } + private static int DaysInMonth(int year, int month) + { + // month is 1-based for DaysInMonth! + if (month == 2) + return IsLeapYear(year) ? 29 : 28; + return month >= 8 + ? 31 - (month & 1) + : 30 + (month & 1); + } + private static int MonthFromTime(double t) { int year = YearFromTime(t); @@ -854,17 +846,165 @@ private static double jsStaticFunction_UTC(Object[] args) return TimeClip(date_msecFromArgs(args)); } + /** + * 15.9.1.15 Date Time String Format
+ * Parse input string according to simplified ISO-8601 Extended Format: + *
    + *
  • YYYY-MM-DD'T'HH:mm:ss.sss'Z'
  • + *
  • or YYYY-MM-DD'T'HH:mm:ss.sss[+-]hh:mm
  • + *
+ */ + private static double parseISOString(String s) { + // we use a simple state machine to parse the input string + final int ERROR = -1; + final int YEAR = 0, MONTH = 1, DAY = 2; + final int HOUR = 3, MIN = 4, SEC = 5, MSEC = 6; + final int TZHOUR = 7, TZMIN = 8; + int state = YEAR; + // default values per [15.9.1.15 Date Time String Format] + int[] values = { 1970, 1, 1, 0, 0, 0, 0, -1, -1 }; + int yearlen = 4, yearmod = 1, tzmod = 1; + int i = 0, len = s.length(); + if (len != 0) { + char c = s.charAt(0); + if (c == '+' || c == '-') { + // 15.9.1.15.1 Extended years + i += 1; + yearlen = 6; + yearmod = (c == '-') ? -1 : 1; + } else if (c == 'T') { + // time-only forms no longer in spec, but follow spidermonkey here + i += 1; + state = HOUR; + } + } + loop: while (state != ERROR) { + int m = i + (state == YEAR ? yearlen : state == MSEC ? 3 : 2); + if (m > len) { + state = ERROR; + break; + } + + int value = 0; + for (; i < m; ++i) { + char c = s.charAt(i); + if (c < '0' || c > '9') { state = ERROR; break loop; } + value = 10 * value + (c - '0'); + } + values[state] = value; + + if (i == len) { + // reached EOF, check for end state + switch (state) { + case HOUR: + case TZHOUR: + state = ERROR; + } + break; + } + + char c = s.charAt(i++); + if (c == 'Z') { + // handle abbrevation for UTC timezone + values[TZHOUR] = 0; + values[TZMIN] = 0; + switch (state) { + case MIN: + case SEC: + case MSEC: + break; + default: + state = ERROR; + } + break; + } + + // state transition + switch (state) { + case YEAR: + case MONTH: + state = (c == '-' ? state + 1 : c == 'T' ? HOUR : ERROR); + break; + case DAY: + state = (c == 'T' ? HOUR : ERROR); + break; + case HOUR: + state = (c == ':' ? MIN : ERROR); + break; + case TZHOUR: + // state = (c == ':' ? state + 1 : ERROR); + // Non-standard extension, https://bugzilla.mozilla.org/show_bug.cgi?id=682754 + if (c != ':') { + // back off by one and try to read without ':' separator + i -= 1; + } + state = TZMIN; + break; + case MIN: + state = (c == ':' ? SEC : c == '+' || c == '-' ? TZHOUR : ERROR); + break; + case SEC: + state = (c == '.' ? MSEC : c == '+' || c == '-' ? TZHOUR : ERROR); + break; + case MSEC: + state = (c == '+' || c == '-' ? TZHOUR : ERROR); + break; + case TZMIN: + state = ERROR; + break; + } + if (state == TZHOUR) { + // save timezone modificator + tzmod = (c == '-') ? -1 : 1; + } + } + + syntax: { + // error or unparsed characters + if (state == ERROR || i != len) break syntax; + + // check values + int year = values[YEAR], month = values[MONTH], day = values[DAY]; + int hour = values[HOUR], min = values[MIN], sec = values[SEC], msec = values[MSEC]; + int tzhour = values[TZHOUR], tzmin = values[TZMIN]; + if (year > 275943 // ceil(1e8/365) + 1970 = 275943 + || (month < 1 || month > 12) + || (day < 1 || day > DaysInMonth(year, month)) + || hour > 24 + || (hour == 24 && (min > 0 || sec > 0 || msec > 0)) + || min > 59 + || sec > 59 + || tzhour > 23 + || tzmin > 59 + ) { + break syntax; + } + // valid ISO-8601 format, compute date in milliseconds + double date = date_msecFromDate(year * yearmod, month - 1, day, + hour, min, sec, msec); + if (tzhour == -1) { + // Spec says to use UTC timezone, the following bug report says + // that local timezone was meant to be used. Stick with spec for now. + // https://bugs.ecmascript.org/show_bug.cgi?id=112 + // date = internalUTC(date); + } else { + date -= (tzhour * 60 + tzmin) * msPerMinute * tzmod; + } + + if (date < -HalfTimeDomain || date > HalfTimeDomain) break syntax; + return date; + } + + // invalid ISO-8601 format, return NaN + return ScriptRuntime.NaN; + } + private static double date_parseString(String s) { - try { - if (s.length() == 24) { - final Date d; - synchronized(isoFormat) { - d = isoFormat.parse(s); - } - return d.getTime(); - } - } catch (java.text.ParseException ex) {} + double d = parseISOString(s); + if (d == d) { + return d; + } int year = -1; int mon = -1; @@ -1074,7 +1214,7 @@ else if (mday < 0) private static String date_format(double t, int methodId) { - StringBuffer result = new StringBuffer(60); + StringBuilder result = new StringBuilder(60); double local = LocalTime(t); /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */ @@ -1214,7 +1354,7 @@ private static String toLocale_helper(double t, int methodId) private static String js_toUTCString(double date) { - StringBuffer result = new StringBuffer(60); + StringBuilder result = new StringBuilder(60); appendWeekDayName(result, WeekDay(date)); result.append(", "); @@ -1237,7 +1377,36 @@ private static String js_toUTCString(double date) return result.toString(); } - private static void append0PaddedUint(StringBuffer sb, int i, int minWidth) + private static String js_toISOString(double t) { + StringBuilder result = new StringBuilder(27); + + int year = YearFromTime(t); + if (year < 0) { + result.append('-'); + year = -year; + } + if (year > 9999) { + append0PaddedUint(result, year, 6); + } else { + append0PaddedUint(result, year, 4); + } + result.append('-'); + append0PaddedUint(result, MonthFromTime(t) + 1, 2); + result.append('-'); + append0PaddedUint(result, DateFromTime(t), 2); + result.append('T'); + append0PaddedUint(result, HourFromTime(t), 2); + result.append(':'); + append0PaddedUint(result, MinFromTime(t), 2); + result.append(':'); + append0PaddedUint(result, SecFromTime(t), 2); + result.append('.'); + append0PaddedUint(result, msFromTime(t), 3); + result.append('Z'); + return result.toString(); + } + + private static void append0PaddedUint(StringBuilder sb, int i, int minWidth) { if (i < 0) Kit.codeBug(); int scale = 1; @@ -1268,7 +1437,7 @@ private static void append0PaddedUint(StringBuffer sb, int i, int minWidth) sb.append((char)('0' + i)); } - private static void appendMonthName(StringBuffer sb, int index) + private static void appendMonthName(StringBuilder sb, int index) { // Take advantage of the fact that all month abbreviations // have the same length to minimize amount of strings runtime has @@ -1281,7 +1450,7 @@ private static void appendMonthName(StringBuffer sb, int index) } } - private static void appendWeekDayName(StringBuffer sb, int index) + private static void appendWeekDayName(StringBuilder sb, int index) { String days = "Sun"+"Mon"+"Tue"+"Wed"+"Thu"+"Fri"+"Sat"; index *= 3; diff --git a/src/org/mozilla/javascript/NativeError.java b/src/org/mozilla/javascript/NativeError.java index 373d5117dc..a221b9aa35 100644 --- a/src/org/mozilla/javascript/NativeError.java +++ b/src/org/mozilla/javascript/NativeError.java @@ -61,6 +61,8 @@ static void init(Scriptable scope, boolean sealed) ScriptableObject.putProperty(obj, "message", ""); ScriptableObject.putProperty(obj, "fileName", ""); ScriptableObject.putProperty(obj, "lineNumber", Integer.valueOf(0)); + obj.setAttributes("name", ScriptableObject.DONTENUM); + obj.setAttributes("message", ScriptableObject.DONTENUM); obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); } @@ -75,8 +77,10 @@ static NativeError make(Context cx, Scriptable scope, int arglen = args.length; if (arglen >= 1) { - ScriptableObject.putProperty(obj, "message", - ScriptRuntime.toString(args[0])); + if (args[0] != Undefined.instance) { + ScriptableObject.putProperty(obj, "message", + ScriptRuntime.toString(args[0])); + } if (arglen >= 2) { ScriptableObject.putProperty(obj, "fileName", args[1]); if (arglen >= 3) { @@ -99,7 +103,7 @@ public String getClassName() public String toString() { // According to spec, Error.prototype.toString() may return undefined. - Object toString = js_toString(this); + Object toString = js_toString(this); return toString instanceof String ? (String) toString : super.toString(); } @@ -119,7 +123,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(ERROR_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); @@ -130,7 +134,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, return make(cx, scope, f, args); case Id_toString: - return js_toString(thisObj); + return js_toString(cx, scope, thisObj); case Id_toSource: return js_toSource(cx, scope, thisObj); @@ -173,6 +177,12 @@ public void setStack(Object value) { put("stack", this, value); } + private static Object js_toString(Context cx, Scriptable scope, + Object thisObject) { + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); + return js_toString(thisObj); + } + private static Object js_toString(Scriptable thisObj) { Object name = ScriptableObject.getProperty(thisObj, "name"); if (name == NOT_FOUND || name == Undefined.instance) { @@ -181,19 +191,25 @@ private static Object js_toString(Scriptable thisObj) { name = ScriptRuntime.toString(name); } Object msg = ScriptableObject.getProperty(thisObj, "message"); - final Object result; if (msg == NOT_FOUND || msg == Undefined.instance) { - result = Undefined.instance; + msg = ""; + } else { + msg = ScriptRuntime.toString(msg); + } + if (name.toString().length() == 0) { + return msg; + } else if (msg.toString().length() == 0) { + return name; } else { - result = ((String) name) + ": " + ScriptRuntime.toString(msg); + return ((String) name) + ": " + ScriptRuntime.toString(msg); } - return result; } private static String js_toSource(Context cx, Scriptable scope, - Scriptable thisObj) + Object thisObject) { // Emulation of SpiderMonkey behavior + Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); Object name = ScriptableObject.getProperty(thisObj, "name"); Object message = ScriptableObject.getProperty(thisObj, "message"); Object fileName = ScriptableObject.getProperty(thisObj, "fileName"); diff --git a/src/org/mozilla/javascript/NativeGenerator.java b/src/org/mozilla/javascript/NativeGenerator.java index 7531d43cc3..9f56aa9d8d 100755 --- a/src/org/mozilla/javascript/NativeGenerator.java +++ b/src/org/mozilla/javascript/NativeGenerator.java @@ -107,7 +107,7 @@ public Object run(Context cx) { Scriptable scope = ScriptableObject.getTopLevelScope(generator); Callable closeGenerator = new Callable() { public Object call(Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) { + Object thisObj, Object[] args) { return ((NativeGenerator)thisObj).resume(cx, scope, GENERATOR_CLOSE, new GeneratorClosedException()); } @@ -134,7 +134,7 @@ protected void initPrototypeId(int id) { @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(GENERATOR_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); diff --git a/src/org/mozilla/javascript/NativeGlobal.java b/src/org/mozilla/javascript/NativeGlobal.java index d473dbf96c..8727625ecd 100644 --- a/src/org/mozilla/javascript/NativeGlobal.java +++ b/src/org/mozilla/javascript/NativeGlobal.java @@ -130,7 +130,6 @@ public static void init(Context cx, Scriptable scope, boolean sealed) { READONLY|DONTENUM|PERMANENT); String[] errorMethods = { - "ConversionError", "EvalError", "RangeError", "ReferenceError", @@ -167,7 +166,7 @@ public static void init(Context cx, Scriptable scope, boolean sealed) { } public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (f.hasTag(FTAG)) { int methodId = f.methodId(); @@ -719,8 +718,6 @@ private static String decode(String str, boolean fullUri) { if (ucs4Char < minUcs4Char || (ucs4Char >= 0xD800 && ucs4Char <= 0xDFFF)) { ucs4Char = INVALID_UTF8; - } else if (ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { - ucs4Char = 0xFFFD; } if (ucs4Char >= 0x10000) { ucs4Char -= 0x10000; diff --git a/src/org/mozilla/javascript/NativeIterator.java b/src/org/mozilla/javascript/NativeIterator.java index 0e6364f35e..fc9dc9aee2 100644 --- a/src/org/mozilla/javascript/NativeIterator.java +++ b/src/org/mozilla/javascript/NativeIterator.java @@ -126,7 +126,7 @@ protected void initPrototypeId(int id) { @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(ITERATOR_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); @@ -158,7 +158,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, /* The JavaScript constructor */ private static Object jsConstructor(Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (args.length == 0 || args[0] == null || args[0] == Undefined.instance) diff --git a/src/org/mozilla/javascript/NativeJSON.java b/src/org/mozilla/javascript/NativeJSON.java index 79854bfd47..fd2f989e55 100644 --- a/src/org/mozilla/javascript/NativeJSON.java +++ b/src/org/mozilla/javascript/NativeJSON.java @@ -100,7 +100,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(JSON_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); diff --git a/src/org/mozilla/javascript/NativeJavaClass.java b/src/org/mozilla/javascript/NativeJavaClass.java index 4cacbd0275..e0770bafba 100644 --- a/src/org/mozilla/javascript/NativeJavaClass.java +++ b/src/org/mozilla/javascript/NativeJavaClass.java @@ -161,7 +161,7 @@ public Object getDefaultValue(Class hint) { return this; } - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { // If it looks like a "cast" of an object to this class type, diff --git a/src/org/mozilla/javascript/NativeJavaConstructor.java b/src/org/mozilla/javascript/NativeJavaConstructor.java index 557aeb5ce6..6ae342b5be 100644 --- a/src/org/mozilla/javascript/NativeJavaConstructor.java +++ b/src/org/mozilla/javascript/NativeJavaConstructor.java @@ -66,7 +66,7 @@ public NativeJavaConstructor(MemberBox ctor) } @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { return NativeJavaClass.constructSpecific(cx, scope, args, ctor); diff --git a/src/org/mozilla/javascript/NativeJavaMethod.java b/src/org/mozilla/javascript/NativeJavaMethod.java index b37fa3b2ac..298bc1c939 100644 --- a/src/org/mozilla/javascript/NativeJavaMethod.java +++ b/src/org/mozilla/javascript/NativeJavaMethod.java @@ -163,7 +163,7 @@ public String toString() } @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { // Find a method that matches the types given. @@ -236,7 +236,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, if (meth.isStatic()) { javaObject = null; // don't need an object } else { - Scriptable o = thisObj; + Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); Class c = meth.getDeclaringClass(); for (;;) { if (o == null) { diff --git a/src/org/mozilla/javascript/NativeJavaTopPackage.java b/src/org/mozilla/javascript/NativeJavaTopPackage.java index a296f4a043..a324dd14ac 100644 --- a/src/org/mozilla/javascript/NativeJavaTopPackage.java +++ b/src/org/mozilla/javascript/NativeJavaTopPackage.java @@ -76,7 +76,7 @@ public class NativeJavaTopPackage super(true, "", loader); } - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { return construct(cx, scope, args); @@ -146,7 +146,7 @@ public static void init(Context cx, Scriptable scope, boolean sealed) } public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (f.hasTag(FTAG)) { if (f.methodId() == Id_getClass) { diff --git a/src/org/mozilla/javascript/NativeMath.java b/src/org/mozilla/javascript/NativeMath.java index d91a80fec2..8d4f0e96b5 100644 --- a/src/org/mozilla/javascript/NativeMath.java +++ b/src/org/mozilla/javascript/NativeMath.java @@ -119,7 +119,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(MATH_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); diff --git a/src/org/mozilla/javascript/NativeNumber.java b/src/org/mozilla/javascript/NativeNumber.java index ec45515e03..9e0e7902cd 100644 --- a/src/org/mozilla/javascript/NativeNumber.java +++ b/src/org/mozilla/javascript/NativeNumber.java @@ -53,8 +53,6 @@ final class NativeNumber extends IdScriptableObject private static final Object NUMBER_TAG = "Number"; - private static final int MAX_PRECISION = 100; - static void init(Scriptable scope, boolean sealed) { NativeNumber obj = new NativeNumber(0.0); @@ -117,7 +115,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(NUMBER_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); @@ -159,7 +157,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, case Id_toFixed: return num_to(value, args, DToA.DTOSTR_FIXED, - DToA.DTOSTR_FIXED, -20, 0); + DToA.DTOSTR_FIXED, 0, 20, 0); case Id_toExponential: { // Handle special values before range check @@ -176,7 +174,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } // General case return num_to(value, args, DToA.DTOSTR_STANDARD_EXPONENTIAL, - DToA.DTOSTR_EXPONENTIAL, 0, 1); + DToA.DTOSTR_EXPONENTIAL, 0, 20, 1); } case Id_toPrecision: { @@ -197,7 +195,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } } return num_to(value, args, DToA.DTOSTR_STANDARD, - DToA.DTOSTR_PRECISION, 1, 0); + DToA.DTOSTR_PRECISION, 1, 21, 0); } default: throw new IllegalArgumentException(String.valueOf(id)); @@ -212,21 +210,21 @@ public String toString() { private static String num_to(double val, Object[] args, int zeroArgMode, int oneArgMode, - int precisionMin, int precisionOffset) + int precisionMin, int precisionMax, + int precisionOffset) { int precision; if (args.length == 0) { precision = 0; oneArgMode = zeroArgMode; } else { - /* We allow a larger range of precision than - ECMA requires; this is permitted by ECMA. */ - precision = ScriptRuntime.toInt32(args[0]); - if (precision < precisionMin || precision > MAX_PRECISION) { + double p = ScriptRuntime.toInteger(args[0]); + if (p < precisionMin || p > precisionMax) { String msg = ScriptRuntime.getMessage1( "msg.bad.precision", ScriptRuntime.toString(args[0])); throw ScriptRuntime.constructError("RangeError", msg); } + precision = ScriptRuntime.toInt32(p); } StringBuilder sb = new StringBuilder(); DToA.JS_dtostr(sb, oneArgMode, precision + precisionOffset, val); diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index 1c2401a7dd..24029f8b91 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -140,7 +140,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(OBJECT_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); @@ -160,7 +160,16 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, return ScriptRuntime.toObject(cx, scope, args[0]); } - case Id_toLocaleString: // For now just alias toString + case Id_toLocaleString: { + Scriptable thisObject = ScriptRuntime.toObject(cx, scope, thisObj); + Object toString = ScriptableObject.getProperty(thisObject, "toString"); + if (!(toString instanceof Callable)) { + ScriptRuntime.notFunctionError(toString); + } + Callable fun = (Callable) toString; + return fun.call(cx, scope, thisObject, ScriptRuntime.emptyArgs); + } + case Id_toString: { if (cx.hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE)) { String s = ScriptRuntime.defaultObjectToSource(cx, scope, @@ -172,49 +181,45 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } return s; } - return ScriptRuntime.defaultObjectToString(thisObj); + return ScriptRuntime.defaultObjectToString(cx, scope, thisObj); } case Id_valueOf: - return thisObj; + return ScriptRuntime.toObject(cx, scope, thisObj); case Id_hasOwnProperty: { boolean result; - if (args.length == 0) { - result = false; + Object arg = args.length < 1 ? Undefined.instance : args[0]; + String s = ScriptRuntime.toStringIdOrIndex(cx, arg); + Scriptable thisObject = ScriptRuntime.toObject(cx, scope, thisObj); + if (s == null) { + int index = ScriptRuntime.lastIndexResult(cx); + result = thisObject.has(index, thisObject); } else { - String s = ScriptRuntime.toStringIdOrIndex(cx, args[0]); - if (s == null) { - int index = ScriptRuntime.lastIndexResult(cx); - result = thisObj.has(index, thisObj); - } else { - result = thisObj.has(s, thisObj); - } + result = thisObject.has(s, thisObject); } return ScriptRuntime.wrapBoolean(result); } case Id_propertyIsEnumerable: { boolean result; - if (args.length == 0) { - result = false; + Object arg = args.length < 1 ? Undefined.instance : args[0]; + String s = ScriptRuntime.toStringIdOrIndex(cx, arg); + Scriptable thisObject = ScriptRuntime.toObject(cx, scope, thisObj); + if (s == null) { + int index = ScriptRuntime.lastIndexResult(cx); + result = thisObject.has(index, thisObject); + if (result && thisObject instanceof ScriptableObject) { + ScriptableObject so = (ScriptableObject)thisObject; + int attrs = so.getAttributes(index); + result = ((attrs & ScriptableObject.DONTENUM) == 0); + } } else { - String s = ScriptRuntime.toStringIdOrIndex(cx, args[0]); - if (s == null) { - int index = ScriptRuntime.lastIndexResult(cx); - result = thisObj.has(index, thisObj); - if (result && thisObj instanceof ScriptableObject) { - ScriptableObject so = (ScriptableObject)thisObj; - int attrs = so.getAttributes(index); - result = ((attrs & ScriptableObject.DONTENUM) == 0); - } - } else { - result = thisObj.has(s, thisObj); - if (result && thisObj instanceof ScriptableObject) { - ScriptableObject so = (ScriptableObject)thisObj; - int attrs = so.getAttributes(s); - result = ((attrs & ScriptableObject.DONTENUM) == 0); - } + result = thisObject.has(s, thisObject); + if (result && thisObject instanceof ScriptableObject) { + ScriptableObject so = (ScriptableObject)thisObject; + int attrs = so.getAttributes(s); + result = ((attrs & ScriptableObject.DONTENUM) == 0); } } return ScriptRuntime.wrapBoolean(result); @@ -223,10 +228,11 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, case Id_isPrototypeOf: { boolean result = false; if (args.length != 0 && args[0] instanceof Scriptable) { + Scriptable thisObject = ScriptRuntime.toObject(cx, scope, thisObj); Scriptable v = (Scriptable) args[0]; do { v = v.getPrototype(); - if (v == thisObj) { + if (v == thisObject) { result = true; break; } diff --git a/src/org/mozilla/javascript/NativeScript.java b/src/org/mozilla/javascript/NativeScript.java index ec1e559666..2022311772 100644 --- a/src/org/mozilla/javascript/NativeScript.java +++ b/src/org/mozilla/javascript/NativeScript.java @@ -82,7 +82,7 @@ public String getClassName() } @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { if (script != null) { @@ -135,7 +135,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(SCRIPT_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); @@ -174,7 +174,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, throw new IllegalArgumentException(String.valueOf(id)); } - private static NativeScript realThis(Scriptable thisObj, IdFunctionObject f) + private static NativeScript realThis(Object thisObj, IdFunctionObject f) { if (!(thisObj instanceof NativeScript)) throw incompatibleCallError(f); diff --git a/src/org/mozilla/javascript/NativeString.java b/src/org/mozilla/javascript/NativeString.java index 2a470ec931..009e7a7f3d 100644 --- a/src/org/mozilla/javascript/NativeString.java +++ b/src/org/mozilla/javascript/NativeString.java @@ -42,6 +42,9 @@ package org.mozilla.javascript; import java.text.Collator; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; /** * This class implements the String native object. @@ -191,7 +194,7 @@ protected void initPrototypeId(int id) case Id_equalsIgnoreCase: arity=1; s="equalsIgnoreCase"; break; case Id_match: arity=1; s="match"; break; case Id_search: arity=1; s="search"; break; - case Id_replace: arity=1; s="replace"; break; + case Id_replace: arity=2; s="replace"; break; case Id_localeCompare: arity=1; s="localeCompare"; break; case Id_toLocaleLowerCase: arity=0; s="toLocaleLowerCase"; break; case Id_toLocaleUpperCase: arity=0; s="toLocaleUpperCase"; break; @@ -203,7 +206,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(STRING_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); @@ -280,6 +283,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, case Id_charAt: case Id_charCodeAt: { // See ECMA 15.5.4.[4,5] + ScriptRuntime.checkObjectCoercible(thisObj); CharSequence target = ScriptRuntime.toCharSequence(thisObj); double pos = ScriptRuntime.toInteger(args, 0); if (pos < 0 || pos >= target.length()) { @@ -292,38 +296,47 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } case Id_indexOf: + ScriptRuntime.checkObjectCoercible(thisObj); return ScriptRuntime.wrapInt(js_indexOf( ScriptRuntime.toString(thisObj), args)); case Id_lastIndexOf: + ScriptRuntime.checkObjectCoercible(thisObj); return ScriptRuntime.wrapInt(js_lastIndexOf( ScriptRuntime.toString(thisObj), args)); case Id_split: + ScriptRuntime.checkObjectCoercible(thisObj); return ScriptRuntime.checkRegExpProxy(cx). js_split(cx, scope, ScriptRuntime.toString(thisObj), args); case Id_substring: + ScriptRuntime.checkObjectCoercible(thisObj); return js_substring(cx, ScriptRuntime.toCharSequence(thisObj), args); case Id_toLowerCase: // See ECMA 15.5.4.11 + ScriptRuntime.checkObjectCoercible(thisObj); return ScriptRuntime.toString(thisObj).toLowerCase( ScriptRuntime.ROOT_LOCALE); case Id_toUpperCase: // See ECMA 15.5.4.12 + ScriptRuntime.checkObjectCoercible(thisObj); return ScriptRuntime.toString(thisObj).toUpperCase( ScriptRuntime.ROOT_LOCALE); case Id_substr: + ScriptRuntime.checkObjectCoercible(thisObj); return js_substr(ScriptRuntime.toCharSequence(thisObj), args); case Id_concat: + ScriptRuntime.checkObjectCoercible(thisObj); return js_concat(ScriptRuntime.toString(thisObj), args); case Id_slice: + ScriptRuntime.checkObjectCoercible(thisObj); return js_slice(ScriptRuntime.toCharSequence(thisObj), args); case Id_bold: @@ -386,8 +399,9 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } else { actionType = RegExpProxy.RA_REPLACE; } + Scriptable thisObject = ScriptRuntime.toObject(cx, scope, thisObj); return ScriptRuntime.checkRegExpProxy(cx). - action(cx, scope, thisObj, args, actionType); + action(cx, scope, thisObject, args, actionType); } // ECMA-262 1 5.5.4.9 case Id_localeCompare: @@ -396,6 +410,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, // actually imagine that this'd be slower than caching them // a la ClassCache, so we aren't trying to outsmart ourselves // with a caching mechanism for now. + ScriptRuntime.checkObjectCoercible(thisObj); Collator collator = Collator.getInstance(cx.getLocale()); collator.setStrength(Collator.IDENTICAL); collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION); @@ -405,16 +420,19 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } case Id_toLocaleLowerCase: { + ScriptRuntime.checkObjectCoercible(thisObj); return ScriptRuntime.toString(thisObj) .toLowerCase(cx.getLocale()); } case Id_toLocaleUpperCase: { + ScriptRuntime.checkObjectCoercible(thisObj); return ScriptRuntime.toString(thisObj) .toUpperCase(cx.getLocale()); } case Id_trim: { + ScriptRuntime.checkObjectCoercible(thisObj); String str = ScriptRuntime.toString(thisObj); char[] chars = str.toCharArray(); @@ -434,7 +452,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } } - private static NativeString realThis(Scriptable thisObj, IdFunctionObject f) + private static NativeString realThis(Object thisObj, IdFunctionObject f) { if (!(thisObj instanceof NativeString)) throw incompatibleCallError(f); @@ -447,6 +465,7 @@ private static NativeString realThis(Scriptable thisObj, IdFunctionObject f) private static String tagify(Object thisObj, String tag, String attribute, Object[] args) { + ScriptRuntime.checkObjectCoercible(thisObj); String str = ScriptRuntime.toString(thisObj); StringBuffer result = new StringBuffer(); result.append('<'); @@ -476,7 +495,6 @@ public String toString() { } /* Make array-style property lookup work for strings. - * XXX is this ECMA? A version check is probably needed. In js too. */ @Override public Object get(int index, Scriptable start) { @@ -494,6 +512,132 @@ public void put(int index, Scriptable start, Object value) { super.put(index, start, value); } + @Override + public boolean has(int index, Scriptable start) { + if (0 <= index && index < string.length()) { + return true; + } + return super.has(index, start); + } + + @Override + public void delete(int index) { + if (0 <= index && index < string.length()) { + return; + } + super.delete(index); + } + + @Override + public int getAttributes(int index) { + if (0 <= index && index < string.length()) { + return READONLY & PERMANENT; + } + return super.getAttributes(index); + } + + @Override + public Object[] getIds() { + Object[] superIds = super.getIds(); + int length = string.length(); + if (length == 0) { + return superIds; + } + int superLength = superIds.length; + Object[] ids = new Object[length + superLength]; + for (int i = 0; i < length; ++i) { + ids[i] = Integer.valueOf(i); + } + System.arraycopy(superIds, 0, ids, length, superLength); + return ids; + } + + @Override + public Object[] getAllIds() { + Set allIds = new LinkedHashSet(Arrays.asList(this + .getIds())); + allIds.addAll(Arrays.asList(super.getAllIds())); + return allIds.toArray(); + } + + private static int toStringIndex(Object id) { + String name = ScriptRuntime.toString(id); + double index = ScriptRuntime.toInteger(id); + if ((int) index != index) { + return -1; + } + if (! name.equals(ScriptRuntime.toString(Math.abs(index)))) { + return -1; + } + return (int) index; + } + + @Override + protected void defineOwnProperty(Context cx, Object id, + ScriptableObject desc, boolean checkValid) { + int index = toStringIndex(id); + if (0 <= index && index < string.length()) { + checkPropertyDefinition(desc); + String name = ScriptRuntime.toString(id); + Object value = getProperty(desc, "value"); + Object configurable = getProperty(desc, "configurable"); + Object enumerable = getProperty(desc, "enumerable"); + Object writable = getProperty(desc, "writable"); + + if (isTrue(configurable)) { + if (! checkValid) return; + throw ScriptRuntime.typeError1( + "msg.change.configurable.false.to.true", name); + } else if (!(enumerable == NOT_FOUND || ScriptRuntime.toBoolean(enumerable))) { + if (! checkValid) return; + throw ScriptRuntime.typeError1( + "msg.change.enumerable.with.configurable.false", name); + } else if (isTrue(writable)) { + if (! checkValid) return; + throw ScriptRuntime.typeError1( + "msg.change.writable.false.to.true.with.configurable.false", name); + } else if (isAccessorDescriptor(desc)) { + if (! checkValid) return; + throw ScriptRuntime.typeError1( + "msg.change.property.data.to.accessor.with.configurable.false", name); + } else if (!(value == NOT_FOUND || sameValue(value, String.valueOf(string.charAt(index))))) { + if (! checkValid) return; + throw ScriptRuntime.typeError1( + "msg.change.value.with.writable.false", name); + } + return; + } + super.defineOwnProperty(cx, id, desc, checkValid); + } + + /* + * ES5.1: 15.5.5.2 [[GetOwnProperty]] for the String object + */ + @Override + protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { + int index = toStringIndex(id); + if (0 <= index && index < string.length()) { + String value = String.valueOf(string.charAt(index)); + return defaultIndexPropertyDescriptor(value); + } + return super.getOwnPropertyDescriptor(cx, id); + } + + private ScriptableObject defaultIndexPropertyDescriptor(Object value) { + Scriptable scope = getParentScope(); + if (scope == null) { + scope = this; + } + ScriptableObject desc = new NativeObject(); + ScriptRuntime.setBuiltinProtoAndParent(desc, scope, + TopLevel.Builtins.Object); + desc.defineProperty("value", value, EMPTY); + desc.defineProperty("writable", false, EMPTY); + desc.defineProperty("enumerable", true, EMPTY); + desc.defineProperty("configurable", false, EMPTY); + return desc; + } + /* * * See ECMA 15.5.4.6. Uses Java String.indexOf() @@ -636,35 +780,32 @@ else if (N == 1) { } private static CharSequence js_slice(CharSequence target, Object[] args) { - if (args.length != 0) { - double begin = ScriptRuntime.toInteger(args[0]); - double end; - int length = target.length(); - if (begin < 0) { - begin += length; - if (begin < 0) - begin = 0; - } else if (begin > length) { - begin = length; - } + double begin = args.length < 1 ? 0 : ScriptRuntime.toInteger(args[0]); + double end; + int length = target.length(); + if (begin < 0) { + begin += length; + if (begin < 0) + begin = 0; + } else if (begin > length) { + begin = length; + } - if (args.length == 1) { + if (args.length < 2 || args[1] == Undefined.instance) { + end = length; + } else { + end = ScriptRuntime.toInteger(args[1]); + if (end < 0) { + end += length; + if (end < 0) + end = 0; + } else if (end > length) { end = length; - } else { - end = ScriptRuntime.toInteger(args[1]); - if (end < 0) { - end += length; - if (end < 0) - end = 0; - } else if (end > length) { - end = length; - } - if (end < begin) - end = begin; } - return target.subSequence((int) begin, (int) end); + if (end < begin) + end = begin; } - return target; + return target.subSequence((int) begin, (int) end); } // #string_id_map# diff --git a/src/org/mozilla/javascript/NativeWith.java b/src/org/mozilla/javascript/NativeWith.java index 749f12a548..dcdbbbf163 100644 --- a/src/org/mozilla/javascript/NativeWith.java +++ b/src/org/mozilla/javascript/NativeWith.java @@ -165,7 +165,7 @@ protected Object updateDotQuery(boolean value) } public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (f.hasTag(FTAG)) { if (f.methodId() == Id_constructor) { diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 388bd2320d..cf367415aa 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -85,7 +85,7 @@ public static BaseFunction typeErrorThrower() { static final long serialVersionUID = -5891740962154902286L; @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { throw typeError0("msg.op.not.allowed"); } @Override @@ -118,7 +118,7 @@ static class NoSuchMethodShim implements Callable { * @param args the array of arguments * @return the result of the call */ - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { Object[] nestedArgs = new Object[2]; @@ -811,6 +811,17 @@ public static String toString(Object val) { } } + static String defaultObjectToString(Context cx, Scriptable scope, Object obj) + { + if (obj == null) { + return "[object Null]"; + } else if (obj == Undefined.instance) { + return "[object Undefined]"; + } + Scriptable val = toObject(cx, scope, obj); + return "[object " + val.getClassName() + ']'; + } + static String defaultObjectToString(Scriptable obj) { return "[object " + obj.getClassName() + ']'; @@ -829,6 +840,11 @@ public static String toString(double val) { } public static String numberToString(double d, int base) { + if ((base < 2) || (base > 36)) { + throw Context.reportRuntimeError1( + "msg.bad.radix", Integer.toString(base)); + } + if (d != d) return "NaN"; if (d == Double.POSITIVE_INFINITY) @@ -838,11 +854,6 @@ public static String numberToString(double d, int base) { if (d == 0.0) return "0"; - if ((base < 2) || (base > 36)) { - throw Context.reportRuntimeError1( - "msg.bad.radix", Integer.toString(base)); - } - if (base != 10) { return DToA.JS_dtobasestr(base, d); } else { @@ -903,8 +914,9 @@ static String uneval(Context cx, Scriptable scope, Object value) } static String defaultObjectToSource(Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObject, Object[] args) { + Scriptable thisObj = toObject(cx, scope, thisObject); boolean toplevel, iterating; if (cx.iterating == null) { toplevel = true; @@ -1067,6 +1079,21 @@ public static Scriptable toObject(Context cx, Scriptable scope, Object val, return toObject(cx, scope, val); } + /** + *

[ECMA 5.1] 9.10 CheckObjectCoercible

+ *

Checks whether {@code val} is convertible by ToObject.

+ * + * @see #toObject(Context, Scriptable, Object) + */ + public static void checkObjectCoercible(Object val) { + if (val == null) { + throw typeError0("msg.null.to.object"); + } + if (val == Undefined.instance) { + throw typeError0("msg.undef.to.object"); + } + } + /** * @deprecated The method is only present for compatibility. */ @@ -2328,7 +2355,6 @@ public static Callable getValueFunctionAndThis(Object value, Context cx) if (!(value instanceof Callable)) { throw notFunctionError(value); } - Callable f = (Callable)value; Scriptable thisObj = null; if (f instanceof Scriptable) { @@ -2441,20 +2467,12 @@ public static Object newSpecial(Context cx, Object fun, */ public static Object applyOrCall(boolean isApply, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { int L = args.length; Callable function = getCallable(thisObj); - Scriptable callThis = null; - if (L != 0) { - callThis = toObjectOrNull(cx, args[0]); - } - if (callThis == null) { - // This covers the case of args[0] == (null|undefined) as well. - callThis = getTopCallScope(cx); - } - + Object callThis = (L != 0 ? args[0] : null); Object[] callArgs; if (isApply) { // Follow Ecma 15.3.4.3 @@ -2484,17 +2502,19 @@ static Object[] getApplyArguments(Context cx, Object arg1) } } - static Callable getCallable(Scriptable thisObj) + static Callable getCallable(Object thisObj) { Callable function; if (thisObj instanceof Callable) { function = (Callable)thisObj; - } else { - Object value = thisObj.getDefaultValue(ScriptRuntime.FunctionClass); + } else if (thisObj instanceof Scriptable) { + Object value = ((ScriptableObject) thisObj).getDefaultValue(ScriptRuntime.FunctionClass); if (!(value instanceof Callable)) { throw ScriptRuntime.notFunctionError(value, thisObj); } function = (Callable)value; + } else { + throw ScriptRuntime.notFunctionError(thisObj); } return function; } @@ -3568,7 +3588,7 @@ public static Scriptable newArrayLiteral(Object[] objects, ++skip; continue; } - ScriptableObject.putProperty(array, i, objects[j]); + ScriptableObject.defineProperty(array, i, objects[j], ScriptableObject.EMPTY); ++j; } return array; diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 0a87a7b527..f3fe9d2119 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -1543,6 +1543,42 @@ private static Class extendsScriptable(Class c) return null; } + /** + * Define a JavaScript property. + * + * Creates the property with an initial value and sets its attributes. + * + * @param propertyName the name of the property to define. + * @param value the initial value of the property + * @param attributes the attributes of the JavaScript property + * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object) + */ + public void defineProperty(int index, Object value, + int attributes) + { + checkNotSealed(null, index); + put(index, this, value); + setAttributes(index, attributes); + } + + /** + * Utility method to add properties to arbitrary Scriptable object. + * If destination is instance of ScriptableObject, calls + * defineProperty there, otherwise calls put in destination + * ignoring attributes + */ + public static void defineProperty(Scriptable destination, + int index, Object value, + int attributes) + { + if (!(destination instanceof ScriptableObject)) { + destination.put(index, destination, value); + return; + } + ScriptableObject so = (ScriptableObject)destination; + so.defineProperty(index, value, attributes); + } + /** * Define a JavaScript property. * @@ -1846,10 +1882,14 @@ protected void defineOwnProperty(Context cx, Object id, ScriptableObject desc, Object getter = getProperty(desc, "get"); if (getter != NOT_FOUND) { gslot.getter = getter; + } else if (isNew) { + gslot.getter = Undefined.instance; } Object setter = getProperty(desc, "set"); if (setter != NOT_FOUND) { gslot.setter = setter; + } else if (isNew) { + gslot.setter = Undefined.instance; } gslot.value = Undefined.instance; @@ -1894,9 +1934,12 @@ protected void checkPropertyChange(String id, ScriptableObject current, if (isTrue(getProperty(desc, "configurable"))) throw ScriptRuntime.typeError1( "msg.change.configurable.false.to.true", id); - if (isTrue(current.get("enumerable", current)) != isTrue(getProperty(desc, "enumerable"))) - throw ScriptRuntime.typeError1( - "msg.change.enumerable.with.configurable.false", id); + if (hasProperty(desc, "enumerable")) { + // only reject if 'enumerable' is present in desc + if (isTrue(current.get("enumerable", current)) != isTrue(getProperty(desc, "enumerable"))) + throw ScriptRuntime.typeError1( + "msg.change.enumerable.with.configurable.false", id); + } boolean isData = isDataDescriptor(desc); boolean isAccessor = isAccessorDescriptor(desc); if (!isData && !isAccessor) { diff --git a/src/org/mozilla/javascript/SpecialRef.java b/src/org/mozilla/javascript/SpecialRef.java index b50b5564aa..c393a9b0e4 100644 --- a/src/org/mozilla/javascript/SpecialRef.java +++ b/src/org/mozilla/javascript/SpecialRef.java @@ -88,6 +88,10 @@ public Object get(Context cx) case SPECIAL_NONE: return ScriptRuntime.getObjectProp(target, name, cx); case SPECIAL_PROTO: + Object proto = target.get("__proto__", target); + if (proto != ScriptableObject.NOT_FOUND) { + return proto; + } return target.getPrototype(); case SPECIAL_PARENT: return target.getParentScope(); diff --git a/src/org/mozilla/javascript/Synchronizer.java b/src/org/mozilla/javascript/Synchronizer.java index 59ecf93679..10a4d2a3b3 100644 --- a/src/org/mozilla/javascript/Synchronizer.java +++ b/src/org/mozilla/javascript/Synchronizer.java @@ -86,7 +86,7 @@ public Synchronizer(Scriptable obj, Object syncObject) { * @see org.mozilla.javascript.Function#call */ @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { Object sync = syncObject != null ? syncObject : thisObj; diff --git a/src/org/mozilla/javascript/commonjs/module/Require.java b/src/org/mozilla/javascript/commonjs/module/Require.java index 5feddc7a1a..5734a27510 100644 --- a/src/org/mozilla/javascript/commonjs/module/Require.java +++ b/src/org/mozilla/javascript/commonjs/module/Require.java @@ -169,7 +169,8 @@ public void install(Scriptable scope) { ScriptableObject.putProperty(scope, "require", this); } - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + @Override + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { if(args == null || args.length < 1) { @@ -213,6 +214,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, return getExportedModuleInterface(cx, id, uri, false); } + @Override public Scriptable construct(Context cx, Scriptable scope, Object[] args) { throw ScriptRuntime.throwError(cx, scope, "require() can not be invoked as a constructor"); diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index 40cd113dac..00004f8e98 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -507,11 +507,39 @@ private void generateCallMethod(ClassFileWriter cfw) cfw.startMethod("call", "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + - "Lorg/mozilla/javascript/Scriptable;" + + "Ljava/lang/Object;" + "[Ljava/lang/Object;)Ljava/lang/Object;", (short)(ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL)); + // Generate code for: + // Scriptable thisObj = ScriptRuntime.toObjectOrNull(cx, thisObject, scope); + // if (thisObj == null) { + // thisObj = ScriptRuntime.getTopCallScope(cx); + // } + int thisObjNull = cfw.acquireLabel(); + cfw.addALoad(1); + cfw.addALoad(3); + cfw.addALoad(2); + cfw.addInvoke(ByteCode.INVOKESTATIC, + "org/mozilla/javascript/ScriptRuntime", + "toObjectOrNull", + "(Lorg/mozilla/javascript/Context;" + +"Ljava/lang/Object;" + +"Lorg/mozilla/javascript/Scriptable;" + +")Lorg/mozilla/javascript/Scriptable;"); + cfw.addAStore(5); + cfw.addALoad(5); + cfw.add(ByteCode.IFNONNULL, thisObjNull); + cfw.addALoad(1); + cfw.addInvoke(ByteCode.INVOKESTATIC, + "org/mozilla/javascript/ScriptRuntime", + "getTopCallScope", + "(Lorg/mozilla/javascript/Context;" + +")Lorg/mozilla/javascript/Scriptable;"); + cfw.addAStore(5); + cfw.markLabel(thisObjNull); + // Generate code for: // if (!ScriptRuntime.hasTopCall(cx)) { // return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args); @@ -528,7 +556,7 @@ private void generateCallMethod(ClassFileWriter cfw) cfw.addALoad(0); cfw.addALoad(1); cfw.addALoad(2); - cfw.addALoad(3); + cfw.addALoad(5); cfw.addALoad(4); cfw.addInvoke(ByteCode.INVOKESTATIC, "org/mozilla/javascript/ScriptRuntime", @@ -546,7 +574,7 @@ private void generateCallMethod(ClassFileWriter cfw) cfw.addALoad(0); cfw.addALoad(1); cfw.addALoad(2); - cfw.addALoad(3); + cfw.addALoad(5); cfw.addALoad(4); int end = scriptOrFnNodes.length; @@ -609,8 +637,8 @@ private void generateCallMethod(ClassFileWriter cfw) getBodyMethodSignature(n)); cfw.add(ByteCode.ARETURN); } - cfw.stopMethod((short)5); - // 5: this, cx, scope, js this, args[] + cfw.stopMethod((short)6); + // 5: this, cx, scope, js this, args[], js this' } private void generateMain(ClassFileWriter cfw) @@ -658,7 +686,7 @@ private void generateExecute(ClassFileWriter cfw) "call", "(Lorg/mozilla/javascript/Context;" +"Lorg/mozilla/javascript/Scriptable;" - +"Lorg/mozilla/javascript/Scriptable;" + +"Ljava/lang/Object;" +"[Ljava/lang/Object;" +")Ljava/lang/Object;"); @@ -3489,7 +3517,7 @@ private void visitOptimizedCall(Node node, OptFunctionNode target, "call", "(Lorg/mozilla/javascript/Context;" +"Lorg/mozilla/javascript/Scriptable;" - +"Lorg/mozilla/javascript/Scriptable;" + +"Ljava/lang/Object;" +"[Ljava/lang/Object;" +")Ljava/lang/Object;"); } diff --git a/src/org/mozilla/javascript/regexp/NativeRegExp.java b/src/org/mozilla/javascript/regexp/NativeRegExp.java index 4994129ae1..5da03d57ad 100644 --- a/src/org/mozilla/javascript/regexp/NativeRegExp.java +++ b/src/org/mozilla/javascript/regexp/NativeRegExp.java @@ -44,7 +44,6 @@ import java.io.Serializable; import org.mozilla.javascript.Context; -import org.mozilla.javascript.Function; import org.mozilla.javascript.IdFunctionObject; import org.mozilla.javascript.IdScriptableObject; import org.mozilla.javascript.Kit; @@ -70,7 +69,7 @@ -public class NativeRegExp extends IdScriptableObject implements Function +public class NativeRegExp extends IdScriptableObject { static final long serialVersionUID = 4965263491464903264L; @@ -173,7 +172,7 @@ public static void init(Context cx, Scriptable scope, boolean sealed) NativeRegExp(Scriptable scope, Object regexpCompiled) { this.re = (RECompiled)regexpCompiled; - this.lastIndex = 0; + this.lastIndex = 0d; ScriptRuntime.setBuiltinProtoAndParent(this, scope, TopLevel.Builtins.RegExp); } @@ -194,17 +193,6 @@ public String getTypeOf() return "object"; } - public Object call(Context cx, Scriptable scope, Scriptable thisObj, - Object[] args) - { - return execSub(cx, scope, args, MATCH); - } - - public Scriptable construct(Context cx, Scriptable scope, Object[] args) - { - return (Scriptable)execSub(cx, scope, args, MATCH); - } - Scriptable compile(Context cx, Scriptable scope, Object[] args) { if (args.length > 0 && args[0] instanceof NativeRegExp) { @@ -217,12 +205,14 @@ Scriptable compile(Context cx, Scriptable scope, Object[] args) this.lastIndex = thatObj.lastIndex; return this; } - String s = args.length == 0 ? "" : ScriptRuntime.toString(args[0]); + String s = args.length == 0 || args[0] == Undefined.instance + ? "" + : ScriptRuntime.toString(args[0]); String global = args.length > 1 && args[1] != Undefined.instance ? ScriptRuntime.toString(args[1]) : null; this.re = (RECompiled)compileRE(cx, s, global, false); - this.lastIndex = 0; + this.lastIndex = 0d; return this; } @@ -262,16 +252,19 @@ private Object execSub(Context cx, Scriptable scopeObj, if (args.length == 0) { str = reImpl.input; if (str == null) { - reportError("msg.no.re.input.for", toString()); + str = ScriptRuntime.toString(Undefined.instance); } } else { str = ScriptRuntime.toString(args[0]); } - double d = ((re.flags & JSREG_GLOB) != 0) ? lastIndex : 0; + double d = 0; + if ((re.flags & JSREG_GLOB) != 0) { + d = ScriptRuntime.toInteger(lastIndex); + } Object rval; if (d < 0 || str.length() < d) { - lastIndex = 0; + lastIndex = 0d; rval = null; } else { @@ -295,15 +288,20 @@ static Object compileRE(Context cx, String str, String global, boolean flat) if (global != null) { for (int i = 0; i < global.length(); i++) { char c = global.charAt(i); + int f = 0; if (c == 'g') { - flags |= JSREG_GLOB; + f = JSREG_GLOB; } else if (c == 'i') { - flags |= JSREG_FOLD; + f = JSREG_FOLD; } else if (c == 'm') { - flags |= JSREG_MULTILINE; + f = JSREG_MULTILINE; } else { reportError("msg.invalid.re.flag", String.valueOf(c)); } + if ((flags & f) != 0) { + reportError("msg.invalid.re.flag", String.valueOf(c)); + } + flags |= f; } } regexp.flags = flags; @@ -1032,6 +1030,7 @@ private static void doFlat(CompilerState state, char c) state.result = new RENode(REOP_DOT); state.progLength++; break; + case '{': case '*': case '+': case '?': @@ -2494,7 +2493,7 @@ protected Object getInstanceIdValue(int id) { switch (id) { case Id_lastIndex: - return ScriptRuntime.wrapNumber(lastIndex); + return lastIndex; case Id_source: return new String(re.source); case Id_global: @@ -2512,7 +2511,7 @@ protected void setInstanceIdValue(int id, Object value) { switch (id) { case Id_lastIndex: - lastIndex = ScriptRuntime.toNumber(value); + lastIndex = value; return; case Id_source: case Id_global: @@ -2529,7 +2528,7 @@ protected void initPrototypeId(int id) String s; int arity; switch (id) { - case Id_compile: arity=1; s="compile"; break; + case Id_compile: arity=2; s="compile"; break; case Id_toString: arity=0; s="toString"; break; case Id_toSource: arity=0; s="toSource"; break; case Id_exec: arity=1; s="exec"; break; @@ -2542,7 +2541,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(REGEXP_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); @@ -2570,7 +2569,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, throw new IllegalArgumentException(String.valueOf(id)); } - private static NativeRegExp realThis(Scriptable thisObj, IdFunctionObject f) + private static NativeRegExp realThis(Object thisObj, IdFunctionObject f) { if (!(thisObj instanceof NativeRegExp)) throw incompatibleCallError(f); @@ -2616,8 +2615,7 @@ protected int findPrototypeId(String s) // #/string_id_map# private RECompiled re; - double lastIndex; /* index after last match, for //g iterator */ - + Object lastIndex = 0d; /* index after last match, for //g iterator */ } // class NativeRegExp class RECompiled implements Serializable diff --git a/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java b/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java index 0298b01455..6e4cae04c8 100644 --- a/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java +++ b/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java @@ -70,7 +70,17 @@ public String getFunctionName() } @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public int getLength() { + return 2; + } + + @Override + public int getArity() { + return 2; + } + + @Override + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { if (args.length > 0 && args[0] instanceof NativeRegExp && diff --git a/src/org/mozilla/javascript/regexp/RegExpImpl.java b/src/org/mozilla/javascript/regexp/RegExpImpl.java index ae87736ec8..745dc2df63 100644 --- a/src/org/mozilla/javascript/regexp/RegExpImpl.java +++ b/src/org/mozilla/javascript/regexp/RegExpImpl.java @@ -65,24 +65,36 @@ public Object action(Context cx, Scriptable scope, { GlobData data = new GlobData(); data.mode = actionType; + data.str = ScriptRuntime.toString(thisObj); switch (actionType) { case RA_MATCH: { - Object rval; - data.optarg = 1; - rval = matchOrReplace(cx, scope, thisObj, args, - this, data, false); + NativeRegExp re = createRegExp(cx, scope, args, 1, false); + Object rval = matchOrReplace(cx, scope, thisObj, args, + this, data, re); return data.arrayobj == null ? rval : data.arrayobj; } case RA_SEARCH: - data.optarg = 1; - return matchOrReplace(cx, scope, thisObj, args, - this, data, false); + { + NativeRegExp re = createRegExp(cx, scope, args, 1, false); + return matchOrReplace(cx, scope, thisObj, args, + this, data, re); + } case RA_REPLACE: { + boolean useRE = args.length != 0 && (args[0] instanceof NativeRegExp); + NativeRegExp re = null; + String search = null; + if (useRE) { + re = createRegExp(cx, scope, args, 2, false); + } else { + Object arg0 = args.length < 1 ? Undefined.instance : args[0]; + search = ScriptRuntime.toString(arg0); + } + Object arg1 = args.length < 2 ? Undefined.instance : args[1]; String repstr = null; Function lambda = null; @@ -91,15 +103,30 @@ public Object action(Context cx, Scriptable scope, } else { repstr = ScriptRuntime.toString(arg1); } - - data.optarg = 2; data.lambda = lambda; data.repstr = repstr; data.dollar = repstr == null ? -1 : repstr.indexOf('$'); data.charBuf = null; data.leftIndex = 0; - Object val = matchOrReplace(cx, scope, thisObj, args, - this, data, true); + + Object val; + if (useRE) { + val = matchOrReplace(cx, scope, thisObj, args, + this, data, re); + } else { + String str = data.str; + int index = str.indexOf(search); + if (index >= 0) { + int slen = search.length(); + this.lastParen = null; + this.leftContext = new SubString(str, 0, index); + this.lastMatch = new SubString(str, index, slen); + this.rightContext = new SubString(str, index + slen, str.length() - index - slen); + val = Boolean.TRUE; + } else { + val = Boolean.FALSE; + } + } if (data.charBuf == null) { if (data.global || val == null @@ -121,21 +148,13 @@ public Object action(Context cx, Scriptable scope, } } - /** - * Analog of C match_or_replace. - */ - private static Object matchOrReplace(Context cx, Scriptable scope, - Scriptable thisObj, Object[] args, - RegExpImpl reImpl, - GlobData data, boolean forceFlat) + private static NativeRegExp createRegExp(Context cx, Scriptable scope, + Object[] args, int optarg, + boolean forceFlat) { NativeRegExp re; - - String str = ScriptRuntime.toString(thisObj); - data.str = str; Scriptable topScope = ScriptableObject.getTopLevelScope(scope); - - if (args.length == 0) { + if (args.length == 0 || args[0] == Undefined.instance) { Object compiled = NativeRegExp.compileRE(cx, "", "", false); re = new NativeRegExp(topScope, compiled); } else if (args[0] instanceof NativeRegExp) { @@ -143,16 +162,27 @@ private static Object matchOrReplace(Context cx, Scriptable scope, } else { String src = ScriptRuntime.toString(args[0]); String opt; - if (data.optarg < args.length) { + if (optarg < args.length) { args[0] = src; - opt = ScriptRuntime.toString(args[data.optarg]); + opt = ScriptRuntime.toString(args[optarg]); } else { opt = null; } Object compiled = NativeRegExp.compileRE(cx, src, opt, forceFlat); re = new NativeRegExp(topScope, compiled); } + return re; + } + /** + * Analog of C match_or_replace. + */ + private static Object matchOrReplace(Context cx, Scriptable scope, + Scriptable thisObj, Object[] args, + RegExpImpl reImpl, + GlobData data, NativeRegExp re) + { + String str = data.str; data.global = (re.getFlags() & NativeRegExp.JSREG_GLOB) != 0; int[] indexp = { 0 }; Object result = null; @@ -164,7 +194,7 @@ private static Object matchOrReplace(Context cx, Scriptable scope, else result = Integer.valueOf(-1); } else if (data.global) { - re.lastIndex = 0; + re.lastIndex = 0d; for (int count = 0; indexp[0] <= str.length(); count++) { result = re.executeRegExp(cx, scope, reImpl, str, indexp, NativeRegExp.TEST); @@ -517,14 +547,6 @@ public Object js_split(Context cx, Scriptable scope, // create an empty Array to return; Scriptable result = cx.newArray(scope, 0); - // return an array consisting of the target if no separator given - // don't check against undefined, because we want - // 'fooundefinedbar'.split(void 0) to split to ['foo', 'bar'] - if (args.length < 1) { - result.put(0, result, target); - return result; - } - // Use the second argument as the split limit, if given. boolean limited = (args.length > 1) && (args[1] != Undefined.instance); long limit = 0; // Initialize to avoid warning. @@ -535,6 +557,12 @@ public Object js_split(Context cx, Scriptable scope, limit = 1 + target.length(); } + // return an array consisting of the target if no separator given + if (args.length < 1 || args[0] == Undefined.instance) { + result.put(0, result, target); + return result; + } + String separator = null; int[] matchlen = new int[1]; Scriptable re = null; @@ -739,7 +767,6 @@ private static int find_split(Context cx, Scriptable scope, String target, final class GlobData { int mode; /* input: return index, match object, or void */ - int optarg; /* input: index of optional flags argument */ boolean global; /* output: whether regexp was global */ String str; /* output: 'this' parameter object as string */ diff --git a/testsrc/doctests/error.tostring.doctest b/testsrc/doctests/error.tostring.doctest index 245c9a0430..9c86dafea0 100644 --- a/testsrc/doctests/error.tostring.doctest +++ b/testsrc/doctests/error.tostring.doctest @@ -3,19 +3,19 @@ js> var str = Error.prototype.toString js> str.call(new TypeError("msg")) TypeError: msg js> str.call(new TypeError()) // message is initialised to '' -TypeError: +TypeError js> str.call(new Error("msg")) Error: msg js> str.call(new Error()) // message is initialised to '' -Error: +Error js> str.call({name:"my error", message:"my message"}) my error: my message -js> str.call({}) === undefined -true -js> str.call({name:"no message defined"}) === undefined -true -js> str.call({name:"message is undefined", message:undefined}) === undefined -true +js> str.call({}) +Error +js> str.call({name:"no message defined"}) +no message defined +js> str.call({name:"message is undefined", message:undefined}) +message is undefined js> str.call({name:"null message", message:null}) null message: null js> str.call({message:"no name defined"}) diff --git a/testsrc/org/mozilla/javascript/tests/TypeOfTest.java b/testsrc/org/mozilla/javascript/tests/TypeOfTest.java index aa74e273f9..c8ad2c0ab0 100644 --- a/testsrc/org/mozilla/javascript/tests/TypeOfTest.java +++ b/testsrc/org/mozilla/javascript/tests/TypeOfTest.java @@ -57,7 +57,7 @@ public void test0() throws Exception final Function f = new BaseFunction() { @Override - public Object call(Context _cx, Scriptable _scope, Scriptable _thisObj, + public Object call(Context _cx, Scriptable _scope, Object _thisObj, Object[] _args) { return _args[0].getClass().getName(); diff --git a/testsrc/org/mozilla/javascript/tests/commonjs/module/ComplianceTest.java b/testsrc/org/mozilla/javascript/tests/commonjs/module/ComplianceTest.java index b33e247ba1..1a2cbada6a 100644 --- a/testsrc/org/mozilla/javascript/tests/commonjs/module/ComplianceTest.java +++ b/testsrc/org/mozilla/javascript/tests/commonjs/module/ComplianceTest.java @@ -88,7 +88,7 @@ private static class Print extends ScriptableObject implements Function setPrototype(ScriptableObject.getFunctionPrototype(scope)); } - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { if(args.length > 1 && "fail".equals(args[1])) { diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java index e7cd92cc15..8bec2fa007 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java @@ -234,7 +234,7 @@ protected void initPrototypeId(int id) public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, + Object thisObj, Object[] args) { if (!f.hasTag(NAMESPACE_TAG)) { @@ -252,7 +252,7 @@ public Object execIdCall(IdFunctionObject f, throw new IllegalArgumentException(String.valueOf(id)); } - private Namespace realThis(Scriptable thisObj, IdFunctionObject f) { + private Namespace realThis(Object thisObj, IdFunctionObject f) { if(!(thisObj instanceof Namespace)) throw incompatibleCallError(f); return (Namespace)thisObj; diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java index 7c124c8b87..166e8d6fa4 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java @@ -260,7 +260,7 @@ protected void initPrototypeId(int id) public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, + Object thisObj, Object[] args) { if (!f.hasTag(QNAME_TAG)) { @@ -278,7 +278,7 @@ public Object execIdCall(IdFunctionObject f, throw new IllegalArgumentException(String.valueOf(id)); } - private QName realThis(Scriptable thisObj, IdFunctionObject f) + private QName realThis(Object thisObj, IdFunctionObject f) { if(!(thisObj instanceof QName)) throw incompatibleCallError(f); diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java index da0431c245..5ddfa013e5 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java @@ -246,7 +246,7 @@ protected void initPrototypeId(int id) @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(XMLCTOR_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java index b210230436..4a01db87ee 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java @@ -744,7 +744,7 @@ private XMLList getPropertyList(XMLName name) { private Object applyOrCall(boolean isApply, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) { + Object thisObj, Object[] args) { String methodName = isApply ? "apply" : "call"; if(!(thisObj instanceof XMLList) || ((XMLList)thisObj).targetProperty == null) @@ -781,7 +781,7 @@ public Scriptable getExtraMethodSource(Context cx) { return null; } - public Object call(Context cx, Scriptable scope, Scriptable thisObj, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { // This XMLList is being called as a Function. // Let's find the real Function object. @@ -798,7 +798,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, throw ScriptRuntime.typeError1("msg.incompat.call", methodName); } Object func = null; - Scriptable sobj = thisObj; + Scriptable sobj = (XMLObject) thisObj; while (sobj instanceof XMLObject) { XMLObject xmlObject = (XMLObject) sobj; diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java index 091d115e2b..a6eeee5642 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java @@ -636,7 +636,7 @@ private void xmlMethodNotFound(Object object, String name) { @Override public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!f.hasTag(XMLOBJECT_TAG)) { return super.execIdCall(f, cx, scope, thisObj, args); From 7e3b6564cc91820ba9b118811e381b740adfc1fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 006/111] Add PropertyDescriptor class and initial implementations for [8.12] "Object Internal Methods" --- .../javascript/PropertyDescriptor.java | 317 ++++++++++++++++ .../mozilla/javascript/ScriptableObject.java | 356 ++++++++++++++++++ 2 files changed, 673 insertions(+) create mode 100755 src/org/mozilla/javascript/PropertyDescriptor.java diff --git a/src/org/mozilla/javascript/PropertyDescriptor.java b/src/org/mozilla/javascript/PropertyDescriptor.java new file mode 100755 index 0000000000..e59e6fb9d1 --- /dev/null +++ b/src/org/mozilla/javascript/PropertyDescriptor.java @@ -0,0 +1,317 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * 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 Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import static org.mozilla.javascript.Scriptable.NOT_FOUND; +import static org.mozilla.javascript.ScriptableObject.DONTENUM; +import static org.mozilla.javascript.ScriptableObject.EMPTY; +import static org.mozilla.javascript.ScriptableObject.PERMANENT; +import static org.mozilla.javascript.ScriptableObject.READONLY; +import static org.mozilla.javascript.ScriptableObject.ensureScriptableObject; + +/** + * + * + */ +final class PropertyDescriptor { + private static final int VALUE = 0x01; + private static final int GET = 0x02; + private static final int SET = 0x04; + private static final int WRITABLE = 0x08; + private static final int ENUMERABLE = 0x10; + private static final int CONFIGURABLE = 0x20; + + private static final int POPULATED_ACCESSOR_DESC = GET | SET | ENUMERABLE + | CONFIGURABLE; + private static final int POPULATED_DATA_DESC = VALUE | WRITABLE + | ENUMERABLE | CONFIGURABLE; + + private int present = 0; + private Object value = Undefined.instance; + private Object getter = Undefined.instance; + private Object setter = Undefined.instance; + private int attributes = READONLY | DONTENUM | PERMANENT; + + private PropertyDescriptor() { + } + + PropertyDescriptor(Object value) { + this.value = value; + this.present = VALUE; + } + + PropertyDescriptor(Object value, int attributes) { + this.value = value; + this.attributes = attributes; + this.present = VALUE | WRITABLE | ENUMERABLE | CONFIGURABLE; + } + + PropertyDescriptor(Object getter, Object setter) { + this.getter = getter; + this.setter = setter; + this.present = GET | SET; + } + + PropertyDescriptor(Object getter, Object setter, int attributes) { + this.getter = getter; + this.setter = setter; + this.attributes = attributes; + this.present = GET | SET | ENUMERABLE | CONFIGURABLE; + } + + static Object fromPropertyDescriptor(Context cx, Scriptable scope, + PropertyDescriptor desc) { + if (desc == null) { + return Undefined.instance; + } + // FromPropertyDescriptor expects a fully populated descriptor + int present = desc.getPresent(); + if ((present & ~POPULATED_ACCESSOR_DESC) != 0 + || (present & ~POPULATED_DATA_DESC) != 0) { + throw new IllegalArgumentException(String.valueOf(present)); + } + + ScriptableObject obj = ensureScriptableObject(cx.newObject(scope)); + if (desc.isDataDescriptor()) { + obj.defineProperty("value", desc.getValue(), EMPTY); + obj.defineProperty("writable", desc.isWritable(), EMPTY); + } else { + obj.defineProperty("get", desc.getGetter(), EMPTY); + obj.defineProperty("set", desc.getSetter(), EMPTY); + } + obj.defineProperty("enumerable", desc.isEnumerable(), EMPTY); + obj.defineProperty("configurable", desc.isConfigurable(), EMPTY); + return obj; + } + + static PropertyDescriptor toPropertyDescriptor(Object object) { + ScriptableObject obj = ensureScriptableObject(object); + PropertyDescriptor desc = new PropertyDescriptor(); + Object enumerable = ScriptableObject.getProperty(obj, "enumerable"); + if (enumerable != NOT_FOUND) { + desc.setEnumerable(ScriptRuntime.toBoolean(enumerable)); + } + Object configurable = ScriptableObject.getProperty(obj, "configurable"); + if (configurable != NOT_FOUND) { + desc.setConfigurable(ScriptRuntime.toBoolean(configurable)); + } + Object value = ScriptableObject.getProperty(obj, "value"); + if (value != NOT_FOUND) { + desc.setValue(value); + } + Object writable = ScriptableObject.getProperty(obj, "writable"); + if (writable != NOT_FOUND) { + desc.setWritable(ScriptRuntime.toBoolean(writable)); + } + Object getter = ScriptableObject.getProperty(obj, "get"); + if (getter != NOT_FOUND) { + if (getter != Undefined.instance && !(getter instanceof Callable)) { + throw ScriptRuntime.notFunctionError(getter); + } + desc.setGetter(getter); + } + Object setter = ScriptableObject.getProperty(obj, "set"); + if (setter != NOT_FOUND) { + if (setter != Undefined.instance && !(setter instanceof Callable)) { + throw ScriptRuntime.notFunctionError(setter); + } + desc.setSetter(setter); + } + if ((desc.present & (GET | SET)) != 0 + && (desc.present & (VALUE | WRITABLE)) != 0) { + throw ScriptRuntime.typeError0("msg.both.data.and.accessor.desc"); + } + return desc; + } + + public boolean isAccessorDescriptor() { + return (present & (GET | SET)) != 0; + } + + public boolean isDataDescriptor() { + return (present & (VALUE | WRITABLE)) != 0; + } + + public boolean isGenericDescriptor() { + return (present & (GET | SET | VALUE | WRITABLE)) == 0; + } + + public int getAttributes() { + return attributes; + } + + public int getAttributeMask() { + int mask = 0; + if ((present & WRITABLE) != 0) { + mask |= READONLY; + } + if ((present & ENUMERABLE) != 0) { + mask |= DONTENUM; + } + if ((present & CONFIGURABLE) != 0) { + mask |= PERMANENT; + } + return mask; + } + + public int getPresent() { + return present; + } + + public boolean hasValue() { + return (present & VALUE) != 0; + } + + public boolean hasGetter() { + return (present & GET) != 0; + } + + public boolean hasSetter() { + return (present & SET) != 0; + } + + public boolean hasWritable() { + return (present & WRITABLE) != 0; + } + + public boolean hasEnumerable() { + return (present & ENUMERABLE) != 0; + } + + public boolean hasConfigurable() { + return (present & CONFIGURABLE) != 0; + } + + /** + * @return the value + */ + public Object getValue() { + return value; + } + + /** + * @param value + * the value to set + */ + public void setValue(Object value) { + present |= VALUE; + this.value = value; + } + + /** + * @return the getter + */ + public Object getGetter() { + return getter; + } + + /** + * @param getter + * the getter to set + */ + public void setGetter(Object getter) { + present |= GET; + this.getter = getter; + } + + /** + * @return the setter + */ + public Object getSetter() { + return setter; + } + + /** + * @param setter + * the setter to set + */ + public void setSetter(Object setter) { + present |= SET; + this.setter = setter; + } + + /** + * @return the writable + */ + public boolean isWritable() { + return (attributes & READONLY) == 0; + } + + /** + * @param writable + * the writable to set + */ + public void setWritable(boolean writable) { + present |= WRITABLE; + attributes = (writable ? attributes & ~READONLY : attributes | READONLY); + } + + /** + * @return the enumerable + */ + public boolean isEnumerable() { + return (attributes & DONTENUM) == 0; + } + + /** + * @param enumerable + * the enumerable to set + */ + public void setEnumerable(boolean enumerable) { + present |= ENUMERABLE; + attributes = (enumerable ? attributes & ~DONTENUM : attributes + | DONTENUM); + } + + /** + * @return the configurable + */ + public boolean isConfigurable() { + return (attributes & PERMANENT) == 0; + } + + /** + * @param configurable + * the configurable to set + */ + public void setConfigurable(boolean configurable) { + present |= CONFIGURABLE; + attributes = (configurable ? attributes & ~PERMANENT : attributes + | PERMANENT); + } +} diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index f3fe9d2119..2cc78dd65f 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -3150,6 +3150,362 @@ protected Slot getSlot(Context cx, Object id, int accessType) { } } + // ECMAScript 5 compliant methods + + private static Object call(Callable call, ScriptableObject thisObject, + Object[] args) { + Context cx = Context.getContext(); + Scriptable scope = (call instanceof Function ? (Function) call + : thisObject).getParentScope(); + return call.call(cx, scope, thisObject, args); + } + + private static boolean isPrimitive(Object value) { + return (value == null || value == Undefined.instance + || value instanceof CharSequence || value instanceof Number || value instanceof Boolean); + } + + /** + * ECMAScript 5 + * + * 8.12.1 [[GetOwnProperty]] (P) + * + */ + protected PropertyDescriptor getOwnProperty(String name) { + Slot slot; + long index = ScriptRuntime.indexFromString(name); + if (index >= 0) { + slot = getSlot(null, (int) index, SLOT_QUERY); + } else { + slot = getSlot(name, 0, SLOT_QUERY); + } + if (slot == null) { + return null; + } + slot = unwrapSlot(slot); + PropertyDescriptor desc; + if (slot instanceof GetterSlot) { + GetterSlot gslot = (GetterSlot) slot; + Object getter = gslot.getter; + Object setter = gslot.setter; + if (getter == null) { + getter = Undefined.instance; + } + if (setter == null) { + setter = Undefined.instance; + } + desc = new PropertyDescriptor(getter, setter, slot.attributes); + } else { + desc = new PropertyDescriptor(slot.value, slot.attributes); + } + return desc; + } + + /** + * ECMAScript 5 + * + * 8.12.2 [[GetProperty]] (P) + * + */ + protected PropertyDescriptor getProperty(String name) { + PropertyDescriptor prop = getOwnProperty(name); + if (prop != null) { + return prop; + } + Scriptable proto = getPrototype(); + if (proto == null || !(proto instanceof ScriptableObject)) { + return null; + } + return ((ScriptableObject) proto).getProperty(name); + } + + /** + * ECMAScript 5 + * + * 8.12.3 [[Get]] (P) + * + */ + protected Object get(String name) { + PropertyDescriptor desc = getProperty(name); + if (desc == null) { + return Undefined.instance; + } else if (desc.isDataDescriptor()) { + return desc.getValue(); + } else { + Object getter = desc.getGetter(); + if (getter == Undefined.instance) { + return Undefined.instance; + } + return call((Callable) getter, this, ScriptRuntime.emptyArgs); + } + } + + /** + * ECMAScript 5 + * + * 8.12.4 [[CanPut]] (P) + * + */ + protected boolean canPut(String name) { + PropertyDescriptor desc = getOwnProperty(name); + if (desc != null) { + if (desc.isAccessorDescriptor()) { + return desc.getSetter() != Undefined.instance; + } else { + return desc.isWritable(); + } + } + Scriptable proto = getPrototype(); + if (proto == null || !(proto instanceof ScriptableObject)) { + return isExtensible(); + } + PropertyDescriptor inherited = ((ScriptableObject) proto) + .getProperty(name); + if (inherited == null) { + return isExtensible(); + } + if (inherited.isAccessorDescriptor()) { + return inherited.getSetter() != Undefined.instance; + } else { + return isExtensible() && inherited.isWritable(); + } + } + + /** + * ECMAScript 5 + * + * 8.12.5 [[Put]] (P, V, Throw) + * + */ + protected void put(String name, Object value, boolean checked) { + if (!canPut(name)) { + if (checked) { + throw ScriptRuntime.typeError("cannot [[Put]]:" + name); + } + return; + } + PropertyDescriptor ownDesc = getOwnProperty(name); + if (ownDesc != null && ownDesc.isDataDescriptor()) { + PropertyDescriptor valueDesc = new PropertyDescriptor(value); + defineOwnProperty(name, valueDesc, checked); + return; + } + PropertyDescriptor desc = getProperty(name); + if (desc != null && desc.isAccessorDescriptor()) { + Object setter = desc.getSetter(); + call((Callable) setter, this, new Object[] { value }); + } else { + PropertyDescriptor newDesc = new PropertyDescriptor(value, EMPTY); + defineOwnProperty(name, newDesc, checked); + } + } + + /** + * ECMAScript 5 + * + * 8.12.6 [[HasProperty]] (P) + * + */ + protected boolean hasProperty(String name) { + return getProperty(name) != null; + } + + /** + * ECMAScript 5 + * + * 8.12.7 [[Delete]] (P, Throw) + * + */ + protected boolean delete(String name, boolean checked) { + PropertyDescriptor desc = getOwnProperty(name); + if (desc == null) { + return true; + } else if (desc.isConfigurable()) { + long index = ScriptRuntime.indexFromString(name); + if (index >= 0) { + removeSlot(null, (int) index); + } else { + removeSlot(name, 0); + } + } else if (checked) { + throw ScriptRuntime.typeError("cannot [[Delete]]: " + name); + } + return false; + } + + /** + * ECMAScript 5 + * + * 8.12.8 [[DefaultValue]] (hint) + */ + protected Object defaultValue(String hint) { + if (hint == null) { + hint = "Number"; + } + String tryFirst, trySecond; + if ("String".equals(hint)) { + tryFirst = "toString"; + trySecond = "valueOf"; + } else if ("Number".equals(hint)) { + tryFirst = "valueOf"; + trySecond = "toString"; + } else { + throw Context.reportRuntimeError1("msg.invalid.type", hint); + } + Object o = get(tryFirst); + if (o instanceof Callable) { + Object val = call((Callable) o, this, ScriptRuntime.emptyArgs); + if (isPrimitive(val)) { + return val; + } + } + o = get(trySecond); + if (o instanceof Callable) { + Object val = call((Callable) o, this, ScriptRuntime.emptyArgs); + if (isPrimitive(val)) { + return val; + } + } + throw ScriptRuntime.typeError1("msg.default.value", hint); + } + + /** + * ECMAScript 5 + * + * 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw) + * + */ + protected boolean defineOwnProperty(String name, PropertyDescriptor desc, + boolean checked) { + PropertyDescriptor current = getOwnProperty(name); + reject: { + if (current == null) { + if (isExtensible()) { + updateOwnProperty(name, desc, current); + return true; + } else { + break reject; + } + } + if (desc.getPresent() == 0) { + return true; + } + if (desc.getPresent() == current.getPresent()) { + int mask = desc.getAttributeMask(); + if ((desc.getAttributes() & mask) == (current.getAttributes() & mask) + && (!desc.hasValue() || sameValue(desc.getValue(), + current.getValue())) + && (!desc.hasGetter() || sameValue(desc.getGetter(), + current.getGetter())) + && (!desc.hasSetter() || sameValue(desc.getSetter(), + current.getSetter()))) { + return true; + } + } + if (!current.isConfigurable()) { + if (desc.isConfigurable()) { + break reject; + } + if (desc.hasEnumerable() + && desc.isEnumerable() != current.isEnumerable()) { + break reject; + } + } + if (desc.isGenericDescriptor()) { + // no further validation required + } else if (current.isDataDescriptor() != desc.isDataDescriptor()) { + if (!current.isConfigurable()) { + break reject; + } + } else if (current.isDataDescriptor() && desc.isDataDescriptor()) { + if (!current.isConfigurable() && !current.isWritable()) { + if (desc.isWritable()) { + break reject; + } else if (desc.hasValue() + && !sameValue(desc.getValue(), current.getValue())) { + break reject; + } + } + } else { + if (!current.isConfigurable()) { + if (desc.hasSetter() + && !sameValue(desc.getSetter(), current.getSetter())) { + break reject; + } else if (desc.hasGetter() + && !sameValue(desc.getGetter(), current.getGetter())) { + break reject; + } + } + } + // update + updateOwnProperty(name, desc, current); + return true; + } + if (checked) { + throw ScriptRuntime.typeError("[[DefineOwnProperty]]"); + } + return false; + } + + private void updateOwnProperty(String name, PropertyDescriptor desc, + PropertyDescriptor current) { + long index = ScriptRuntime.indexFromString(name); + if (index >= 0) { + name = null; + } else { + index = 0; + } + Slot slot = getSlot(name, (int) index, SLOT_QUERY); + boolean isAccessor = desc.isAccessorDescriptor(); + boolean isNew = slot == null; + if (slot == null) { + slot = getSlot(name, (int) index, + isAccessor ? SLOT_MODIFY_GETTER_SETTER : SLOT_MODIFY); + } + slot = unwrapSlot(slot); + + int baseAttrs, mask; + if (isNew) { + baseAttrs = 0; + mask = isAccessor ? (DONTENUM | PERMANENT) + : (READONLY | DONTENUM | PERMANENT); + } else { + baseAttrs = slot.getAttributes(); + mask = desc.getAttributeMask(); + } + + if (isAccessor) { + boolean hasGetter = isNew || desc.hasGetter(); + boolean hasSetter = isNew || desc.hasSetter(); + if (!(slot instanceof GetterSlot)) { + hasGetter = hasSetter = true; + baseAttrs = (baseAttrs & (DONTENUM | PERMANENT)); + slot = getSlot(name, (int) index, SLOT_MODIFY_GETTER_SETTER); + } + GetterSlot gslot = (GetterSlot) slot; + gslot.value = null; + if (hasGetter) { + gslot.getter = desc.getGetter(); + } + if (hasSetter) { + gslot.setter = desc.getSetter(); + } + } else { + boolean hasValue = isNew || desc.hasValue(); + if (slot instanceof GetterSlot && desc.isDataDescriptor()) { + hasValue = true; + mask |= READONLY; + baseAttrs = (baseAttrs & (DONTENUM | PERMANENT)); + slot = getSlot(name, (int) index, SLOT_CONVERT_ACCESSOR_TO_DATA); + } + if (hasValue) { + slot.value = desc.getValue(); + } + } + int attributes = (baseAttrs & ~mask) | (desc.getAttributes() & mask); + slot.attributes = (short) attributes; + } + // Partial implementation of java.util.Map. See NativeObject for // a subclass that implements java.util.Map. From d1963720a50360170bbd02893d3314990312317c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 007/111] switch to new [[DefineProperty]] implementation in ScriptableObject --- .../mozilla/javascript/ScriptableObject.java | 63 ++----------------- 1 file changed, 4 insertions(+), 59 deletions(-) diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 2cc78dd65f..f34a54019f 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -1849,64 +1849,9 @@ public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { */ protected void defineOwnProperty(Context cx, Object id, ScriptableObject desc, boolean checkValid) { - - Slot slot = getSlot(cx, id, SLOT_QUERY); - boolean isNew = slot == null; - - if (checkValid) { - ScriptableObject current = slot == null ? - null : slot.getPropertyDescriptor(cx, this); - String name = ScriptRuntime.toString(id); - checkPropertyChange(name, current, desc); - } - - boolean isAccessor = isAccessorDescriptor(desc); - final int attributes; - - if (slot == null) { // new slot - slot = getSlot(cx, id, isAccessor ? SLOT_MODIFY_GETTER_SETTER : SLOT_MODIFY); - attributes = applyDescriptorToAttributeBitset(DONTENUM|READONLY|PERMANENT, desc); - } else { - attributes = applyDescriptorToAttributeBitset(slot.getAttributes(), desc); - } - - slot = unwrapSlot(slot); - - if (isAccessor) { - if ( !(slot instanceof GetterSlot) ) { - slot = getSlot(cx, id, SLOT_MODIFY_GETTER_SETTER); - } - - GetterSlot gslot = (GetterSlot) slot; - - Object getter = getProperty(desc, "get"); - if (getter != NOT_FOUND) { - gslot.getter = getter; - } else if (isNew) { - gslot.getter = Undefined.instance; - } - Object setter = getProperty(desc, "set"); - if (setter != NOT_FOUND) { - gslot.setter = setter; - } else if (isNew) { - gslot.setter = Undefined.instance; - } - - gslot.value = Undefined.instance; - gslot.setAttributes(attributes); - } else { - if (slot instanceof GetterSlot && isDataDescriptor(desc)) { - slot = getSlot(cx, id, SLOT_CONVERT_ACCESSOR_TO_DATA); - } - - Object value = getProperty(desc, "value"); - if (value != NOT_FOUND) { - slot.value = value; - } else if (isNew) { - slot.value = Undefined.instance; - } - slot.setAttributes(attributes); - } + String name = ScriptRuntime.toString(id); + PropertyDescriptor d = PropertyDescriptor.toPropertyDescriptor(desc); + defineOwnProperty(name, d, checkValid); } protected void checkPropertyDefinition(ScriptableObject desc) { @@ -3447,7 +3392,7 @@ protected boolean defineOwnProperty(String name, PropertyDescriptor desc, return false; } - private void updateOwnProperty(String name, PropertyDescriptor desc, + protected void updateOwnProperty(String name, PropertyDescriptor desc, PropertyDescriptor current) { long index = ScriptRuntime.indexFromString(name); if (index >= 0) { From 1cadfddc3afe3804da66e8013d23afe29a388324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 008/111] Implement custom [[DefineProperty]] for IdScriptableObject --- .../javascript/IdScriptableObject.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index 67dd71a308..52c2ab04d5 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -819,6 +819,77 @@ private ScriptableObject getBuiltInDescriptor(String name) { return null; } + private static class IdPropertyDescriptor extends PropertyDescriptor { + final int id; + final boolean proto; + + public IdPropertyDescriptor(Object value, int attributes, int id, + boolean proto) { + super(value, attributes); + this.id = id; + this.proto = proto; + } + } + + @Override + protected PropertyDescriptor getOwnProperty(String name) { + PropertyDescriptor desc = super.getOwnProperty(name); + if (desc == null) { + int info = findInstanceIdInfo(name); + if (info != 0) { + int id = (info & 0xFFFF); + Object value = getInstanceIdValue(id); + int attr = (info >>> 16); + desc = new IdPropertyDescriptor(value, attr, id, false); + } else if (prototypeValues != null) { + int id = prototypeValues.findId(name); + if (id != 0) { + Object value = prototypeValues.get(id); + int attr = prototypeValues.getAttributes(id); + desc = new IdPropertyDescriptor(value, attr, id, true); + } + } + } + return desc; + } + + @Override + protected void updateOwnProperty(String name, PropertyDescriptor desc, + PropertyDescriptor current) { + if (current instanceof IdPropertyDescriptor) { + IdPropertyDescriptor iddesc = (IdPropertyDescriptor) current; + int id = iddesc.id; + int baseAttrs = iddesc.getAttributes(); + int mask = desc.getAttributeMask(); + int attributes = (baseAttrs & ~mask) + | (desc.getAttributes() & mask); + if (!iddesc.proto) { + if (desc.isAccessorDescriptor()) { + delete(id); // it will be replaced with a slot + } else { + if (desc.hasValue()) { + setInstanceIdValue(id, desc.getValue()); + } + if (attributes != baseAttrs) { + setInstanceIdAttributes(id, attributes); + } + return; + } + } else { + if (desc.isAccessorDescriptor()) { + prototypeValues.delete(id); // it will be replaced with a slot + } else { + if (desc.hasValue()) { + prototypeValues.set(id, this, desc.getValue()); + } + prototypeValues.setAttributes(id, attributes); + return; + } + } + } + super.updateOwnProperty(name, desc, current); + } + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { From 8ef010fbf0bbc676ffae4126e7ffe5bdfbf144c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 009/111] Implement custom [[DefineProperty]] for NativeArray --- src/org/mozilla/javascript/NativeArray.java | 140 ++++++++++++++++++ .../javascript/PropertyDescriptor.java | 55 ++++++- 2 files changed, 194 insertions(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index 41d93882d4..f917b145de 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -42,11 +42,13 @@ import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.ListIterator; +import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; @@ -719,6 +721,144 @@ protected void defineOwnProperty(Context cx, Object id, super.defineOwnProperty(cx, id, desc, checkValid); } + @Override + protected PropertyDescriptor getOwnProperty(String name) { + if (dense != null) { + int index = toDenseIndex(name); + if (0 <= index && index < dense.length && dense[index] != NOT_FOUND) { + return new PropertyDescriptor(dense[index], EMPTY); + } + } + return super.getOwnProperty(name); + } + + /** + * 15.4.5.1 [[DefineOwnProperty]] for Array 'length' + */ + private boolean defineOwnPropertyLength(PropertyDescriptor desc, + boolean checked) { + reject: { + if (!desc.hasValue()) { + return super.defineOwnProperty("length", desc, checked); + } + + long newLen = ScriptRuntime.toUint32(desc.getValue()); + if (newLen != ScriptRuntime.toNumber(desc.getValue())) { + String msg = ScriptRuntime.getMessage0("msg.arraylength.bad"); + throw ScriptRuntime.constructError("RangeError", msg); + } + PropertyDescriptor newLenDesc = new PropertyDescriptor(desc); + newLenDesc.setValue(newLen); + + if (newLen >= length) { + return super.defineOwnProperty("length", newLenDesc, checked); + } + if ((lengthAttr & READONLY) != 0) { + break reject; + } + boolean readonly = newLenDesc.hasWritable() + && !newLenDesc.isWritable(); + if (readonly) { + newLenDesc.setWritable(true); + } + boolean succeeded = super.defineOwnProperty("length", newLenDesc, + checked); + if (!succeeded) { + return false; + } + // length updated to expected value? + succeeded = (length == newLen); + + if (readonly) { + // short cut: set length to read-only + // super.defineOwnProperty("length", {writable: false}, false); + lengthAttr = lengthAttr | READONLY; + } + + if (!succeeded) { + break reject; + } + return true; + } + if (checked) { + throw ScriptRuntime.typeError("[[DefineOwnProperty]]"); + } + return false; + } + + /** + * 15.4.5.1 [[DefineOwnProperty]] for Array 'index' + */ + private boolean defineOwnPropertyIndex(String name, + PropertyDescriptor desc, boolean checked, long index) { + reject: { + long oldLen = length; + if (index >= oldLen && (lengthAttr & READONLY) != 0) { + break reject; + } + if (dense != null) { + // convert all dense entries to slots + Object[] values = dense; + dense = null; + denseOnly = false; + for (int i = 0; i < values.length; i++) { + if (values[i] != NOT_FOUND) { + put(i, this, values[i]); + } + } + } + boolean succeeded = super.defineOwnProperty(name, desc, false); + if (!succeeded) { + break reject; + } + if (index >= oldLen) { + // short cut: update length + // super.defineOwnProperty("length", {value: index + 1}, false); + length = index + 1; + } + return true; + } + if (checked) { + throw ScriptRuntime.typeError("[[DefineOwnProperty]]"); + } + return false; + } + + @Override + protected boolean defineOwnProperty(String name, PropertyDescriptor desc, + boolean checked) { + if ("length".equals(name)) { + // intercept 'length' property + return defineOwnPropertyLength(desc, checked); + } + long index = toArrayIndex(name); + if (index >= 0) { + // intercept index properties + return defineOwnPropertyIndex(name, desc, checked, index); + } + return super.defineOwnProperty(name, desc, checked); + } + + @Override + protected void updateOwnProperty(String name, PropertyDescriptor desc, + PropertyDescriptor current) { + if ("length".equals(name)) { + int mask = desc.getAttributeMask(); + lengthAttr = (lengthAttr & ~mask) | (desc.getAttributes() & mask); + if (desc.hasValue()) { + long newLen = ((Number) desc.getValue()).longValue(); + long entry = updateEntries(length, newLen); + if (entry != -1) { + // account for undeletable entries + newLen = entry + 1; + } + length = newLen; + } + return; + } + super.updateOwnProperty(name, desc, current); + } + /** * See ECMA 15.4.1,2 */ diff --git a/src/org/mozilla/javascript/PropertyDescriptor.java b/src/org/mozilla/javascript/PropertyDescriptor.java index e59e6fb9d1..c177679ee6 100755 --- a/src/org/mozilla/javascript/PropertyDescriptor.java +++ b/src/org/mozilla/javascript/PropertyDescriptor.java @@ -44,11 +44,17 @@ import static org.mozilla.javascript.ScriptableObject.READONLY; import static org.mozilla.javascript.ScriptableObject.ensureScriptableObject; +import java.util.Map; + /** * * */ -final class PropertyDescriptor { +class PropertyDescriptor { + public static enum Field { + Value, Get, Set, Writable, Enumerable, Configurable; + } + private static final int VALUE = 0x01; private static final int GET = 0x02; private static final int SET = 0x04; @@ -70,6 +76,14 @@ final class PropertyDescriptor { private PropertyDescriptor() { } + PropertyDescriptor(PropertyDescriptor desc) { + this.present = desc.present; + this.value = desc.value; + this.getter = desc.getter; + this.setter = desc.setter; + this.attributes = desc.attributes; + } + PropertyDescriptor(Object value) { this.value = value; this.present = VALUE; @@ -119,6 +133,45 @@ static Object fromPropertyDescriptor(Context cx, Scriptable scope, return obj; } + static PropertyDescriptor toPropertyDescriptor(Map map) { + PropertyDescriptor desc = new PropertyDescriptor(); + for (Map.Entry entry : map.entrySet()) { + Field field = entry.getKey(); + Object value = entry.getValue(); + switch (field) { + case Writable: + desc.setWritable(ScriptRuntime.toBoolean(value)); + break; + case Enumerable: + desc.setEnumerable(ScriptRuntime.toBoolean(value)); + break; + case Configurable: + desc.setConfigurable(ScriptRuntime.toBoolean(value)); + break; + case Value: + desc.setValue(value); + break; + case Get: + if (value != Undefined.instance && !(value instanceof Callable)) { + throw ScriptRuntime.notFunctionError(value); + } + desc.setGetter(value); + break; + case Set: + if (value != Undefined.instance && !(value instanceof Callable)) { + throw ScriptRuntime.notFunctionError(value); + } + desc.setSetter(value); + break; + } + } + if ((desc.present & (GET | SET)) != 0 + && (desc.present & (VALUE | WRITABLE)) != 0) { + throw ScriptRuntime.typeError0("msg.both.data.and.accessor.desc"); + } + return desc; + } + static PropertyDescriptor toPropertyDescriptor(Object object) { ScriptableObject obj = ensureScriptableObject(object); PropertyDescriptor desc = new PropertyDescriptor(); From 63043a7bf5ddca2fed898b22c89d75e0a97c6eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 010/111] Implement custom [[GetOwnProperty]] and [[DefineOwnProperty]] for NativeString --- src/org/mozilla/javascript/NativeString.java | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/org/mozilla/javascript/NativeString.java b/src/org/mozilla/javascript/NativeString.java index 009e7a7f3d..0f5843a636 100644 --- a/src/org/mozilla/javascript/NativeString.java +++ b/src/org/mozilla/javascript/NativeString.java @@ -638,6 +638,27 @@ private ScriptableObject defaultIndexPropertyDescriptor(Object value) { return desc; } + @Override + protected PropertyDescriptor getOwnProperty(String name) { + int index = toStringIndex(name); + if (0 <= index && index < string.length()) { + String value = String.valueOf(string.charAt(index)); + return new PropertyDescriptor(value, READONLY | PERMANENT); + } + return super.getOwnProperty(name); + } + + @Override + protected void updateOwnProperty(String name, PropertyDescriptor desc, + PropertyDescriptor current) { + int index = toStringIndex(name); + if (0 <= index && index < string.length()) { + // nothing to change + return; + } + super.updateOwnProperty(name, desc, current); + } + /* * * See ECMA 15.5.4.6. Uses Java String.indexOf() From 0208e12b7f37cf1c2731f6c376df37e5be4e0778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 011/111] Add [[Get]], [[Delete]], [[DefineOwnProperty]] and [[GetOwnProperty]] for Arguments --- src/org/mozilla/javascript/Arguments.java | 107 +++++++++++++++++- .../mozilla/javascript/ScriptableObject.java | 1 + 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/Arguments.java b/src/org/mozilla/javascript/Arguments.java index 9123cb6078..ddfef3e6d4 100644 --- a/src/org/mozilla/javascript/Arguments.java +++ b/src/org/mozilla/javascript/Arguments.java @@ -231,10 +231,16 @@ protected int findInstanceIdInfo(String s) int attr; switch (id) { case Id_callee: + attr = calleeAttr; + break; case Id_caller: + attr = callerAttr; + break; case Id_length: + attr = lengthAttr; + break; case Id_constructor: - attr = DONTENUM; + attr = constructorAttr; break; default: throw new IllegalStateException(); } @@ -292,6 +298,17 @@ protected void setInstanceIdValue(int id, Object value) super.setInstanceIdValue(id, value); } + @Override + protected void setInstanceIdAttributes(int id, int attr) { + switch (id) { + case Id_callee: calleeAttr = attr; return; + case Id_length: lengthAttr = attr; return; + case Id_caller: callerAttr = attr; return; + case Id_constructor: constructorAttr = attr; return; + } + super.setInstanceIdAttributes(id, attr); + } + @Override Object[] getIds(boolean getAll) { @@ -405,6 +422,89 @@ protected void defineOwnProperty(Context cx, Object id, } } + @Override + protected Object get(String name) { + double d = ScriptRuntime.toNumber(name); + int index = (int) d; + if (d == index) { + Object value = arg(index); + if (value != NOT_FOUND) { + if (sharedWithActivation(index)) { + return getFromActivation(index); + } else { + return value; + } + } + } + return super.get(name); + } + + @Override + protected boolean delete(String name, boolean checked) { + double d = ScriptRuntime.toNumber(name); + int index = (int) d; + boolean result = super.delete(name, checked); + if (result && d == index) { + if (0 <= index && index < args.length) { + removeArg(index); + } + } + return result; + } + + @Override + protected boolean defineOwnProperty(String name, PropertyDescriptor desc, + boolean checked) { + double d = ScriptRuntime.toNumber(name); + int index = (int) d; + boolean isMapped = (d == index && arg(index) != NOT_FOUND); + + boolean allowed = super.defineOwnProperty(name, desc, false); + if (!allowed) { + if (checked) { + throw ScriptRuntime.typeError("[[DefineOwnProperty]]"); + } + return false; + } + + if (isMapped) { + if (desc.isAccessorDescriptor()) { + removeArg(index); + } else { + if (desc.hasValue()) { + replaceArg(index, desc.getValue()); + } + if (desc.hasWritable() && !desc.isWritable()) { + removeArg(index); + } + } + } + return true; + } + + @Override + protected PropertyDescriptor getOwnProperty(String name) { + PropertyDescriptor desc = super.getOwnProperty(name); + double d = ScriptRuntime.toNumber(name); + int index = (int) d; + if (d != index) { + return desc; + } + Object value = arg(index); + if (value == NOT_FOUND) { + return desc; + } + if (sharedWithActivation(index)) { + value = getFromActivation(index); + } + if (desc != null) { // the descriptor has been redefined + desc.setValue(value); + } else { + desc = new PropertyDescriptor(value, EMPTY); + } + return desc; + } + // Fields to hold caller, callee and length properties, // where NOT_FOUND value tags deleted properties. // In addition if callerObj == NULL_VALUE, it tags null for scripts, as @@ -415,6 +515,11 @@ protected void defineOwnProperty(Context cx, Object id, private Object lengthObj; private Object constructor; + private int callerAttr = DONTENUM; + private int calleeAttr = DONTENUM; + private int lengthAttr = DONTENUM; + private int constructorAttr = DONTENUM; + private NativeCall activation; // Initially args holds activation.getOriginalArgs(), but any modification diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index f34a54019f..e38dc40b51 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -3272,6 +3272,7 @@ protected boolean delete(String name, boolean checked) { } else { removeSlot(name, 0); } + return true; } else if (checked) { throw ScriptRuntime.typeError("cannot [[Delete]]: " + name); } From e5baec3135a09c81187ca19a41339ad029a7efb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 012/111] Update BoundFunction to new [[DefineOwnProperty]] API --- src/org/mozilla/javascript/BoundFunction.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/org/mozilla/javascript/BoundFunction.java b/src/org/mozilla/javascript/BoundFunction.java index e7476d7b5e..3f614bf348 100644 --- a/src/org/mozilla/javascript/BoundFunction.java +++ b/src/org/mozilla/javascript/BoundFunction.java @@ -68,15 +68,9 @@ public BoundFunction(Context cx, Scriptable scope, Callable targetFunction, Scri ScriptRuntime.setFunctionProtoAndParent(this, scope); Function thrower = ScriptRuntime.typeErrorThrower(); - NativeObject throwing = new NativeObject(); - throwing.put("get", throwing, thrower); - throwing.put("set", throwing, thrower); - throwing.put("enumerable", throwing, false); - throwing.put("configurable", throwing, false); - throwing.preventExtensions(); - - this.defineOwnProperty(cx, "caller", throwing, false); - this.defineOwnProperty(cx, "arguments", throwing, false); + PropertyDescriptor throwing = new PropertyDescriptor(thrower, thrower, DONTENUM | PERMANENT); + this.defineOwnProperty("caller", throwing, false); + this.defineOwnProperty("arguments", throwing, false); } @Override From 96b943096a705c822bf1baa87073ae5f0ee0fb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 013/111] Update NativeObject to new [[DefineOwnProperty]] API --- src/org/mozilla/javascript/NativeObject.java | 94 +++++++++++++------- 1 file changed, 64 insertions(+), 30 deletions(-) diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index 24029f8b91..5f0d007dbd 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -337,17 +337,18 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, ScriptableObject obj = ensureScriptableObject(arg); Object nameArg = args.length < 2 ? Undefined.instance : args[1]; String name = ScriptRuntime.toString(nameArg); - Scriptable desc = obj.getOwnPropertyDescriptor(cx, name); - return desc == null ? Undefined.instance : desc; + PropertyDescriptor desc = obj.getOwnProperty(name); + return PropertyDescriptor.fromPropertyDescriptor(cx, scope, desc); } case ConstructorId_defineProperty: { Object arg = args.length < 1 ? Undefined.instance : args[0]; ScriptableObject obj = ensureScriptableObject(arg); - Object name = args.length < 2 ? Undefined.instance : args[1]; + Object nameArg = args.length < 2 ? Undefined.instance : args[1]; + String name = ScriptRuntime.toString(nameArg); Object descArg = args.length < 3 ? Undefined.instance : args[2]; - ScriptableObject desc = ensureScriptableObject(descArg); - obj.defineOwnProperty(cx, name, desc); + PropertyDescriptor desc = PropertyDescriptor.toPropertyDescriptor(descArg); + obj.defineOwnProperty(name, desc, true); return obj; } case ConstructorId_isExtensible: @@ -368,27 +369,24 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, Object arg = args.length < 1 ? Undefined.instance : args[0]; ScriptableObject obj = ensureScriptableObject(arg); Object propsObj = args.length < 2 ? Undefined.instance : args[1]; - Scriptable props = Context.toObject(propsObj, getParentScope()); - obj.defineOwnProperties(cx, ensureScriptableObject(props)); + js_defineProperties(cx, getParentScope(), obj, propsObj); return obj; - } + } case ConstructorId_create: { Object arg = args.length < 1 ? Undefined.instance : args[0]; Scriptable obj = (arg == null) ? null : ensureScriptable(arg); ScriptableObject newObject = new NativeObject(); - newObject.setParentScope(this.getParentScope()); + newObject.setParentScope(getParentScope()); newObject.setPrototype(obj); if (args.length > 1 && args[1] != Undefined.instance) { - Scriptable props = Context.toObject(args[1], getParentScope()); - newObject.defineOwnProperties(cx, ensureScriptableObject(props)); + js_defineProperties(cx, getParentScope(), newObject, args[1]); } return newObject; } - case ConstructorId_isSealed: { Object arg = args.length < 1 ? Undefined.instance : args[0]; @@ -396,10 +394,14 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, if (obj.isExtensible()) return Boolean.FALSE; - for (Object name: obj.getAllIds()) { - Object configurable = obj.getOwnPropertyDescriptor(cx, name).get("configurable"); - if (Boolean.TRUE.equals(configurable)) + for (Object name : obj.getAllIds()) { + String sname = ScriptRuntime.toString(name); + PropertyDescriptor desc = obj.getOwnProperty(sname); + if (desc == null) { + continue; + } else if (desc.isConfigurable()) { return Boolean.FALSE; + } } return Boolean.TRUE; @@ -411,12 +413,15 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, if (obj.isExtensible()) return Boolean.FALSE; - for (Object name: obj.getAllIds()) { - ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name); - if (Boolean.TRUE.equals(desc.get("configurable"))) - return Boolean.FALSE; - if (isDataDescriptor(desc) && Boolean.TRUE.equals(desc.get("writable"))) + for (Object name : obj.getAllIds()) { + String sname = ScriptRuntime.toString(name); + PropertyDescriptor desc = obj.getOwnProperty(sname); + if (desc == null) { + continue; + } else if ((desc.isDataDescriptor() && desc.isWritable()) + || desc.isConfigurable()) { return Boolean.FALSE; + } } return Boolean.TRUE; @@ -427,10 +432,11 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, ScriptableObject obj = ensureScriptableObject(arg); for (Object name: obj.getAllIds()) { - ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name); - if (Boolean.TRUE.equals(desc.get("configurable"))) { - desc.put("configurable", desc, Boolean.FALSE); - obj.defineOwnProperty(cx, name, desc, false); + String sname = ScriptRuntime.toString(name); + PropertyDescriptor desc = obj.getOwnProperty(sname); + if (desc != null && desc.isConfigurable()) { + desc.setConfigurable(false); + obj.defineOwnProperty(sname, desc, true); } } obj.preventExtensions(); @@ -443,12 +449,20 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, ScriptableObject obj = ensureScriptableObject(arg); for (Object name: obj.getAllIds()) { - ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name); - if (isDataDescriptor(desc) && Boolean.TRUE.equals(desc.get("writable"))) - desc.put("writable", desc, Boolean.FALSE); - if (Boolean.TRUE.equals(desc.get("configurable"))) - desc.put("configurable", desc, Boolean.FALSE); - obj.defineOwnProperty(cx, name, desc, false); + String sname = ScriptRuntime.toString(name); + PropertyDescriptor desc = obj.getOwnProperty(sname); + if (desc == null) { + continue; + } else if (desc.isDataDescriptor() && desc.isWritable()) { + desc.setWritable(false); + if (desc.isConfigurable()) { + desc.setConfigurable(false); + } + obj.defineOwnProperty(sname, desc, true); + } else if (desc.isConfigurable()) { + desc.setConfigurable(false); + obj.defineOwnProperty(sname, desc, true); + } } obj.preventExtensions(); @@ -461,6 +475,26 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } } + private static void js_defineProperties(Context cx, Scriptable scope, + ScriptableObject obj, Object propsObj) { + ScriptableObject props = ensureScriptableObject(ScriptRuntime.toObject( + cx, scope, propsObj)); + Object[] ids = props.getIds(); + Object[] descs = new Object[ids.length * 2]; + for (int i = 0, len = ids.length; i < len; ++i) { + Object name = ids[i]; + descs[i << 1] = ScriptRuntime.toString(name); + Object descObj = props.get(name); + descs[(i << 1) + 1] = PropertyDescriptor + .toPropertyDescriptor(descObj); + } + for (int i = 0, len = ids.length; i < len; ++i) { + String name = (String) descs[i << 1]; + PropertyDescriptor desc = (PropertyDescriptor) descs[(i << 1) + 1]; + obj.defineOwnProperty(name, desc, true); + } + } + // methods implementing java.util.Map public boolean containsKey(Object key) { From 7d1140ca5a38b2d9739f6f5a12d89570c3b390b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 014/111] RegExp.prototype.lastIndex can change 'writable' attribute --- .../mozilla/javascript/regexp/NativeRegExp.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/regexp/NativeRegExp.java b/src/org/mozilla/javascript/regexp/NativeRegExp.java index 5da03d57ad..923369ede9 100644 --- a/src/org/mozilla/javascript/regexp/NativeRegExp.java +++ b/src/org/mozilla/javascript/regexp/NativeRegExp.java @@ -2461,7 +2461,7 @@ else if (s_length==9) { int attr; switch (id) { case Id_lastIndex: - attr = PERMANENT | DONTENUM; + attr = lastIndexAttr; break; case Id_source: case Id_global: @@ -2522,6 +2522,17 @@ protected void setInstanceIdValue(int id, Object value) super.setInstanceIdValue(id, value); } + @Override + protected void setInstanceIdAttributes(int id, int attr) + { + switch (id) { + case Id_lastIndex: + lastIndexAttr = attr; + return; + } + super.setInstanceIdAttributes(id, attr); + } + @Override protected void initPrototypeId(int id) { @@ -2616,6 +2627,7 @@ protected int findPrototypeId(String s) private RECompiled re; Object lastIndex = 0d; /* index after last match, for //g iterator */ + private int lastIndexAttr = DONTENUM | PERMANENT; } // class NativeRegExp class RECompiled implements Serializable From d0e907c570069abbe41d4b906a9a2c6ef9345107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 015/111] Fixed braino when checking descriptor's present attributes; Account for accessor shenanigans when creating property descriptor --- .../javascript/PropertyDescriptor.java | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/org/mozilla/javascript/PropertyDescriptor.java b/src/org/mozilla/javascript/PropertyDescriptor.java index c177679ee6..3fde5406e8 100755 --- a/src/org/mozilla/javascript/PropertyDescriptor.java +++ b/src/org/mozilla/javascript/PropertyDescriptor.java @@ -43,6 +43,7 @@ import static org.mozilla.javascript.ScriptableObject.PERMANENT; import static org.mozilla.javascript.ScriptableObject.READONLY; import static org.mozilla.javascript.ScriptableObject.ensureScriptableObject; +import static org.mozilla.javascript.ScriptableObject.getProperty; import java.util.Map; @@ -116,7 +117,7 @@ static Object fromPropertyDescriptor(Context cx, Scriptable scope, // FromPropertyDescriptor expects a fully populated descriptor int present = desc.getPresent(); if ((present & ~POPULATED_ACCESSOR_DESC) != 0 - || (present & ~POPULATED_DATA_DESC) != 0) { + && (present & ~POPULATED_DATA_DESC) != 0) { throw new IllegalArgumentException(String.valueOf(present)); } @@ -175,31 +176,37 @@ static PropertyDescriptor toPropertyDescriptor(Map map) { static PropertyDescriptor toPropertyDescriptor(Object object) { ScriptableObject obj = ensureScriptableObject(object); PropertyDescriptor desc = new PropertyDescriptor(); - Object enumerable = ScriptableObject.getProperty(obj, "enumerable"); - if (enumerable != NOT_FOUND) { + if (ScriptableObject.hasProperty(obj, "enumerable")) { + Object enumerable = getProperty(obj, "enumerable"); + if (enumerable == NOT_FOUND) enumerable = Boolean.FALSE; desc.setEnumerable(ScriptRuntime.toBoolean(enumerable)); } - Object configurable = ScriptableObject.getProperty(obj, "configurable"); - if (configurable != NOT_FOUND) { + if (ScriptableObject.hasProperty(obj, "configurable")) { + Object configurable = getProperty(obj, "configurable"); + if (configurable == NOT_FOUND) configurable = Boolean.FALSE; desc.setConfigurable(ScriptRuntime.toBoolean(configurable)); } - Object value = ScriptableObject.getProperty(obj, "value"); - if (value != NOT_FOUND) { + if (ScriptableObject.hasProperty(obj, "value")) { + Object value = ScriptableObject.getProperty(obj, "value"); + if (value == NOT_FOUND) value = Undefined.instance; desc.setValue(value); } - Object writable = ScriptableObject.getProperty(obj, "writable"); - if (writable != NOT_FOUND) { + if (ScriptableObject.hasProperty(obj, "writable")) { + Object writable = getProperty(obj, "writable"); + if (writable == NOT_FOUND) writable = Boolean.FALSE; desc.setWritable(ScriptRuntime.toBoolean(writable)); } - Object getter = ScriptableObject.getProperty(obj, "get"); - if (getter != NOT_FOUND) { + if (ScriptableObject.hasProperty(obj, "get")) { + Object getter = ScriptableObject.getProperty(obj, "get"); + if (getter == NOT_FOUND) getter = Undefined.instance; if (getter != Undefined.instance && !(getter instanceof Callable)) { throw ScriptRuntime.notFunctionError(getter); } desc.setGetter(getter); } - Object setter = ScriptableObject.getProperty(obj, "set"); - if (setter != NOT_FOUND) { + if (ScriptableObject.hasProperty(obj, "set")) { + Object setter = ScriptableObject.getProperty(obj, "set"); + if (setter == NOT_FOUND) setter = Undefined.instance; if (setter != Undefined.instance && !(setter instanceof Callable)) { throw ScriptRuntime.notFunctionError(setter); } From 2292aeafab531a4414353ce51790687a043ddfe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 016/111] Use different method to plug values from descriptor --- src/org/mozilla/javascript/NativeObject.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index 5f0d007dbd..23709f158a 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -484,9 +484,8 @@ private static void js_defineProperties(Context cx, Scriptable scope, for (int i = 0, len = ids.length; i < len; ++i) { Object name = ids[i]; descs[i << 1] = ScriptRuntime.toString(name); - Object descObj = props.get(name); - descs[(i << 1) + 1] = PropertyDescriptor - .toPropertyDescriptor(descObj); + Object descObj = ScriptRuntime.getObjectElem(props, name, cx); + descs[(i << 1) + 1] = PropertyDescriptor.toPropertyDescriptor(descObj); } for (int i = 0, len = ids.length; i < len; ++i) { String name = (String) descs[i << 1]; From e2c96b446c6a9909047f3e559d0b7cd407310f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 017/111] Ensure 'value' for getter-slot is 'undefined'; Make 'isNew' flag only dependent on 'current' instead of slot --- src/org/mozilla/javascript/ScriptableObject.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index e38dc40b51..a2c57822c7 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -1812,6 +1812,7 @@ public void defineProperty(String propertyName, Object delegateTo, * @param cx the current Context * @param props a map of property ids to property descriptors */ + @Deprecated public void defineOwnProperties(Context cx, ScriptableObject props) { Object[] ids = props.getIds(); for (Object id : ids) { @@ -1832,6 +1833,7 @@ public void defineOwnProperties(Context cx, ScriptableObject props) { * @param id the name/index of the property * @param desc the new property descriptor, as described in 8.6.1 */ + @Deprecated public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { checkPropertyDefinition(desc); defineOwnProperty(cx, id, desc, true); @@ -1847,6 +1849,7 @@ public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { * @param desc the new property descriptor, as described in 8.6.1 * @param checkValid whether to perform validity checks */ + @Deprecated protected void defineOwnProperty(Context cx, Object id, ScriptableObject desc, boolean checkValid) { String name = ScriptRuntime.toString(id); @@ -3403,8 +3406,9 @@ protected void updateOwnProperty(String name, PropertyDescriptor desc, } Slot slot = getSlot(name, (int) index, SLOT_QUERY); boolean isAccessor = desc.isAccessorDescriptor(); - boolean isNew = slot == null; + boolean isNew = current == null; if (slot == null) { + // caution: slot may be null even if current != null slot = getSlot(name, (int) index, isAccessor ? SLOT_MODIFY_GETTER_SETTER : SLOT_MODIFY); } @@ -3429,7 +3433,7 @@ protected void updateOwnProperty(String name, PropertyDescriptor desc, slot = getSlot(name, (int) index, SLOT_MODIFY_GETTER_SETTER); } GetterSlot gslot = (GetterSlot) slot; - gslot.value = null; + gslot.value = Undefined.instance; if (hasGetter) { gslot.getter = desc.getGetter(); } From 4b1f19cc5f133c0dc2cab6f00a5e26c55bed7978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 018/111] Handle 'arguments' property on BaseFunction more correctly --- src/org/mozilla/javascript/BaseFunction.java | 25 ++++++++++++++++--- src/org/mozilla/javascript/BoundFunction.java | 3 ++- .../javascript/IdScriptableObject.java | 6 +++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/org/mozilla/javascript/BaseFunction.java b/src/org/mozilla/javascript/BaseFunction.java index 2c33208d55..bfb2f044fb 100644 --- a/src/org/mozilla/javascript/BaseFunction.java +++ b/src/org/mozilla/javascript/BaseFunction.java @@ -166,7 +166,7 @@ protected int findInstanceIdInfo(String s) attr = prototypePropertyAttributes; break; case Id_arguments: - attr = DONTENUM | PERMANENT; + attr = argumentsAttributes; break; default: throw new IllegalStateException(); } @@ -214,7 +214,11 @@ protected void setInstanceIdValue(int id, Object value) // This should not be called since "arguments" is PERMANENT Kit.codeBug(); } - defaultPut("arguments", value); + if (defaultHas("arguments")) { + defaultPut("arguments", value); + } else if ((argumentsAttributes & READONLY) == 0) { + argumentsObj = value; + } return; case Id_name: case Id_arity: @@ -224,6 +228,19 @@ protected void setInstanceIdValue(int id, Object value) super.setInstanceIdValue(id, value); } + @Override + protected void setInstanceIdAttributes(int id, int attr) { + switch (id) { + case Id_prototype: + prototypePropertyAttributes = attr; + return; + case Id_arguments: + argumentsAttributes = attr; + return; + } + super.setInstanceIdAttributes(id, attr); + } + @Override protected void fillConstructorProperties(IdFunctionObject ctor) { @@ -508,7 +525,7 @@ private Object getArguments() // .arguments is deprecated, so we use a slow // way of getting it that doesn't add to the invocation cost. // TODO: add warning, error based on version - Object value = defaultGet("arguments"); + Object value = defaultHas("arguments") ? defaultGet("arguments") : argumentsObj; if (value != NOT_FOUND) { // Should after changing .arguments its // activation still be available during Function call? @@ -628,5 +645,7 @@ protected int findPrototypeId(String s) // {configurable:false, enumerable:false}; // see ECMA 15.3.5.2 private int prototypePropertyAttributes = PERMANENT|DONTENUM; + private int argumentsAttributes = PERMANENT|DONTENUM; + private Object argumentsObj = NOT_FOUND; } diff --git a/src/org/mozilla/javascript/BoundFunction.java b/src/org/mozilla/javascript/BoundFunction.java index 3f614bf348..195f50ed51 100644 --- a/src/org/mozilla/javascript/BoundFunction.java +++ b/src/org/mozilla/javascript/BoundFunction.java @@ -70,7 +70,8 @@ public BoundFunction(Context cx, Scriptable scope, Callable targetFunction, Scri Function thrower = ScriptRuntime.typeErrorThrower(); PropertyDescriptor throwing = new PropertyDescriptor(thrower, thrower, DONTENUM | PERMANENT); this.defineOwnProperty("caller", throwing, false); - this.defineOwnProperty("arguments", throwing, false); + // this.defineOwnProperty("arguments", throwing, false); + this.updateOwnProperty("arguments", throwing, null); } @Override diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index 52c2ab04d5..36ba718f98 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -322,6 +322,11 @@ public IdScriptableObject(Scriptable scope, Scriptable prototype) super(scope, prototype); } + protected final boolean defaultHas(String name) + { + return super.has(name, this); + } + protected final Object defaultGet(String name) { return super.get(name, this); @@ -732,6 +737,7 @@ private IdFunctionObject newIdFunction(Object tag, int id, String name, } @Override + @Deprecated public void defineOwnProperty(Context cx, Object key, ScriptableObject desc) { if (key instanceof String) { String name = (String) key; From 17d094540235cedddc43f359f863db4f433a03f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 019/111] Remove unused feature --- .../javascript/PropertyDescriptor.java | 45 ------------------- 1 file changed, 45 deletions(-) diff --git a/src/org/mozilla/javascript/PropertyDescriptor.java b/src/org/mozilla/javascript/PropertyDescriptor.java index 3fde5406e8..57376d6219 100755 --- a/src/org/mozilla/javascript/PropertyDescriptor.java +++ b/src/org/mozilla/javascript/PropertyDescriptor.java @@ -45,17 +45,11 @@ import static org.mozilla.javascript.ScriptableObject.ensureScriptableObject; import static org.mozilla.javascript.ScriptableObject.getProperty; -import java.util.Map; - /** * * */ class PropertyDescriptor { - public static enum Field { - Value, Get, Set, Writable, Enumerable, Configurable; - } - private static final int VALUE = 0x01; private static final int GET = 0x02; private static final int SET = 0x04; @@ -134,45 +128,6 @@ static Object fromPropertyDescriptor(Context cx, Scriptable scope, return obj; } - static PropertyDescriptor toPropertyDescriptor(Map map) { - PropertyDescriptor desc = new PropertyDescriptor(); - for (Map.Entry entry : map.entrySet()) { - Field field = entry.getKey(); - Object value = entry.getValue(); - switch (field) { - case Writable: - desc.setWritable(ScriptRuntime.toBoolean(value)); - break; - case Enumerable: - desc.setEnumerable(ScriptRuntime.toBoolean(value)); - break; - case Configurable: - desc.setConfigurable(ScriptRuntime.toBoolean(value)); - break; - case Value: - desc.setValue(value); - break; - case Get: - if (value != Undefined.instance && !(value instanceof Callable)) { - throw ScriptRuntime.notFunctionError(value); - } - desc.setGetter(value); - break; - case Set: - if (value != Undefined.instance && !(value instanceof Callable)) { - throw ScriptRuntime.notFunctionError(value); - } - desc.setSetter(value); - break; - } - } - if ((desc.present & (GET | SET)) != 0 - && (desc.present & (VALUE | WRITABLE)) != 0) { - throw ScriptRuntime.typeError0("msg.both.data.and.accessor.desc"); - } - return desc; - } - static PropertyDescriptor toPropertyDescriptor(Object object) { ScriptableObject obj = ensureScriptableObject(object); PropertyDescriptor desc = new PropertyDescriptor(); From d17fa3485f9f34b13bd2b6155275868fcdf43514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 020/111] Add specialized PropertyDescriptor for slots --- .../mozilla/javascript/ScriptableObject.java | 61 ++++++++++++------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index a2c57822c7..5fc11275f7 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -3113,6 +3113,22 @@ private static boolean isPrimitive(Object value) { || value instanceof CharSequence || value instanceof Number || value instanceof Boolean); } + private static class SlotPropertyDescriptor extends PropertyDescriptor { + private final Slot slot; + + SlotPropertyDescriptor(Slot slot) { + super(slot.value, slot.attributes); + this.slot = slot; + } + + SlotPropertyDescriptor(GetterSlot slot) { + super(slot.getter == null ? Undefined.instance : slot.getter, + slot.setter == null ? Undefined.instance : slot.setter, + ((Slot) slot).attributes); + this.slot = slot; + } + } + /** * ECMAScript 5 * @@ -3133,18 +3149,9 @@ protected PropertyDescriptor getOwnProperty(String name) { slot = unwrapSlot(slot); PropertyDescriptor desc; if (slot instanceof GetterSlot) { - GetterSlot gslot = (GetterSlot) slot; - Object getter = gslot.getter; - Object setter = gslot.setter; - if (getter == null) { - getter = Undefined.instance; - } - if (setter == null) { - setter = Undefined.instance; - } - desc = new PropertyDescriptor(getter, setter, slot.attributes); + desc = new SlotPropertyDescriptor((GetterSlot) slot); } else { - desc = new PropertyDescriptor(slot.value, slot.attributes); + desc = new SlotPropertyDescriptor(slot); } return desc; } @@ -3398,21 +3405,29 @@ protected boolean defineOwnProperty(String name, PropertyDescriptor desc, protected void updateOwnProperty(String name, PropertyDescriptor desc, PropertyDescriptor current) { - long index = ScriptRuntime.indexFromString(name); - if (index >= 0) { - name = null; - } else { - index = 0; - } - Slot slot = getSlot(name, (int) index, SLOT_QUERY); boolean isAccessor = desc.isAccessorDescriptor(); boolean isNew = current == null; - if (slot == null) { - // caution: slot may be null even if current != null - slot = getSlot(name, (int) index, - isAccessor ? SLOT_MODIFY_GETTER_SETTER : SLOT_MODIFY); + long index; + Slot slot; + if (desc instanceof SlotPropertyDescriptor) { + slot = ((SlotPropertyDescriptor) desc).slot; + name = slot.name; + index = slot.indexOrHash; + } else { + index = ScriptRuntime.indexFromString(name); + if (index >= 0) { + name = null; + } else { + index = 0; + } + slot = getSlot(name, (int) index, SLOT_QUERY); + if (slot == null) { + // caution: slot may be null even if current != null + slot = getSlot(name, (int) index, + isAccessor ? SLOT_MODIFY_GETTER_SETTER : SLOT_MODIFY); + } + slot = unwrapSlot(slot); } - slot = unwrapSlot(slot); int baseAttrs, mask; if (isNew) { From fce0efda1638a226ee2eaf56a90e8ed354d3ca9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 021/111] remove previous ES5 property descriptor implementation --- src/org/mozilla/javascript/Arguments.java | 69 ------ .../javascript/IdScriptableObject.java | 89 -------- src/org/mozilla/javascript/NativeArray.java | 157 ------------- src/org/mozilla/javascript/NativeString.java | 66 ------ .../mozilla/javascript/ScriptableObject.java | 216 ------------------ 5 files changed, 597 deletions(-) diff --git a/src/org/mozilla/javascript/Arguments.java b/src/org/mozilla/javascript/Arguments.java index ddfef3e6d4..10ba243e51 100644 --- a/src/org/mozilla/javascript/Arguments.java +++ b/src/org/mozilla/javascript/Arguments.java @@ -353,75 +353,6 @@ Object[] getIds(boolean getAll) return ids; } - @Override - protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { - double d = ScriptRuntime.toNumber(id); - int index = (int) d; - if (d != index) { - return super.getOwnPropertyDescriptor(cx, id); - } - Object value = arg(index); - if (value == NOT_FOUND) { - return super.getOwnPropertyDescriptor(cx, id); - } - if (sharedWithActivation(index)) { - value = getFromActivation(index); - } - if (super.has(index, this)) { // the descriptor has been redefined - ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id); - desc.put("value", desc, value); - return desc; - } else { - Scriptable scope = getParentScope(); - if (scope == null) scope = this; - return buildDataDescriptor(scope, value, EMPTY); - } - } - - @Override - protected void defineOwnProperty(Context cx, Object id, - ScriptableObject desc, - boolean checkValid) { - double d = ScriptRuntime.toNumber(id); - int index = (int) d; - if (d == index) { - Object value = arg(index); - if (value != NOT_FOUND && !super.has(index, this)) { - // set-up default descriptor - if (sharedWithActivation(index)) { - value = getFromActivation(index); - } - Scriptable scope = getParentScope(); - if (scope == null) scope = this; - // descriptor with {configurable: true, enumerable: true, writable: true} - ScriptableObject def = buildDataDescriptor(scope, value, EMPTY); - super.defineOwnProperty(cx, id, def, checkValid); - } - } - - super.defineOwnProperty(cx, id, desc, checkValid); - - if (d != index) return; - - Object value = arg(index); - if (value == NOT_FOUND) return; - - if (isAccessorDescriptor(desc)) { - removeArg(index); - return; - } - - Object newValue = getProperty(desc, "value"); - if (newValue != NOT_FOUND) { - replaceArg(index, newValue); - } - - Object writable = getProperty(desc, "writable"); - if (!(writable == NOT_FOUND || ScriptRuntime.toBoolean(writable))) { - removeArg(index); - } - } - @Override protected Object get(String name) { double d = ScriptRuntime.toNumber(name); diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index 36ba718f98..c2a2277070 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -736,95 +736,6 @@ private IdFunctionObject newIdFunction(Object tag, int id, String name, return f; } - @Override - @Deprecated - public void defineOwnProperty(Context cx, Object key, ScriptableObject desc) { - if (key instanceof String) { - String name = (String) key; - int info = findInstanceIdInfo(name); - if (info != 0) { - int id = (info & 0xFFFF); - if (isAccessorDescriptor(desc)) { - delete(id); // it will be replaced with a slot - } else { - checkPropertyDefinition(desc); - ScriptableObject current = getOwnPropertyDescriptor(cx, key); - checkPropertyChange(name, current, desc); - int attr = (info >>> 16); - Object value = getProperty(desc, "value"); - if (value != NOT_FOUND && (attr & READONLY) == 0) { - Object currentValue = getInstanceIdValue(id); - if (!sameValue(value, currentValue)) { - setInstanceIdValue(id, value); - } - } - setAttributes(name, applyDescriptorToAttributeBitset(attr, desc)); - return; - } - } - if (prototypeValues != null) { - int id = prototypeValues.findId(name); - if (id != 0) { - if (isAccessorDescriptor(desc)) { - prototypeValues.delete(id); // it will be replaced with a slot - } else { - checkPropertyDefinition(desc); - ScriptableObject current = getOwnPropertyDescriptor(cx, key); - checkPropertyChange(name, current, desc); - int attr = prototypeValues.getAttributes(id); - Object value = getProperty(desc, "value"); - if (value != NOT_FOUND && (attr & READONLY) == 0) { - Object currentValue = prototypeValues.get(id); - if (!sameValue(value, currentValue)) { - prototypeValues.set(id, this, value); - } - } - prototypeValues.setAttributes(id, applyDescriptorToAttributeBitset(attr, desc)); - return; - } - } - } - } - super.defineOwnProperty(cx, key, desc); - } - - - @Override - protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { - ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id); - if (desc == null && id instanceof String) { - desc = getBuiltInDescriptor((String) id); - } - return desc; - } - - private ScriptableObject getBuiltInDescriptor(String name) { - Object value = null; - int attr = EMPTY; - - Scriptable scope = getParentScope(); - if (scope == null) { - scope = this; - } - - int info = findInstanceIdInfo(name); - if (info != 0) { - int id = (info & 0xFFFF); - value = getInstanceIdValue(id); - attr = (info >>> 16); - return buildDataDescriptor(scope, value, attr); - } - if (prototypeValues != null) { - int id = prototypeValues.findId(name); - if (id != 0) { - value = prototypeValues.get(id); - attr = prototypeValues.getAttributes(id); - return buildDataDescriptor(scope, value, attr); - } - } - return null; - } - private static class IdPropertyDescriptor extends PropertyDescriptor { final int id; final boolean proto; diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index f917b145de..48e0db1cb5 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -42,13 +42,11 @@ import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.ListIterator; -import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; @@ -557,18 +555,6 @@ public Object getDefaultValue(Class hint) return super.getDefaultValue(hint); } - private ScriptableObject defaultIndexPropertyDescriptor(Object value) { - Scriptable scope = getParentScope(); - if (scope == null) scope = this; - ScriptableObject desc = new NativeObject(); - ScriptRuntime.setBuiltinProtoAndParent(desc, scope, TopLevel.Builtins.Object); - desc.defineProperty("value", value, EMPTY); - desc.defineProperty("writable", true, EMPTY); - desc.defineProperty("enumerable", true, EMPTY); - desc.defineProperty("configurable", true, EMPTY); - return desc; - } - @Override public int getAttributes(int index) { if (dense != null && index >= 0 && index < dense.length @@ -578,149 +564,6 @@ public int getAttributes(int index) { return super.getAttributes(index); } - @Override - protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { - if (dense != null) { - int index = toDenseIndex(id); - if (0 <= index && index < dense.length && dense[index] != NOT_FOUND) { - Object value = dense[index]; - return defaultIndexPropertyDescriptor(value); - } - } - return super.getOwnPropertyDescriptor(cx, id); - } - - /** - * 15.4.5.1 [[DefineOwnProperty]] for Array 'length' - */ - private void defineOwnPropertyLength(ScriptableObject desc, - boolean checkValid) { - checkPropertyDefinition(desc); - Object value = getProperty(desc, "value"); - Object configurable = getProperty(desc, "configurable"); - Object enumerable = getProperty(desc, "enumerable"); - Object writable = getProperty(desc, "writable"); - - // 8.12.9 [[DefineOwnProperty]] checks for non-configurable properties, cf. - // 15.4.5.2 'length' has attributes {writable: true, enumerable: false, configurable: false} - if (isTrue(configurable)) { - if (! checkValid) return; - throw ScriptRuntime.typeError1( - "msg.change.configurable.false.to.true", "length"); - } else if (isTrue(enumerable)) { - if (! checkValid) return; - throw ScriptRuntime.typeError1( - "msg.change.enumerable.with.configurable.false", "length"); - } else if (isAccessorDescriptor(desc)) { - if (! checkValid) return; - throw ScriptRuntime.typeError1( - "msg.change.property.data.to.accessor.with.configurable.false", "length"); - } - - long newLen, oldLen = length; - if (value == NOT_FOUND) { - // generic-descriptor or data-descriptor with 'writable' - newLen = oldLen; - } else { - // data-descriptor with 'value' and possibly 'writable' - newLen = ScriptRuntime.toUint32(value); - if (newLen != ScriptRuntime.toNumber(value)) { - String msg = ScriptRuntime.getMessage0("msg.arraylength.bad"); - throw ScriptRuntime.constructError("RangeError", msg); - } - } - - if (newLen == oldLen) { - if (writable != NOT_FOUND) { - boolean w = ScriptRuntime.toBoolean(writable); - if ((lengthAttr & READONLY) != 0 && w) { - if (! checkValid) return; - throw ScriptRuntime.typeError1( - "msg.change.writable.false.to.true.with.configurable.false", "length"); - } - lengthAttr = w - ? lengthAttr & ~READONLY - : lengthAttr | READONLY; - } - } else { - if ((lengthAttr & READONLY) != 0) { - if (! checkValid) return; - throw ScriptRuntime.typeError1( - "msg.change.value.with.writable.false", "length"); - } - long entry = updateEntries(oldLen, newLen); - if (entry != -1) { - newLen = entry + 1; - } - length = newLen; - if (!(writable == NOT_FOUND || ScriptRuntime.toBoolean(writable))) { - lengthAttr = lengthAttr | READONLY; - } - if (entry != -1 && checkValid) { - throw ScriptRuntime.typeError("'missing error description'"); - } - } - } - - /** - * 15.4.5.1 [[DefineOwnProperty]] for Array 'index' - */ - private void defineOwnPropertyIndex(Context cx, Object id, - ScriptableObject desc, boolean checkValid, long index) { - // 15.4.5.1 - step 4 => property is array index - long oldLen = length; - if (index >= oldLen) { - if ((lengthAttr & READONLY) != 0) { - if (!checkValid) return; - throw ScriptRuntime.typeError1( - "msg.change.value.with.writable.false", "length"); - } - } - if (dense != null) { - Object[] values = dense; - dense = null; - denseOnly = false; - for (int i = 0; i < values.length; i++) { - if (values[i] != NOT_FOUND) { - put(i, this, values[i]); - } - } - } - if (index >= oldLen) { - length = index + 1; - } - checkPropertyDefinition(desc); - super.defineOwnProperty(cx, id, desc, checkValid); - } - - @Override - public void defineOwnProperty(Context cx, Object key, ScriptableObject desc) { - if ("length".equals(ScriptRuntime.toString(key))) { - // intercept 'length' property - defineOwnPropertyLength(desc, true); - return; - } - super.defineOwnProperty(cx, key, desc); - } - - @Override - protected void defineOwnProperty(Context cx, Object id, - ScriptableObject desc, - boolean checkValid) { - if ("length".equals(ScriptRuntime.toString(id))) { - // intercept 'length' property - defineOwnPropertyLength(desc, checkValid); - return; - } - long index = toArrayIndex(id); - if (index >= 0) { - // intercept index properties - defineOwnPropertyIndex(cx, id, desc, checkValid, index); - return; - } - super.defineOwnProperty(cx, id, desc, checkValid); - } - @Override protected PropertyDescriptor getOwnProperty(String name) { if (dense != null) { diff --git a/src/org/mozilla/javascript/NativeString.java b/src/org/mozilla/javascript/NativeString.java index 0f5843a636..8949026167 100644 --- a/src/org/mozilla/javascript/NativeString.java +++ b/src/org/mozilla/javascript/NativeString.java @@ -572,72 +572,6 @@ private static int toStringIndex(Object id) { return (int) index; } - @Override - protected void defineOwnProperty(Context cx, Object id, - ScriptableObject desc, boolean checkValid) { - int index = toStringIndex(id); - if (0 <= index && index < string.length()) { - checkPropertyDefinition(desc); - String name = ScriptRuntime.toString(id); - Object value = getProperty(desc, "value"); - Object configurable = getProperty(desc, "configurable"); - Object enumerable = getProperty(desc, "enumerable"); - Object writable = getProperty(desc, "writable"); - - if (isTrue(configurable)) { - if (! checkValid) return; - throw ScriptRuntime.typeError1( - "msg.change.configurable.false.to.true", name); - } else if (!(enumerable == NOT_FOUND || ScriptRuntime.toBoolean(enumerable))) { - if (! checkValid) return; - throw ScriptRuntime.typeError1( - "msg.change.enumerable.with.configurable.false", name); - } else if (isTrue(writable)) { - if (! checkValid) return; - throw ScriptRuntime.typeError1( - "msg.change.writable.false.to.true.with.configurable.false", name); - } else if (isAccessorDescriptor(desc)) { - if (! checkValid) return; - throw ScriptRuntime.typeError1( - "msg.change.property.data.to.accessor.with.configurable.false", name); - } else if (!(value == NOT_FOUND || sameValue(value, String.valueOf(string.charAt(index))))) { - if (! checkValid) return; - throw ScriptRuntime.typeError1( - "msg.change.value.with.writable.false", name); - } - return; - } - super.defineOwnProperty(cx, id, desc, checkValid); - } - - /* - * ES5.1: 15.5.5.2 [[GetOwnProperty]] for the String object - */ - @Override - protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { - int index = toStringIndex(id); - if (0 <= index && index < string.length()) { - String value = String.valueOf(string.charAt(index)); - return defaultIndexPropertyDescriptor(value); - } - return super.getOwnPropertyDescriptor(cx, id); - } - - private ScriptableObject defaultIndexPropertyDescriptor(Object value) { - Scriptable scope = getParentScope(); - if (scope == null) { - scope = this; - } - ScriptableObject desc = new NativeObject(); - ScriptRuntime.setBuiltinProtoAndParent(desc, scope, - TopLevel.Builtins.Object); - desc.defineProperty("value", value, EMPTY); - desc.defineProperty("writable", false, EMPTY); - desc.defineProperty("enumerable", true, EMPTY); - desc.defineProperty("configurable", false, EMPTY); - return desc; - } - @Override protected PropertyDescriptor getOwnProperty(String name) { int index = toStringIndex(name); diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 5fc11275f7..0373ba190d 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -230,23 +230,6 @@ void markDeleted() { value = null; name = null; } - - ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) { - return buildDataDescriptor(scope, value, attributes); - } - - } - - protected static ScriptableObject buildDataDescriptor(Scriptable scope, - Object value, - int attributes) { - ScriptableObject desc = new NativeObject(); - ScriptRuntime.setBuiltinProtoAndParent(desc, scope, TopLevel.Builtins.Object); - desc.defineProperty("value", value, EMPTY); - desc.defineProperty("writable", (attributes & READONLY) == 0, EMPTY); - desc.defineProperty("enumerable", (attributes & DONTENUM) == 0, EMPTY); - desc.defineProperty("configurable", (attributes & PERMANENT) == 0, EMPTY); - return desc; } private static final class GetterSlot extends Slot @@ -261,18 +244,6 @@ private static final class GetterSlot extends Slot super(name, indexOrHash, attributes); } - @Override - ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) { - int attr = getAttributes(); - ScriptableObject desc = new NativeObject(); - ScriptRuntime.setBuiltinProtoAndParent(desc, scope, TopLevel.Builtins.Object); - desc.defineProperty("enumerable", (attr & DONTENUM) == 0, EMPTY); - desc.defineProperty("configurable", (attr & PERMANENT) == 0, EMPTY); - if (getter != null) desc.defineProperty("get", getter, EMPTY); - if (setter != null) desc.defineProperty("set", setter, EMPTY); - return desc; - } - @Override boolean setValue(Object value, Scriptable owner, Scriptable start) { if (setter == null) { @@ -386,11 +357,6 @@ Object getValue(Scriptable start) { return slot.getValue(start); } - @Override - ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) { - return slot.getPropertyDescriptor(cx, scope); - } - @Override int getAttributes() { return slot.getAttributes(); @@ -1806,130 +1772,6 @@ public void defineProperty(String propertyName, Object delegateTo, gslot.setter = setterBox; } - /** - * Defines one or more properties on this object. - * - * @param cx the current Context - * @param props a map of property ids to property descriptors - */ - @Deprecated - public void defineOwnProperties(Context cx, ScriptableObject props) { - Object[] ids = props.getIds(); - for (Object id : ids) { - Object descObj = props.get(id); - ScriptableObject desc = ensureScriptableObject(descObj); - checkPropertyDefinition(desc); - } - for (Object id : ids) { - ScriptableObject desc = (ScriptableObject)props.get(id); - defineOwnProperty(cx, id, desc); - } - } - - /** - * Defines a property on an object. - * - * @param cx the current Context - * @param id the name/index of the property - * @param desc the new property descriptor, as described in 8.6.1 - */ - @Deprecated - public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { - checkPropertyDefinition(desc); - defineOwnProperty(cx, id, desc, true); - } - - /** - * Defines a property on an object. - * - * Based on [[DefineOwnProperty]] from 8.12.10 of the spec. - * - * @param cx the current Context - * @param id the name/index of the property - * @param desc the new property descriptor, as described in 8.6.1 - * @param checkValid whether to perform validity checks - */ - @Deprecated - protected void defineOwnProperty(Context cx, Object id, ScriptableObject desc, - boolean checkValid) { - String name = ScriptRuntime.toString(id); - PropertyDescriptor d = PropertyDescriptor.toPropertyDescriptor(desc); - defineOwnProperty(name, d, checkValid); - } - - protected void checkPropertyDefinition(ScriptableObject desc) { - Object getter = getProperty(desc, "get"); - if (getter != NOT_FOUND && getter != Undefined.instance - && !(getter instanceof Callable)) { - throw ScriptRuntime.notFunctionError(getter); - } - Object setter = getProperty(desc, "set"); - if (setter != NOT_FOUND && setter != Undefined.instance - && !(setter instanceof Callable)) { - throw ScriptRuntime.notFunctionError(setter); - } - if (isDataDescriptor(desc) && isAccessorDescriptor(desc)) { - throw ScriptRuntime.typeError0("msg.both.data.and.accessor.desc"); - } - } - - protected void checkPropertyChange(String id, ScriptableObject current, - ScriptableObject desc) { - if (current == null) { // new property - if (!isExtensible()) throw ScriptRuntime.typeError0("msg.not.extensible"); - } else { - if (isFalse(current.get("configurable", current))) { - if (isTrue(getProperty(desc, "configurable"))) - throw ScriptRuntime.typeError1( - "msg.change.configurable.false.to.true", id); - if (hasProperty(desc, "enumerable")) { - // only reject if 'enumerable' is present in desc - if (isTrue(current.get("enumerable", current)) != isTrue(getProperty(desc, "enumerable"))) - throw ScriptRuntime.typeError1( - "msg.change.enumerable.with.configurable.false", id); - } - boolean isData = isDataDescriptor(desc); - boolean isAccessor = isAccessorDescriptor(desc); - if (!isData && !isAccessor) { - // no further validation required for generic descriptor - } else if (isData && isDataDescriptor(current)) { - if (isFalse(current.get("writable", current))) { - if (isTrue(getProperty(desc, "writable"))) - throw ScriptRuntime.typeError1( - "msg.change.writable.false.to.true.with.configurable.false", id); - - if (!sameValue(getProperty(desc, "value"), current.get("value", current))) - throw ScriptRuntime.typeError1( - "msg.change.value.with.writable.false", id); - } - } else if (isAccessor && isAccessorDescriptor(current)) { - if (!sameValue(getProperty(desc, "set"), current.get("set", current))) - throw ScriptRuntime.typeError1( - "msg.change.setter.with.configurable.false", id); - - if (!sameValue(getProperty(desc, "get"), current.get("get", current))) - throw ScriptRuntime.typeError1( - "msg.change.getter.with.configurable.false", id); - } else { - if (isDataDescriptor(current)) - throw ScriptRuntime.typeError1( - "msg.change.property.data.to.accessor.with.configurable.false", id); - else - throw ScriptRuntime.typeError1( - "msg.change.property.accessor.to.data.with.configurable.false", id); - } - } - } - } - - protected static boolean isTrue(Object value) { - return (value != NOT_FOUND) && ScriptRuntime.toBoolean(value); - } - - protected static boolean isFalse(Object value) { - return !isTrue(value); - } - /** * Implements SameValue as described in ES5 9.12, additionally checking * if new value is defined. @@ -1959,57 +1801,6 @@ protected boolean sameValue(Object newValue, Object currentValue) { return ScriptRuntime.shallowEq(currentValue, newValue); } - protected int applyDescriptorToAttributeBitset(int attributes, - ScriptableObject desc) - { - Object enumerable = getProperty(desc, "enumerable"); - if (enumerable != NOT_FOUND) { - attributes = ScriptRuntime.toBoolean(enumerable) - ? attributes & ~DONTENUM : attributes | DONTENUM; - } - - Object writable = getProperty(desc, "writable"); - if (writable != NOT_FOUND) { - attributes = ScriptRuntime.toBoolean(writable) - ? attributes & ~READONLY : attributes | READONLY; - } - - Object configurable = getProperty(desc, "configurable"); - if (configurable != NOT_FOUND) { - attributes = ScriptRuntime.toBoolean(configurable) - ? attributes & ~PERMANENT : attributes | PERMANENT; - } - - return attributes; - } - - /** - * Implements IsDataDescriptor as described in ES5 8.10.2 - * @param desc a property descriptor - * @return true if this is a data descriptor. - */ - protected boolean isDataDescriptor(ScriptableObject desc) { - return hasProperty(desc, "value") || hasProperty(desc, "writable"); - } - - /** - * Implements IsAccessorDescriptor as described in ES5 8.10.1 - * @param desc a property descriptor - * @return true if this is an accessor descriptor. - */ - protected boolean isAccessorDescriptor(ScriptableObject desc) { - return hasProperty(desc, "get") || hasProperty(desc, "set"); - } - - /** - * Implements IsGenericDescriptor as described in ES5 8.10.3 - * @param desc a property descriptor - * @return true if this is a generic descriptor. - */ - protected boolean isGenericDescriptor(ScriptableObject desc) { - return !isDataDescriptor(desc) && !isAccessorDescriptor(desc); - } - protected static Scriptable ensureScriptable(Object arg) { if ( !(arg instanceof Scriptable) ) throw ScriptRuntime.typeError1("msg.arg.not.object", ScriptRuntime.typeof(arg)); @@ -3082,13 +2873,6 @@ private void readObject(ObjectInputStream in) } } - protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { - Slot slot = getSlot(cx, id, SLOT_QUERY); - if (slot == null) return null; - Scriptable scope = getParentScope(); - return slot.getPropertyDescriptor(cx, (scope == null ? this : scope)); - } - protected Slot getSlot(Context cx, Object id, int accessType) { String name = ScriptRuntime.toStringIdOrIndex(cx, id); if (name == null) { From e8e881a31253749e1b1e2519c8cd2ee71b9dfd2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 022/111] Avoid ConsString allocation if one of the operands is empty --- src/org/mozilla/javascript/Interpreter.java | 6 ++--- src/org/mozilla/javascript/ScriptRuntime.java | 24 ++++++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index dfaa2e3694..98d3891091 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -3036,7 +3036,7 @@ private static void do_add(Object[] stack, double[] sDbl, int stackTop, } else if (lhs instanceof CharSequence || rhs instanceof CharSequence) { CharSequence lstr = ScriptRuntime.toCharSequence(lhs); CharSequence rstr = ScriptRuntime.toCharSequence(rhs); - stack[stackTop] = new ConsString(lstr, rstr); + stack[stackTop] = ScriptRuntime.add(lstr, rstr); } else { double lDbl = (lhs instanceof Number) ? ((Number)lhs).doubleValue() : ScriptRuntime.toNumber(lhs); @@ -3061,9 +3061,9 @@ private static void do_add(Object[] stack, double[] sDbl, int stackTop, CharSequence lstr = (CharSequence)lhs; CharSequence rstr = ScriptRuntime.toCharSequence(d); if (leftRightOrder) { - stack[stackTop] = new ConsString(lstr, rstr); + stack[stackTop] = ScriptRuntime.add(lstr, rstr); } else { - stack[stackTop] = new ConsString(rstr, lstr); + stack[stackTop] = ScriptRuntime.add(lstr, rstr); } } else { double lDbl = (lhs instanceof Number) diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index cf367415aa..9d33cda850 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -52,7 +52,9 @@ import java.io.Serializable; import java.lang.reflect.*; import java.text.MessageFormat; +import java.util.LinkedHashMap; import java.util.Locale; +import java.util.Map; import java.util.ResourceBundle; import org.mozilla.javascript.ast.FunctionNode; @@ -2645,15 +2647,31 @@ public static Object add(Object val1, Object val2, Context cx) ((Number)val2).doubleValue()); else return wrapNumber(toNumber(val1) + toNumber(val2)); - return new ConsString(toCharSequence(val1), toCharSequence(val2)); + return newConsString(toCharSequence(val1), toCharSequence(val2)); } public static CharSequence add(CharSequence val1, Object val2) { - return new ConsString(val1, toCharSequence(val2)); + return newConsString(val1, toCharSequence(val2)); } public static CharSequence add(Object val1, CharSequence val2) { - return new ConsString(toCharSequence(val1), val2); + return newConsString(toCharSequence(val1), val2); + } + + public static CharSequence add(CharSequence val1, CharSequence val2) { + return newConsString(val1, val2); + } + + private static CharSequence newConsString(CharSequence val1, CharSequence val2) { + int len1 = val1.length(); + if (len1 == 0) { + return val2; + } + int len2 = val2.length(); + if (len2 == 0) { + return val1; + } + return new ConsString(val1, val2); } /** From e9af62ad86d41137653aa05aef937113b8772b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 023/111] didn't pay attention to left-right order flag --- src/org/mozilla/javascript/Interpreter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index 98d3891091..f3c1b08dae 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -3063,7 +3063,7 @@ private static void do_add(Object[] stack, double[] sDbl, int stackTop, if (leftRightOrder) { stack[stackTop] = ScriptRuntime.add(lstr, rstr); } else { - stack[stackTop] = ScriptRuntime.add(lstr, rstr); + stack[stackTop] = ScriptRuntime.add(rstr, lstr); } } else { double lDbl = (lhs instanceof Number) From b262db404d023583e0ae3d77ffe2a6ab35252e2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 024/111] set[UTC]XXX() methods may not evaluate all arguments; fix for ch15/15.9/15.9.5/15.9.5.40/15.9.5.40_1 --- src/org/mozilla/javascript/NativeDate.java | 238 ++++++++++----------- 1 file changed, 111 insertions(+), 127 deletions(-) diff --git a/src/org/mozilla/javascript/NativeDate.java b/src/org/mozilla/javascript/NativeDate.java index d9e55b003b..506405d78c 100644 --- a/src/org/mozilla/javascript/NativeDate.java +++ b/src/org/mozilla/javascript/NativeDate.java @@ -1461,6 +1461,14 @@ private static void appendWeekDayName(StringBuilder sb, int index) private static double makeTime(double date, Object[] args, int methodId) { + if (args.length == 0) { + /* Seems like all the Date.setWhatever functions in ECMA are only + * varargs beyond the first argument; This means that + * "d = new Date(); d.setMilliseconds()" returns NaN. Blech. + */ + return ScriptRuntime.NaN; + } + int maxargs; boolean local = true; switch (methodId) { @@ -1468,108 +1476,91 @@ private static double makeTime(double date, Object[] args, int methodId) local = false; // fallthrough case Id_setMilliseconds: - maxargs = 1; + maxargs = 1; break; case Id_setUTCSeconds: local = false; // fallthrough case Id_setSeconds: - maxargs = 2; + maxargs = 2; break; case Id_setUTCMinutes: local = false; // fallthrough case Id_setMinutes: - maxargs = 3; + maxargs = 3; break; case Id_setUTCHours: local = false; // fallthrough case Id_setHours: - maxargs = 4; + maxargs = 4; break; default: - Kit.codeBug(); - maxargs = 0; + throw Kit.codeBug(); } - int i; - double conv[] = new double[4]; - double hour, min, sec, msec; - double lorutime; /* Local or UTC version of date */ - - double time; - double result; - - /* just return NaN if the date is already NaN */ - if (date != date) - return date; + // [0: hour, 1: minute, 2: second, 3: millisecond] + double[] conv = new double[4]; + for (int i = 0; i < args.length && i < maxargs; ++i) { + // derive conv-index from args-index + int k = i + 4 - maxargs; + conv[k] = ScriptRuntime.toNumber(args[i]); + } - /* Satisfy the ECMA rule that if a function is called with - * fewer arguments than the specified formal arguments, the - * remaining arguments are set to undefined. Seems like all - * the Date.setWhatever functions in ECMA are only varargs - * beyond the first argument; this should be set to undefined - * if it's not given. This means that "d = new Date(); - * d.setMilliseconds()" returns NaN. Blech. - */ - if (args.length == 0) - args = ScriptRuntime.padArguments(args, 1); - - for (i = 0; i < args.length && i < maxargs; i++) { - conv[i] = ScriptRuntime.toNumber(args[i]); - - // limit checks that happen in MakeTime in ECMA. - if (conv[i] != conv[i] || Double.isInfinite(conv[i])) { - return ScriptRuntime.NaN; + if (date != date) { + // if base date is invalid, return NaN; must happen after arguments + // have been evaluated + return ScriptRuntime.NaN; + } + double t = local ? LocalTime(date) : date; + + for (int i = 0; i < 4; ++i) { + // derive args-index from conv-index + int k = i - 4 + maxargs; + if (k < 0 || k >= args.length) { + // if argument not supplied, use default + if (i == 0) { + conv[0] = HourFromTime(t); + } else if (i == 1) { + conv[1] = MinFromTime(t); + } else if (i == 2) { + conv[2] = SecFromTime(t); + } else { + conv[3] = msFromTime(t); + } + } else { + // limit checks that happen in MakeTime in ECMA. + double x = conv[i]; + if (x != x || Double.isInfinite(x)) { + return ScriptRuntime.NaN; + } + conv[i] = ScriptRuntime.toInteger(x); } - conv[i] = ScriptRuntime.toInteger(conv[i]); } - if (local) - lorutime = LocalTime(date); - else - lorutime = date; - - i = 0; - int stop = args.length; - - if (maxargs >= 4 && i < stop) - hour = conv[i++]; - else - hour = HourFromTime(lorutime); - - if (maxargs >= 3 && i < stop) - min = conv[i++]; - else - min = MinFromTime(lorutime); - - if (maxargs >= 2 && i < stop) - sec = conv[i++]; - else - sec = SecFromTime(lorutime); - - if (maxargs >= 1 && i < stop) - msec = conv[i++]; - else - msec = msFromTime(lorutime); - - time = MakeTime(hour, min, sec, msec); - result = MakeDate(Day(lorutime), time); - - if (local) - result = internalUTC(result); - date = TimeClip(result); - - return date; + date = MakeDate(Day(t), MakeTime(conv[0], conv[1], conv[2], conv[3])); + if (local) { + return TimeClip(internalUTC(date)); + } else { + return TimeClip(date); + } } private static double makeDate(double date, Object[] args, int methodId) { + if (args.length == 0) { + /* Seems like all the Date.setWhatever functions in ECMA are only + * varargs beyond the first argument; This means that + * "d = new Date(); d.setMilliseconds()" returns NaN. Blech. + */ + return ScriptRuntime.NaN; + } + int maxargs; boolean local = true; switch (methodId) { @@ -1595,72 +1586,65 @@ private static double makeDate(double date, Object[] args, int methodId) break; default: - Kit.codeBug(); - maxargs = 0; + throw Kit.codeBug(); } - int i; - double conv[] = new double[3]; - double year, month, day; - double lorutime; /* local or UTC version of date */ - double result; - - /* See arg padding comment in makeTime.*/ - if (args.length == 0) - args = ScriptRuntime.padArguments(args, 1); + // [0: year, 1: month, 2: day] + double[] conv = new double[3]; + for (int i = 0; i < args.length && i < maxargs; ++i) { + // derive conv-index from args-index + int k = i + 3 - maxargs; + conv[k] = ScriptRuntime.toNumber(args[i]); + } - for (i = 0; i < args.length && i < maxargs; i++) { - conv[i] = ScriptRuntime.toNumber(args[i]); + double t; + switch (methodId) { + case Id_setUTCFullYear: + case Id_setFullYear: + if (date != date) { + // if base date is NaN, use 0 for set[UTC]Years() + t = 0; + break; + } + // fallthrough - // limit checks that happen in MakeDate in ECMA. - if (conv[i] != conv[i] || Double.isInfinite(conv[i])) { - return ScriptRuntime.NaN; - } - conv[i] = ScriptRuntime.toInteger(conv[i]); + default: + if (date != date) { + // if base date is invalid, return NaN; must happen after + // arguments have been evaluated + return ScriptRuntime.NaN; + } + t = local ? LocalTime(date) : date; } - /* return NaN if date is NaN and we're not setting the year, - * If we are, use 0 as the time. */ - if (date != date) { - if (args.length < 3) { - return ScriptRuntime.NaN; + for (int i = 0; i < 3; ++i) { + // derive args-index from conv-index + int k = i - 3 + maxargs; + if (k < 0 || k >= args.length) { + // if argument not supplied, use default + if (i == 0) { + conv[0] = YearFromTime(t); + } else if (i == 1) { + conv[1] = MonthFromTime(t); + } else { + conv[2] = DateFromTime(t); + } } else { - lorutime = 0; + // limit checks that happen in MakeDate in ECMA. + double x = conv[i]; + if (x != x || Double.isInfinite(x)) { + return ScriptRuntime.NaN; + } + conv[i] = ScriptRuntime.toInteger(x); } - } else { - if (local) - lorutime = LocalTime(date); - else - lorutime = date; } - i = 0; - int stop = args.length; - - if (maxargs >= 3 && i < stop) - year = conv[i++]; - else - year = YearFromTime(lorutime); - - if (maxargs >= 2 && i < stop) - month = conv[i++]; - else - month = MonthFromTime(lorutime); - - if (maxargs >= 1 && i < stop) - day = conv[i++]; - else - day = DateFromTime(lorutime); - - day = MakeDay(year, month, day); /* day within year */ - result = MakeDate(day, TimeWithinDay(lorutime)); - - if (local) - result = internalUTC(result); - - date = TimeClip(result); - - return date; + date = MakeDate(MakeDay(conv[0], conv[1], conv[2]), TimeWithinDay(t)); + if (local) { + return TimeClip(internalUTC(date)); + } else { + return TimeClip(date); + } } // #string_id_map# From 2846575a5040e2a4bf62653f303feb880bf56196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 025/111] add missing 'throw' statement --- src/org/mozilla/javascript/NativeObject.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index 23709f158a..a2f2fa8e54 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -164,7 +164,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObject = ScriptRuntime.toObject(cx, scope, thisObj); Object toString = ScriptableObject.getProperty(thisObject, "toString"); if (!(toString instanceof Callable)) { - ScriptRuntime.notFunctionError(toString); + throw ScriptRuntime.notFunctionError(toString); } Callable fun = (Callable) toString; return fun.call(cx, scope, thisObject, ScriptRuntime.emptyArgs); From 199d1db04a3d6fb05d504fe1569520bdb04c0ea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 026/111] Fix ch11/11.2/11.2.3 test cases: arguments are evaluated before check is made to see if object is callable --- src/org/mozilla/javascript/Interpreter.java | 16 +- src/org/mozilla/javascript/ScriptRuntime.java | 205 +++++++++++++++--- .../mozilla/javascript/optimizer/Codegen.java | 83 ++++--- .../javascript/optimizer/OptRuntime.java | 35 ++- 4 files changed, 257 insertions(+), 82 deletions(-) diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index f3c1b08dae..d63c1d14e8 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -1502,8 +1502,8 @@ private static Object interpretLoop(Context cx, CallFrame frame, case Icode_NAME_AND_THIS : // stringReg: name ++stackTop; - stack[stackTop] = ScriptRuntime.getNameFunctionAndThis(stringReg, - cx, frame.scope); + stack[stackTop] = ScriptRuntime.getNameObjectAndThis(stringReg, + cx, frame.scope); ++stackTop; stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); continue Loop; @@ -1511,8 +1511,8 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object obj = stack[stackTop]; if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]); // stringReg: property - stack[stackTop] = ScriptRuntime.getPropFunctionAndThis(obj, stringReg, - cx, frame.scope); + stack[stackTop] = ScriptRuntime.getPropObjectAndThis(obj, stringReg, + cx, frame.scope); ++stackTop; stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); continue Loop; @@ -1522,14 +1522,14 @@ private static Object interpretLoop(Context cx, CallFrame frame, if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop - 1]); Object id = stack[stackTop]; if (id == DBL_MRK) id = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop - 1] = ScriptRuntime.getElemFunctionAndThis(obj, id, cx); + stack[stackTop - 1] = ScriptRuntime.getElemObjectAndThis(obj, id, cx); stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); continue Loop; } case Icode_VALUE_AND_THIS : { Object value = stack[stackTop]; if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.getValueFunctionAndThis(value, cx); + stack[stackTop] = ScriptRuntime.getValueObjectAndThis(value, cx); ++stackTop; stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); continue Loop; @@ -1561,7 +1561,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, // Call code generation ensure that stack here // is ... Callable Scriptable Scriptable functionThis = (Scriptable)stack[stackTop + 1]; - Callable function = (Callable)stack[stackTop]; + Callable function = ScriptRuntime.ensureCallable(stack[stackTop]); Object[] outArgs = getArgsArray( stack, sDbl, stackTop + 2, indexReg); stack[stackTop] = ScriptRuntime.callSpecial( @@ -1584,7 +1584,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, // CALL generation ensures that fun and funThisObj // are already Scriptable and Callable objects respectively - Callable fun = (Callable)stack[stackTop]; + Callable fun = ScriptRuntime.ensureCallable(stack[stackTop]); Scriptable funThisObj = (Scriptable)stack[stackTop + 1]; if (op == Token.REF_CALL) { Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 2, diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 9d33cda850..5542ceeb22 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -52,9 +52,7 @@ import java.io.Serializable; import java.lang.reflect.*; import java.text.MessageFormat; -import java.util.LinkedHashMap; import java.util.Locale; -import java.util.Map; import java.util.ResourceBundle; import org.mozilla.javascript.ast.FunctionNode; @@ -1862,7 +1860,10 @@ private static Object nameOrFunction(Context cx, Scriptable scope, if (parentScope == null) { result = topScopeName(cx, scope, name); if (result == Scriptable.NOT_FOUND) { - if (firstXMLObject == null || asFunctionCall) { + if (asFunctionCall) { + storeScriptable(cx, BAD_SCRIPTABLE); + return createNotFoundError(scope, name); + } else if (firstXMLObject == null) { throw notFoundError(scope, name); } // The name was not found, but we did find an XML @@ -1879,7 +1880,8 @@ private static Object nameOrFunction(Context cx, Scriptable scope, if (asFunctionCall) { if (!(result instanceof Callable)) { - throw notFunctionError(result, name); + storeScriptable(cx, BAD_SCRIPTABLE); + return notFunctionError(result, name); } storeScriptable(cx, thisObj); } @@ -2229,6 +2231,35 @@ private static void enumChangeObject(IdEnumeration x) x.index = 0; } + /** + * {@code value} is either instanceof {@link Callable} or instanceof + * {@link RuntimeException}. If the former, the method only returns the + * input. If the latter, the method throws {@code value}. + * + * @param value object to inspect + * @return {@code value} if instanceof {@link Callable} + * @throws RuntimeException if {@code value} instanceof {@link RuntimeException} + */ + public static Callable ensureCallable(Object value) throws RuntimeException { + if (!(value instanceof Callable)) { + throw (RuntimeException)value; + } + return (Callable)value; + } + + /** + * Internal helper function for exclusive use within the more or less + * superseded getXXXFunctionAndThis() methods + */ + private static Callable ensureCallable(Context cx, Object value) { + if (!(value instanceof Callable)) { + // clear stored scriptable before throwing + lastStoredScriptable(cx); + throw (RuntimeException)value; + } + return (Callable)value; + } + /** * Prepare for calling name(...): return function corresponding to * name and make current top scope available @@ -2239,25 +2270,127 @@ private static void enumChangeObject(IdEnumeration x) public static Callable getNameFunctionAndThis(String name, Context cx, Scriptable scope) + { + Object value = getNameObjectAndThis(name, cx, scope); + return ensureCallable(cx, value); + } + + /** + * Prepare for calling obj[id](...): return function corresponding to + * obj[id] and make obj properly converted to Scriptable available + * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. + * The caller must call ScriptRuntime.lastStoredScriptable() immediately + * after calling this method. + */ + public static Callable getElemFunctionAndThis(Object obj, + Object elem, + Context cx) + { + Object value = getElemObjectAndThis(obj, elem, cx); + return ensureCallable(cx, value); + } + + /** + * Prepare for calling obj.property(...): return function corresponding to + * obj.property and make obj properly converted to Scriptable available + * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. + * The caller must call ScriptRuntime.lastStoredScriptable() immediately + * after calling this method. + * Warning: this doesn't allow to resolve primitive prototype properly when + * many top scopes are involved. + */ + public static Callable getPropFunctionAndThis(Object obj, + String property, + Context cx) + { + Object value = getPropObjectAndThis(obj, property, cx); + return ensureCallable(cx, value); + } + + /** + * Prepare for calling obj.property(...): return function corresponding to + * obj.property and make obj properly converted to Scriptable available + * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. + * The caller must call ScriptRuntime.lastStoredScriptable() immediately + * after calling this method. + */ + public static Callable getPropFunctionAndThis(Object obj, + String property, + Context cx, final Scriptable scope) + { + Object value = getPropObjectAndThis(obj, property, cx, scope); + return ensureCallable(cx, value); + } + + /** + * Prepare for calling (...): return function corresponding to + * and make parent scope of the function available + * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. + * The caller must call ScriptRuntime.lastStoredScriptable() immediately + * after calling this method. + */ + public static Callable getValueFunctionAndThis(Object value, Context cx) + { + value = getValueObjectAndThis(value, cx); + return ensureCallable(cx, value); + } + + /** + * Internal sentinel object + */ + private static final Scriptable BAD_SCRIPTABLE = new Scriptable() { + private IllegalStateException error() { + return new IllegalStateException(); + } + + public String getClassName() { throw error(); } + public Object get(String name, Scriptable start) { throw error(); } + public Object get(int index, Scriptable start) { throw error(); } + public boolean has(String name, Scriptable start) { throw error(); } + public boolean has(int index, Scriptable start) { throw error(); } + public void put(String name, Scriptable start, Object value) { throw error(); } + public void put(int index, Scriptable start, Object value) { throw error(); } + public void delete(String name) { throw error(); } + public void delete(int index) { throw error(); } + public Scriptable getPrototype() { throw error(); } + public void setPrototype(Scriptable prototype) { throw error(); } + public Scriptable getParentScope() { throw error(); } + public void setParentScope(Scriptable parent) { throw error(); } + public Object[] getIds() { throw error(); } + public Object getDefaultValue(Class hint) { throw error(); } + public boolean hasInstance(Object instance) { throw error(); } + }; + + /** + * Prepare for calling name(...): return function corresponding to + * name and make current top scope available + * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. + * The caller must call ScriptRuntime.lastStoredScriptable() immediately + * after calling this method. + */ + public static Object getNameObjectAndThis(String name, + Context cx, + Scriptable scope) { Scriptable parent = scope.getParentScope(); if (parent == null) { Object result = topScopeName(cx, scope, name); if (!(result instanceof Callable)) { + storeScriptable(cx, BAD_SCRIPTABLE); if (result == Scriptable.NOT_FOUND) { - throw notFoundError(scope, name); + return createNotFoundError(scope, name); } else { - throw notFunctionError(result, name); + return notFunctionError(result, name); } } // Top scope is not NativeWith or NativeCall => thisObj == scope Scriptable thisObj = scope; storeScriptable(cx, thisObj); - return (Callable)result; + return result; } // name will call storeScriptable(cx, thisObj); - return (Callable)nameOrFunction(cx, scope, parent, name, true); + return nameOrFunction(cx, scope, parent, name, true); } /** @@ -2267,13 +2400,13 @@ public static Callable getNameFunctionAndThis(String name, * The caller must call ScriptRuntime.lastStoredScriptable() immediately * after calling this method. */ - public static Callable getElemFunctionAndThis(Object obj, - Object elem, - Context cx) + public static Object getElemObjectAndThis(Object obj, + Object elem, + Context cx) { String str = toStringIdOrIndex(cx, elem); if (str != null) { - return getPropFunctionAndThis(obj, str, cx); + return getPropObjectAndThis(obj, str, cx); } int index = lastIndexResult(cx); @@ -2284,11 +2417,12 @@ public static Callable getElemFunctionAndThis(Object obj, Object value = ScriptableObject.getProperty(thisObj, index); if (!(value instanceof Callable)) { - throw notFunctionError(value, elem); + storeScriptable(cx, BAD_SCRIPTABLE); + return notFunctionError(value, elem); } storeScriptable(cx, thisObj); - return (Callable)value; + return value; } /** @@ -2300,9 +2434,9 @@ public static Callable getElemFunctionAndThis(Object obj, * Warning: this doesn't allow to resolve primitive prototype properly when * many top scopes are involved. */ - public static Callable getPropFunctionAndThis(Object obj, - String property, - Context cx) + public static Object getPropObjectAndThis(Object obj, + String property, + Context cx) { Scriptable thisObj = toObjectOrNull(cx, obj); return getPropFunctionAndThisHelper(obj, property, cx, thisObj); @@ -2315,15 +2449,15 @@ public static Callable getPropFunctionAndThis(Object obj, * The caller must call ScriptRuntime.lastStoredScriptable() immediately * after calling this method. */ - public static Callable getPropFunctionAndThis(Object obj, - String property, - Context cx, final Scriptable scope) + public static Object getPropObjectAndThis(Object obj, + String property, + Context cx, final Scriptable scope) { Scriptable thisObj = toObjectOrNull(cx, obj, scope); return getPropFunctionAndThisHelper(obj, property, cx, thisObj); } - private static Callable getPropFunctionAndThisHelper(Object obj, + private static Object getPropFunctionAndThisHelper(Object obj, String property, Context cx, Scriptable thisObj) { if (thisObj == null) { @@ -2338,11 +2472,12 @@ private static Callable getPropFunctionAndThisHelper(Object obj, } if (!(value instanceof Callable)) { - throw notFunctionError(thisObj, value, property); + storeScriptable(cx, BAD_SCRIPTABLE); + return notFunctionError(thisObj, value, property); } storeScriptable(cx, thisObj); - return (Callable)value; + return value; } /** @@ -2352,15 +2487,15 @@ private static Callable getPropFunctionAndThisHelper(Object obj, * The caller must call ScriptRuntime.lastStoredScriptable() immediately * after calling this method. */ - public static Callable getValueFunctionAndThis(Object value, Context cx) + public static Object getValueObjectAndThis(Object value, Context cx) { if (!(value instanceof Callable)) { - throw notFunctionError(value); + storeScriptable(cx, BAD_SCRIPTABLE); + return notFunctionError(value); } - Callable f = (Callable)value; Scriptable thisObj = null; - if (f instanceof Scriptable) { - thisObj = ((Scriptable)f).getParentScope(); + if (value instanceof Scriptable) { + thisObj = ((Scriptable)value).getParentScope(); } if (thisObj == null) { if (cx.topCallScope == null) throw new IllegalStateException(); @@ -2376,7 +2511,7 @@ public static Callable getValueFunctionAndThis(Object value, Context cx) } } storeScriptable(cx, thisObj); - return f; + return value; } /** @@ -3915,6 +4050,18 @@ public static RuntimeException notFunctionError(Object obj, Object value, typeof(value)); } + /** + * Same method as {@link #notFoundError(Scriptable, String)}, but returns + * the exception object instead of throwing it + */ + private static RuntimeException createNotFoundError(Scriptable object, + String property) + { + // XXX: use object to improve the error message + String msg = getMessage1("msg.is.not.defined", property); + return constructError("ReferenceError", msg); + } + private static RuntimeException notXmlError(Object value) { throw typeError1("msg.isnt.xml.object", toString(value)); diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index 00004f8e98..d97825bc76 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -2318,14 +2318,14 @@ private void generateExpression(Node node, Node parent) break; case Token.REF_CALL: - generateFunctionAndThisObj(child, node); + generateObjectAndThisObj(child, node); // stack: ... functionObj thisObj child = child.getNext(); generateCallArgArray(node, child, false); cfw.addALoad(contextLocal); - addScriptRuntimeInvoke( + addOptRuntimeInvoke( "callRef", - "(Lorg/mozilla/javascript/Callable;" + "(Ljava/lang/Object;" +"Lorg/mozilla/javascript/Scriptable;" +"[Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" @@ -3242,7 +3242,7 @@ private void visitSpecialCall(Node node, int type, int specialType, generateExpression(child, node); // stack: ... cx functionObj } else { - generateFunctionAndThisObj(child, node); + generateObjectAndThisObj(child, node); // stack: ... cx functionObj thisObj } child = child.getNext(); @@ -3267,7 +3267,7 @@ private void visitSpecialCall(Node node, int type, int specialType, } else { methodName = "callSpecial"; callSignature = "(Lorg/mozilla/javascript/Context;" - +"Lorg/mozilla/javascript/Callable;" + +"Ljava/lang/Object;" +"Lorg/mozilla/javascript/Scriptable;" +"[Ljava/lang/Object;" +"Lorg/mozilla/javascript/Scriptable;" @@ -3322,9 +3322,9 @@ private void visitStandardCall(Node node, Node child) } else if (childType == Token.GETPROPNOWARN) { throw Kit.codeBug(); } else { - generateFunctionAndThisObj(child, node); + generateObjectAndThisObj(child, node); methodName = "call0"; - signature = "(Lorg/mozilla/javascript/Callable;" + signature = "(Ljava/lang/Object;" +"Lorg/mozilla/javascript/Scriptable;" +"Lorg/mozilla/javascript/Context;" +"Lorg/mozilla/javascript/Scriptable;" @@ -3350,12 +3350,12 @@ private void visitStandardCall(Node node, Node child) for (Node arg = firstArgChild; arg != null; arg = arg.getNext()) { ++argCount; } - generateFunctionAndThisObj(child, node); + generateObjectAndThisObj(child, node); // stack: ... functionObj thisObj if (argCount == 1) { generateExpression(firstArgChild, node); methodName = "call1"; - signature = "(Lorg/mozilla/javascript/Callable;" + signature = "(Ljava/lang/Object;" +"Lorg/mozilla/javascript/Scriptable;" +"Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" @@ -3365,7 +3365,7 @@ private void visitStandardCall(Node node, Node child) generateExpression(firstArgChild, node); generateExpression(firstArgChild.getNext(), node); methodName = "call2"; - signature = "(Lorg/mozilla/javascript/Callable;" + signature = "(Ljava/lang/Object;" +"Lorg/mozilla/javascript/Scriptable;" +"Ljava/lang/Object;" +"Ljava/lang/Object;" @@ -3375,7 +3375,7 @@ private void visitStandardCall(Node node, Node child) } else { generateCallArgArray(node, firstArgChild, false); methodName = "callN"; - signature = "(Lorg/mozilla/javascript/Callable;" + signature = "(Ljava/lang/Object;" +"Lorg/mozilla/javascript/Scriptable;" +"[Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" @@ -3420,7 +3420,7 @@ private void visitOptimizedCall(Node node, OptFunctionNode target, if (type == Token.NEW) { generateExpression(child, node); } else { - generateFunctionAndThisObj(child, node); + generateObjectAndThisObj(child, node); thisObjLocal = getNewWordLocal(); cfw.addAStore(thisObjLocal); } @@ -3490,20 +3490,14 @@ private void visitOptimizedCall(Node node, OptFunctionNode target, cfw.add(ByteCode.GOTO, beyond); cfw.markLabel(regularCall); - // stack: ... functionObj - cfw.addALoad(contextLocal); - cfw.addALoad(variableObjectLocal); - // stack: ... functionObj cx scope - if (type != Token.NEW) { - cfw.addALoad(thisObjLocal); - releaseWordLocal(thisObjLocal); - // stack: ... functionObj cx scope thisObj - } - // XXX: this will generate code for the child array the second time, - // so expression code generation better not to alter tree structure... - generateCallArgArray(node, firstArgChild, true); - if (type == Token.NEW) { + // stack: ... functionObj + cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); + // XXX: this will generate code for the child array the second time, + // so expression code generation better not to alter tree structure... + generateCallArgArray(node, firstArgChild, true); + // stack: ... functionObj cx scope args addScriptRuntimeInvoke( "newObject", "(Ljava/lang/Object;" @@ -3512,6 +3506,25 @@ private void visitOptimizedCall(Node node, OptFunctionNode target, +"[Ljava/lang/Object;" +")Lorg/mozilla/javascript/Scriptable;"); } else { + // XXX: this will generate code for the child array the second time, + // so expression code generation better not to alter tree structure... + short argsLocal = getNewWordLocal(); + generateCallArgArray(node, firstArgChild, true); + cfw.addAStore(argsLocal); + // stack: ... functionObj + addScriptRuntimeInvoke( + "ensureCallable", + "(Ljava/lang/Object;" + +")Lorg/mozilla/javascript/Callable;"); + // stack: ... functionObj + cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); + cfw.addALoad(thisObjLocal); + releaseWordLocal(thisObjLocal); + cfw.addALoad(argsLocal); + releaseWordLocal(argsLocal); + // stack: ... functionObj cx scope thisObj args + cfw.addInvoke(ByteCode.INVOKEINTERFACE, "org/mozilla/javascript/Callable", "call", @@ -3587,7 +3600,7 @@ private void generateCallArgArray(Node node, Node argChild, boolean directCall) } } - private void generateFunctionAndThisObj(Node node, Node parent) + private void generateObjectAndThisObj(Node node, Node parent) { // Place on stack (function object, function this) pair int type = node.getType(); @@ -3606,12 +3619,12 @@ private void generateFunctionAndThisObj(Node node, Node parent) cfw.addALoad(contextLocal); cfw.addALoad(variableObjectLocal); addScriptRuntimeInvoke( - "getPropFunctionAndThis", + "getPropObjectAndThis", "(Ljava/lang/Object;" +"Ljava/lang/String;" +"Lorg/mozilla/javascript/Context;" +"Lorg/mozilla/javascript/Scriptable;" - +")Lorg/mozilla/javascript/Callable;"); + +")Ljava/lang/Object;"); } else { // Optimizer do not optimize this case for now if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) @@ -3619,11 +3632,11 @@ private void generateFunctionAndThisObj(Node node, Node parent) generateExpression(id, node); // id cfw.addALoad(contextLocal); addScriptRuntimeInvoke( - "getElemFunctionAndThis", + "getElemObjectAndThis", "(Ljava/lang/Object;" +"Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" - +")Lorg/mozilla/javascript/Callable;"); + +")Ljava/lang/Object;"); } break; } @@ -3634,11 +3647,11 @@ private void generateFunctionAndThisObj(Node node, Node parent) cfw.addALoad(contextLocal); cfw.addALoad(variableObjectLocal); addScriptRuntimeInvoke( - "getNameFunctionAndThis", + "getNameObjectAndThis", "(Ljava/lang/String;" +"Lorg/mozilla/javascript/Context;" +"Lorg/mozilla/javascript/Scriptable;" - +")Lorg/mozilla/javascript/Callable;"); + +")Ljava/lang/Object;"); break; } @@ -3646,13 +3659,13 @@ private void generateFunctionAndThisObj(Node node, Node parent) generateExpression(node, parent); cfw.addALoad(contextLocal); addScriptRuntimeInvoke( - "getValueFunctionAndThis", + "getValueObjectAndThis", "(Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" - +")Lorg/mozilla/javascript/Callable;"); + +")Ljava/lang/Object;"); break; } - // Get thisObj prepared by get(Name|Prop|Elem|Value)FunctionAndThis + // Get thisObj prepared by get(Name|Prop|Elem|Value)ObjectAndThis cfw.addALoad(contextLocal); addScriptRuntimeInvoke( "lastStoredScriptable", diff --git a/src/org/mozilla/javascript/optimizer/OptRuntime.java b/src/org/mozilla/javascript/optimizer/OptRuntime.java index 6922f7735b..d34608ff8e 100644 --- a/src/org/mozilla/javascript/optimizer/OptRuntime.java +++ b/src/org/mozilla/javascript/optimizer/OptRuntime.java @@ -51,39 +51,43 @@ public final class OptRuntime extends ScriptRuntime /** * Implement ....() call shrinking optimizer code. */ - public static Object call0(Callable fun, Scriptable thisObj, + public static Object call0(Object fun, Scriptable thisObj, Context cx, Scriptable scope) { - return fun.call(cx, scope, thisObj, ScriptRuntime.emptyArgs); + Callable c = ensureCallable(fun); + return c.call(cx, scope, thisObj, ScriptRuntime.emptyArgs); } /** * Implement ....(arg) call shrinking optimizer code. */ - public static Object call1(Callable fun, Scriptable thisObj, Object arg0, + public static Object call1(Object fun, Scriptable thisObj, Object arg0, Context cx, Scriptable scope) { - return fun.call(cx, scope, thisObj, new Object[] { arg0 } ); + Callable c = ensureCallable(fun); + return c.call(cx, scope, thisObj, new Object[] { arg0 } ); } /** * Implement ....(arg0, arg1) call shrinking optimizer code. */ - public static Object call2(Callable fun, Scriptable thisObj, + public static Object call2(Object fun, Scriptable thisObj, Object arg0, Object arg1, Context cx, Scriptable scope) { - return fun.call(cx, scope, thisObj, new Object[] { arg0, arg1 }); + Callable c = ensureCallable(fun); + return c.call(cx, scope, thisObj, new Object[] { arg0, arg1 }); } /** * Implement ....(arg0, arg1, ...) call shrinking optimizer code. */ - public static Object callN(Callable fun, Scriptable thisObj, + public static Object callN(Object fun, Scriptable thisObj, Object[] args, Context cx, Scriptable scope) { - return fun.call(cx, scope, thisObj, args); + Callable c = ensureCallable(fun); + return c.call(cx, scope, thisObj, args); } /** @@ -119,6 +123,16 @@ public static Object callProp0(Object value, String property, return f.call(cx, scope, thisObj, ScriptRuntime.emptyArgs); } + /** + * Implement ....(arg0, arg1, ...) call-ref shrinking optimizer code. + */ + public static Ref callRef(Object fun, Scriptable thisObj, + Object[] args, Context cx) + { + Callable c = ensureCallable(fun); + return callRef(c, thisObj, args, cx); + } + public static Object add(Object val1, double val2) { if (val1 instanceof Scriptable) @@ -156,13 +170,14 @@ public static void initFunction(NativeFunction fn, int functionType, ScriptRuntime.initFunction(cx, scope, fn, functionType, false); } - public static Object callSpecial(Context cx, Callable fun, + public static Object callSpecial(Context cx, Object fun, Scriptable thisObj, Object[] args, Scriptable scope, Scriptable callerThis, int callType, String fileName, int lineNumber) { - return ScriptRuntime.callSpecial(cx, fun, thisObj, args, scope, + Callable c = ensureCallable(fun); + return ScriptRuntime.callSpecial(cx, c, thisObj, args, scope, callerThis, callType, fileName, lineNumber); } From 1f1b7de3d642e9d3b5640f386bbf5f08229b81bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 027/111] S9.1_A1_T3, S9.8_A5_T2: '+' operator calls first ToPrimitive() then ToString() --- src/org/mozilla/javascript/ScriptRuntime.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 5542ceeb22..38a390bb11 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -2786,10 +2786,14 @@ public static Object add(Object val1, Object val2, Context cx) } public static CharSequence add(CharSequence val1, Object val2) { + if (val2 instanceof Scriptable) + val2 = ((Scriptable) val2).getDefaultValue(null); return newConsString(val1, toCharSequence(val2)); } public static CharSequence add(Object val1, CharSequence val2) { + if (val1 instanceof Scriptable) + val1 = ((Scriptable) val1).getDefaultValue(null); return newConsString(toCharSequence(val1), val2); } From a9d86123628918cacf4d8598cc24e9589f2c53bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 028/111] Identifier names no longer need to be quoted in object literals, therefore remove now unused functions --- src/org/mozilla/javascript/ScriptRuntime.java | 23 +------------------ src/org/mozilla/javascript/TokenStream.java | 5 ---- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 38a390bb11..07ea59dca9 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -755,20 +755,6 @@ public static String escapeString(String s, char escapeQuote) return (sb == null) ? s : sb.toString(); } - static boolean isValidIdentifierName(String s) - { - int L = s.length(); - if (L == 0) - return false; - if (!Character.isJavaIdentifierStart(s.charAt(0))) - return false; - for (int i = 1; i != L; ++i) { - if (!Character.isJavaIdentifierPart(s.charAt(i))) - return false; - } - return !TokenStream.isKeyword(s); - } - public static CharSequence toCharSequence(Object val) { if (val instanceof NativeString) { return ((NativeString)val).toCharSequence(); @@ -957,14 +943,7 @@ static String defaultObjectToSource(Context cx, Scriptable scope, continue; // a property has been removed if (i > 0) result.append(", "); - if (ScriptRuntime.isValidIdentifierName(strId)) { - result.append(strId); - } else { - result.append('\''); - result.append( - ScriptRuntime.escapeString(strId, '\'')); - result.append('\''); - } + result.append(strId); } result.append(':'); result.append(ScriptRuntime.uneval(cx, scope, value)); diff --git a/src/org/mozilla/javascript/TokenStream.java b/src/org/mozilla/javascript/TokenStream.java index 7d0b58af46..ac572d9280 100644 --- a/src/org/mozilla/javascript/TokenStream.java +++ b/src/org/mozilla/javascript/TokenStream.java @@ -113,11 +113,6 @@ String tokenToString(int token) return ""; } - static boolean isKeyword(String s) - { - return Token.EOF != stringToKeyword(s); - } - private static int stringToKeyword(String name) { // #string_id_map# From 3d74c284e4d83b7dae45e2dcb78c68d1bb9bc0d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 029/111] add new token type for strict-mode reserved identifiers --- src/org/mozilla/javascript/Token.java | 74 +++++++++++---------- src/org/mozilla/javascript/TokenStream.java | 23 ++++--- 2 files changed, 52 insertions(+), 45 deletions(-) diff --git a/src/org/mozilla/javascript/Token.java b/src/org/mozilla/javascript/Token.java index 8dcf4027a4..1dfaa69540 100644 --- a/src/org/mozilla/javascript/Token.java +++ b/src/org/mozilla/javascript/Token.java @@ -218,53 +218,54 @@ public static enum CommentType { FINALLY = 125, // finally keyword VOID = 126, // void keyword RESERVED = 127, // reserved keywords + RESERVED_STRICT= 128, // reserved keywords (strict mode) - EMPTY = 128, + EMPTY = 129, /* types used for the parse tree - these never get returned * by the scanner. */ - BLOCK = 129, // statement block - LABEL = 130, // label - TARGET = 131, - LOOP = 132, - EXPR_VOID = 133, // expression statement in functions - EXPR_RESULT = 134, // expression statement in scripts - JSR = 135, - SCRIPT = 136, // top-level node for entire script - TYPEOFNAME = 137, // for typeof(simple-name) - USE_STACK = 138, - SETPROP_OP = 139, // x.y op= something - SETELEM_OP = 140, // x[y] op= something - LOCAL_BLOCK = 141, - SET_REF_OP = 142, // *reference op= something + BLOCK = 130, // statement block + LABEL = 131, // label + TARGET = 132, + LOOP = 133, + EXPR_VOID = 134, // expression statement in functions + EXPR_RESULT = 135, // expression statement in scripts + JSR = 136, + SCRIPT = 137, // top-level node for entire script + TYPEOFNAME = 138, // for typeof(simple-name) + USE_STACK = 139, + SETPROP_OP = 140, // x.y op= something + SETELEM_OP = 141, // x[y] op= something + LOCAL_BLOCK = 142, + SET_REF_OP = 143, // *reference op= something // For XML support: - DOTDOT = 143, // member operator (..) - COLONCOLON = 144, // namespace::name - XML = 145, // XML type - DOTQUERY = 146, // .() -- e.g., x.emps.emp.(name == "terry") - XMLATTR = 147, // @ - XMLEND = 148, + DOTDOT = 144, // member operator (..) + COLONCOLON = 145, // namespace::name + XML = 146, // XML type + DOTQUERY = 147, // .() -- e.g., x.emps.emp.(name == "terry") + XMLATTR = 148, // @ + XMLEND = 149, // Optimizer-only-tokens - TO_OBJECT = 149, - TO_DOUBLE = 150, + TO_OBJECT = 150, + TO_DOUBLE = 151, - GET = 151, // JS 1.5 get pseudo keyword - SET = 152, // JS 1.5 set pseudo keyword - LET = 153, // JS 1.7 let pseudo keyword - CONST = 154, - SETCONST = 155, - SETCONSTVAR = 156, - ARRAYCOMP = 157, // array comprehension - LETEXPR = 158, - WITHEXPR = 159, - DEBUGGER = 160, - COMMENT = 161, - GENEXPR = 162, - LAST_TOKEN = 163; + GET = 152, // JS 1.5 get pseudo keyword + SET = 153, // JS 1.5 set pseudo keyword + LET = 154, // JS 1.7 let pseudo keyword + CONST = 155, + SETCONST = 156, + SETCONSTVAR = 157, + ARRAYCOMP = 158, // array comprehension + LETEXPR = 159, + WITHEXPR = 160, + DEBUGGER = 161, + COMMENT = 162, + GENEXPR = 163, + LAST_TOKEN = 164; /** * Returns a name for the token. If Rhino is compiled with certain @@ -414,6 +415,7 @@ public static String typeToName(int token) { case FINALLY: return "FINALLY"; case VOID: return "VOID"; case RESERVED: return "RESERVED"; + case RESERVED_STRICT: return "RESERVED_STRICT"; case EMPTY: return "EMPTY"; case BLOCK: return "BLOCK"; case LABEL: return "LABEL"; diff --git a/src/org/mozilla/javascript/TokenStream.java b/src/org/mozilla/javascript/TokenStream.java index ac572d9280..68cb4c4884 100644 --- a/src/org/mozilla/javascript/TokenStream.java +++ b/src/org/mozilla/javascript/TokenStream.java @@ -158,14 +158,14 @@ private static int stringToKeyword(String name) Id_import = Token.RESERVED, Id_super = Token.RESERVED, // FutureReservedWord (strict-mode) - Id_implements = Token.RESERVED, - Id_interface = Token.RESERVED, + Id_implements = Token.RESERVED_STRICT, + Id_interface = Token.RESERVED_STRICT, Id_let = Token.LET, - Id_package = Token.RESERVED, - Id_private = Token.RESERVED, - Id_protected = Token.RESERVED, - Id_public = Token.RESERVED, - Id_static = Token.RESERVED, + Id_package = Token.RESERVED_STRICT, + Id_private = Token.RESERVED_STRICT, + Id_protected = Token.RESERVED_STRICT, + Id_public = Token.RESERVED_STRICT, + Id_static = Token.RESERVED_STRICT, Id_yield = Token.YIELD; int id; @@ -392,8 +392,13 @@ final int getToken() throws IOException < Context.VERSION_1_7) { // LET and YIELD are tokens only in 1.7 and later - string = result == Token.LET ? "let" : "yield"; - result = Token.NAME; + this.string = result == Token.LET ? "let" : "yield"; + result = Token.RESERVED_STRICT; + } + if (result == Token.RESERVED_STRICT) { + result = parser.inUseStrictDirective + ? Token.RESERVED + : Token.NAME; } if (result != Token.RESERVED) { return result; From 298f4b147ab2de45afe73a98f4f86f54e756142f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 030/111] Print negative years always in extended mode for toISOString --- src/org/mozilla/javascript/NativeDate.java | 5 +-- src/org/mozilla/javascript/Parser.java | 40 +++++++++++++++---- src/org/mozilla/javascript/Token.java | 4 +- src/org/mozilla/javascript/TokenStream.java | 28 ++++++++----- src/org/mozilla/javascript/ast/AstRoot.java | 9 ----- .../mozilla/javascript/ast/ScriptNode.java | 9 +++++ .../mozilla/javascript/ast/StringLiteral.java | 12 ++++++ .../javascript/resources/Messages.properties | 2 +- .../resources/Messages_fr.properties | 2 +- 9 files changed, 79 insertions(+), 32 deletions(-) diff --git a/src/org/mozilla/javascript/NativeDate.java b/src/org/mozilla/javascript/NativeDate.java index 506405d78c..98e3b58f44 100644 --- a/src/org/mozilla/javascript/NativeDate.java +++ b/src/org/mozilla/javascript/NativeDate.java @@ -1383,9 +1383,8 @@ private static String js_toISOString(double t) { int year = YearFromTime(t); if (year < 0) { result.append('-'); - year = -year; - } - if (year > 9999) { + append0PaddedUint(result, -year, 6); + } else if (year > 9999) { append0PaddedUint(result, year, 6); } else { append0PaddedUint(result, year, 4); diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index d34a88a7f2..273fd016bb 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -546,6 +546,7 @@ private AstRoot parse() throws IOException boolean savedStrictMode = inUseStrictDirective; // TODO: eval code should get strict mode from invoking code inUseStrictDirective = false; + ts.setOctalCharacterEscape(false); try { for (;;) { @@ -564,6 +565,7 @@ private AstRoot parse() throws IOException } catch (ParserException e) { break; } + inDirectivePrologue = false; } else { n = statement(); if (inDirectivePrologue) { @@ -571,11 +573,9 @@ private AstRoot parse() throws IOException if (directive == null) { inDirectivePrologue = false; } else if (directive.equals("use strict")) { - inUseStrictDirective = true; - root.setInStrictMode(true); + setStrictMode(root); } } - } end = getNodeEnd(n); root.addChildToBack(n); @@ -616,7 +616,7 @@ private AstRoot parse() throws IOException return root; } - private AstNode parseFunctionBody() + private AstNode parseFunctionBody(FunctionNode fnNode) throws IOException { boolean isExpressionClosure = false; @@ -633,10 +633,12 @@ private AstNode parseFunctionBody() boolean inDirectivePrologue = true; boolean savedStrictMode = inUseStrictDirective; + ts.setOctalCharacterEscape(false); // Don't set 'inUseStrictDirective' to false: inherit strict mode. pn.setLineno(ts.lineno); try { +<<<<<<< HEAD if (isExpressionClosure) { ReturnStatement n = new ReturnStatement(ts.lineno); n.setReturnValue(assignExpr()); @@ -657,6 +659,7 @@ private AstNode parseFunctionBody() case Token.FUNCTION: consumeToken(); n = function(FunctionNode.FUNCTION_STATEMENT); + inDirectivePrologue = false; break; default: n = statement(); @@ -665,7 +668,7 @@ private AstNode parseFunctionBody() if (directive == null) { inDirectivePrologue = false; } else if (directive.equals("use strict")) { - inUseStrictDirective = true; + setStrictMode(fnNode); } } break; @@ -692,12 +695,35 @@ private String getDirective(AstNode n) { if (n instanceof ExpressionStatement) { AstNode e = ((ExpressionStatement) n).getExpression(); if (e instanceof StringLiteral) { - return ((StringLiteral) e).getValue(); + StringLiteral s = (StringLiteral) e; + if (s.isEscapeFreeStringLiteral()) { + return s.getValue(); + } } } return null; } + private void setStrictMode(ScriptNode script) { + // Unfortunately, Directive Prologue members in general may contain + // escapes, even while "use strict" directives may not. Therefore + // we must check whether an octal character escape has been seen in + // any previous directives whenever we encounter a "use strict" + // directive, so that the octal escape is properly treated as a + // syntax error. An example of this case: + // + // function error() + // { + // "\145"; // octal escape + // "use strict"; // retroactively makes "\145" a syntax error + // } + if (ts.hasOctalCharacterEscape()) { + addError("msg.no.octal.strict"); + } + inUseStrictDirective = true; + script.setInStrictMode(true); + } + private void parseFunctionParams(FunctionNode fnNode) throws IOException { @@ -820,7 +846,7 @@ private FunctionNode function(int type) PerFunctionVariables savedVars = new PerFunctionVariables(fnNode); try { parseFunctionParams(fnNode); - fnNode.setBody(parseFunctionBody()); + fnNode.setBody(parseFunctionBody(fnNode)); fnNode.setEncodedSourceBounds(functionSourceStart, ts.tokenEnd); fnNode.setLength(ts.tokenEnd - functionSourceStart); diff --git a/src/org/mozilla/javascript/Token.java b/src/org/mozilla/javascript/Token.java index 1dfaa69540..b1b4ea4733 100644 --- a/src/org/mozilla/javascript/Token.java +++ b/src/org/mozilla/javascript/Token.java @@ -218,7 +218,7 @@ public static enum CommentType { FINALLY = 125, // finally keyword VOID = 126, // void keyword RESERVED = 127, // reserved keywords - RESERVED_STRICT= 128, // reserved keywords (strict mode) + STRICT_RESERVED= 128, // reserved keywords in strict mode EMPTY = 129, @@ -415,7 +415,7 @@ public static String typeToName(int token) { case FINALLY: return "FINALLY"; case VOID: return "VOID"; case RESERVED: return "RESERVED"; - case RESERVED_STRICT: return "RESERVED_STRICT"; + case STRICT_RESERVED: return "STRICT_RESERVED"; case EMPTY: return "EMPTY"; case BLOCK: return "BLOCK"; case LABEL: return "LABEL"; diff --git a/src/org/mozilla/javascript/TokenStream.java b/src/org/mozilla/javascript/TokenStream.java index 68cb4c4884..a84aea4c69 100644 --- a/src/org/mozilla/javascript/TokenStream.java +++ b/src/org/mozilla/javascript/TokenStream.java @@ -158,14 +158,14 @@ private static int stringToKeyword(String name) Id_import = Token.RESERVED, Id_super = Token.RESERVED, // FutureReservedWord (strict-mode) - Id_implements = Token.RESERVED_STRICT, - Id_interface = Token.RESERVED_STRICT, + Id_implements = Token.STRICT_RESERVED, + Id_interface = Token.STRICT_RESERVED, Id_let = Token.LET, - Id_package = Token.RESERVED_STRICT, - Id_private = Token.RESERVED_STRICT, - Id_protected = Token.RESERVED_STRICT, - Id_public = Token.RESERVED_STRICT, - Id_static = Token.RESERVED_STRICT, + Id_package = Token.STRICT_RESERVED, + Id_private = Token.STRICT_RESERVED, + Id_protected = Token.STRICT_RESERVED, + Id_public = Token.STRICT_RESERVED, + Id_static = Token.STRICT_RESERVED, Id_yield = Token.YIELD; int id; @@ -262,6 +262,8 @@ final char getQuoteChar() { final double getNumber() { return number; } final boolean isNumberOctal() { return isOctal; } + final boolean hasOctalCharacterEscape() { return octalChar; } + final void setOctalCharacterEscape(boolean b) { octalChar = b; } final boolean eof() { return hitEOF; } @@ -393,9 +395,9 @@ final int getToken() throws IOException { // LET and YIELD are tokens only in 1.7 and later this.string = result == Token.LET ? "let" : "yield"; - result = Token.RESERVED_STRICT; + result = Token.STRICT_RESERVED; } - if (result == Token.RESERVED_STRICT) { + if (result == Token.STRICT_RESERVED) { result = parser.inUseStrictDirective ? Token.RESERVED : Token.NAME; @@ -587,6 +589,13 @@ final int getToken() throws IOException if ('0' <= c && c < '8') { int val = c - '0'; c = getChar(); + // Strict mode code allows only \0, then a non-digit. + if (c != 0 || (c >= '0' && c <= '9')) { + if (parser.inUseStrictDirective) { + parser.reportError("msg.no.octal.strict"); + } + octalChar = true; + } if ('0' <= c && c < '8') { val = 8 * val + c - '0'; c = getChar(); @@ -1543,6 +1552,7 @@ final String getAndResetCurrentComment() { private String string = ""; private double number; private boolean isOctal; + private boolean octalChar = false; // delimiter for last string literal scanned private int quoteChar; diff --git a/src/org/mozilla/javascript/ast/AstRoot.java b/src/org/mozilla/javascript/ast/AstRoot.java index 327e046f7b..68b35ae39f 100644 --- a/src/org/mozilla/javascript/ast/AstRoot.java +++ b/src/org/mozilla/javascript/ast/AstRoot.java @@ -57,7 +57,6 @@ public class AstRoot extends ScriptNode { private SortedSet comments; - private boolean inStrictMode; { type = Token.SCRIPT; @@ -108,14 +107,6 @@ public void addComment(Comment comment) { comment.setParent(this); } - public void setInStrictMode(boolean inStrictMode) { - this.inStrictMode = inStrictMode; - } - - public boolean isInStrictMode() { - return inStrictMode; - } - /** * Visits the comment nodes in the order they appear in the source code. * The comments are not visited by the {@link #visit} function - you must diff --git a/src/org/mozilla/javascript/ast/ScriptNode.java b/src/org/mozilla/javascript/ast/ScriptNode.java index b1330fdc6b..24039a18a7 100644 --- a/src/org/mozilla/javascript/ast/ScriptNode.java +++ b/src/org/mozilla/javascript/ast/ScriptNode.java @@ -59,6 +59,7 @@ public class ScriptNode extends Scope { private String sourceName; private String encodedSource; private int endLineno = -1; + private boolean inStrictMode; private List functions; private List regexps; @@ -193,6 +194,14 @@ public void setEndLineno(int lineno) { endLineno = lineno; } + public void setInStrictMode(boolean inStrictMode) { + this.inStrictMode = inStrictMode; + } + + public boolean isInStrictMode() { + return inStrictMode; + } + public int getFunctionCount() { return functions == null ? 0 : functions.size(); } diff --git a/src/org/mozilla/javascript/ast/StringLiteral.java b/src/org/mozilla/javascript/ast/StringLiteral.java index de0f1dd345..acfeb2d04f 100644 --- a/src/org/mozilla/javascript/ast/StringLiteral.java +++ b/src/org/mozilla/javascript/ast/StringLiteral.java @@ -108,6 +108,18 @@ public void setQuoteCharacter(char c) { quoteChar = c; } + /** + * Return true if this node, known to be an unparenthesized string literal, + * could be the string of a directive in a Directive Prologue. Directive + * strings never contain escape sequences or line continuations. + */ + public boolean isEscapeFreeStringLiteral() { + // If the string's length in the source code is its length as a value, + // accounting for the quotes, then it must not contain any escape + // sequences or line continuations. + return value.length() + 2 == getLength(); + } + @Override public String toSource(int depth) { return new StringBuilder(makeIndent(depth)) diff --git a/src/org/mozilla/javascript/resources/Messages.properties b/src/org/mozilla/javascript/resources/Messages.properties index 01284990df..5921f1fb40 100644 --- a/src/org/mozilla/javascript/resources/Messages.properties +++ b/src/org/mozilla/javascript/resources/Messages.properties @@ -532,7 +532,7 @@ msg.destruct.assign.no.init =\ Missing = in destructuring declaration msg.no.octal.strict =\ - Octal numbers prohibited in strict mode. + Octal literals and octal escape sequences are deprecated. msg.dup.obj.lit.prop.strict =\ Property "{0}" already defined in this object literal. diff --git a/src/org/mozilla/javascript/resources/Messages_fr.properties b/src/org/mozilla/javascript/resources/Messages_fr.properties index ac795cf98a..16611a8363 100644 --- a/src/org/mozilla/javascript/resources/Messages_fr.properties +++ b/src/org/mozilla/javascript/resources/Messages_fr.properties @@ -505,7 +505,7 @@ msg.destruct.assign.no.init =\ Il manque ''='' dans l''assignation d\u00e9structurante msg.no.octal.strict =\ - Les nombres octaux ne sont pas autoris\u00e9s en mode strict. + Les nombres octaux et les s\u00e9quences d''\u00e9chappement octales sont d\u00e9pr\u00e9ci\u00e9s. msg.dup.obj.lit.prop.strict =\ La propri\u00e9t\u00e9 "{0}" est d\u00e9j\u00e0 d\u00e9finie dans cet objet lit\u00e9ral. From c37a6d0aa38a6b5648d6fc2cfc77f1ddeba9c248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 031/111] strict-mode function name and function parameter checks need to happen after function body has been parsed --- src/org/mozilla/javascript/Parser.java | 69 ++++++++++++++++----- src/org/mozilla/javascript/TokenStream.java | 5 ++ 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index 273fd016bb..750294dbb4 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -734,7 +734,6 @@ private void parseFunctionParams(FunctionNode fnNode) // Would prefer not to call createDestructuringAssignment until codegen, // but the symbol definitions have to happen now, before body is parsed. Map destructuring = null; - Set paramNames = new HashSet(); do { int tt = peekToken(); if (tt == Token.LB || tt == Token.LC) { @@ -755,16 +754,6 @@ private void parseFunctionParams(FunctionNode fnNode) fnNode.addParam(createNameNode()); String paramName = ts.getString(); defineSymbol(Token.LP, paramName); - if (this.inUseStrictDirective) { - if ("eval".equals(paramName) || - "arguments".equals(paramName)) - { - reportError("msg.bad.id.strict", paramName); - } - if (paramNames.contains(paramName)) - addError("msg.dup.param.strict", paramName); - paramNames.add(paramName); - } } else { fnNode.addParam(makeErrorNode()); } @@ -797,14 +786,14 @@ private FunctionNode function(int type) Name name = null; AstNode memberExprNode = null; + // We forbid function statements in strict mode code. + if (type == FunctionNode.FUNCTION_EXPRESSION_STATEMENT && inUseStrictDirective) { + // TODO: "in strict mode code, functions may be declared only at top level or immediately within another function" + // addError(); + } + if (matchToken(Token.NAME)) { name = createNameNode(true, Token.NAME); - if (inUseStrictDirective) { - String id = name.getIdentifier(); - if ("eval".equals(id)|| "arguments".equals(id)) { - reportError("msg.bad.id.strict", id); - } - } if (!matchToken(Token.LP)) { if (compilerEnv.isAllowMemberExprAsFunctionName()) { AstNode memberExprHead = name; @@ -815,6 +804,10 @@ private FunctionNode function(int type) } } else if (matchToken(Token.LP)) { // Anonymous function: leave name as null + if (type != FunctionNode.FUNCTION_EXPRESSION) { + // TODO: Unnamed function expressions are forbidden in statement context. + // addError("function statement requires a name"); + } } else { if (compilerEnv.isAllowMemberExprAsFunctionName()) { // Note that memberExpr can not start with '(' like @@ -857,6 +850,9 @@ private FunctionNode function(int type) : "msg.anon.no.return.value"; addStrictWarning(msg, name == null ? "" : name.getIdentifier()); } + if (inUseStrictDirective) { + checkStrictFunction(fnNode); + } } finally { savedVars.restore(); } @@ -890,6 +886,45 @@ private FunctionNode function(int type) return fnNode; } + /** + * In strict mode code, all parameter names must be distinct, must not be + * strict mode reserved keywords, and must not be 'eval' or 'arguments'. We + * must perform these checks here, and not eagerly during parsing, because a + * function's body may turn on strict mode for the function head. + */ + private void checkStrictFunction(FunctionNode fnNode) { + Name name = fnNode.getFunctionName(); + if (name != null) { + String id = name.getIdentifier(); + if ("eval".equals(id) || + "arguments".equals(id) || + TokenStream.isKeyword(id)) + { + reportError("msg.bad.id.strict", id); + } + } + + boolean hasDestruct = fnNode.getProp(Node.DESTRUCTURING_PARAMS) != null; + Set paramNames = new HashSet(); + for (AstNode param : fnNode.getParams()) { + if (param instanceof Name) { + String paramName = param.getString(); + if ("eval".equals(paramName) || + "arguments".equals(paramName) || + TokenStream.isKeyword(paramName)) + { + reportError("msg.bad.id.strict", paramName); + } + if (paramNames.contains(paramName)) { + addError("msg.dup.param.strict", paramName); + } + paramNames.add(paramName); + } else if (hasDestruct && param instanceof DestructuringForm) { + // TODO: check duplicate arguments in destructuring params + } + } + } + // This function does not match the closing RC: the caller matches // the RC so it can provide a suitable error message if not matched. // This means it's up to the caller to set the length of the node to diff --git a/src/org/mozilla/javascript/TokenStream.java b/src/org/mozilla/javascript/TokenStream.java index a84aea4c69..278bb180fb 100644 --- a/src/org/mozilla/javascript/TokenStream.java +++ b/src/org/mozilla/javascript/TokenStream.java @@ -113,6 +113,11 @@ String tokenToString(int token) return ""; } + static boolean isKeyword(String s) + { + return Token.EOF != stringToKeyword(s); + } + private static int stringToKeyword(String name) { // #string_id_map# From f13eb275d8ad734bce860390afcc63d1187b6aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 032/111] Pass strict-mode flag to InterpreterData for functions as well --- src/org/mozilla/javascript/CodeGenerator.java | 5 ++--- src/org/mozilla/javascript/InterpreterData.java | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/org/mozilla/javascript/CodeGenerator.java b/src/org/mozilla/javascript/CodeGenerator.java index c872c280ae..6e7bd304f3 100644 --- a/src/org/mozilla/javascript/CodeGenerator.java +++ b/src/org/mozilla/javascript/CodeGenerator.java @@ -47,7 +47,6 @@ package org.mozilla.javascript; -import org.mozilla.javascript.ast.AstRoot; import org.mozilla.javascript.ast.ScriptNode; import org.mozilla.javascript.ast.Jump; import org.mozilla.javascript.ast.FunctionNode; @@ -115,7 +114,7 @@ public InterpreterData compile(CompilerEnvirons compilerEnv, itsData = new InterpreterData(compilerEnv.getLanguageVersion(), scriptOrFn.getSourceName(), encodedSource, - ((AstRoot)tree).isInStrictMode()); + tree.isInStrictMode()); itsData.topLevel = true; if (returnFunction) { @@ -226,7 +225,7 @@ private void generateNestedFunctions() CodeGenerator gen = new CodeGenerator(); gen.compilerEnv = compilerEnv; gen.scriptOrFn = fn; - gen.itsData = new InterpreterData(itsData); + gen.itsData = new InterpreterData(itsData, fn.isInStrictMode()); gen.generateFunctionICode(); array[i] = gen.itsData; } diff --git a/src/org/mozilla/javascript/InterpreterData.java b/src/org/mozilla/javascript/InterpreterData.java index 37b4dc46c5..6599e438b6 100644 --- a/src/org/mozilla/javascript/InterpreterData.java +++ b/src/org/mozilla/javascript/InterpreterData.java @@ -62,13 +62,13 @@ final class InterpreterData implements Serializable, DebuggableScript init(); } - InterpreterData(InterpreterData parent) + InterpreterData(InterpreterData parent, boolean isStrict) { this.parentData = parent; this.languageVersion = parent.languageVersion; this.itsSourceFile = parent.itsSourceFile; this.encodedSource = parent.encodedSource; - + this.isStrict = isStrict; init(); } From 1699f9a764a6d51298cc4c598cc7f6740939cc2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 033/111] Pass strict-flag from interpreter to parser for direct eval --- src/org/mozilla/javascript/BaseFunction.java | 4 +++- src/org/mozilla/javascript/Context.java | 21 ++++++++++++------- src/org/mozilla/javascript/Interpreter.java | 3 ++- src/org/mozilla/javascript/NativeGlobal.java | 2 +- src/org/mozilla/javascript/NativeScript.java | 4 +++- src/org/mozilla/javascript/Parser.java | 2 -- src/org/mozilla/javascript/ScriptRuntime.java | 21 ++++++++++++++++--- .../javascript/optimizer/OptRuntime.java | 2 +- 8 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/org/mozilla/javascript/BaseFunction.java b/src/org/mozilla/javascript/BaseFunction.java index bfb2f044fb..1fc3e74297 100644 --- a/src/org/mozilla/javascript/BaseFunction.java +++ b/src/org/mozilla/javascript/BaseFunction.java @@ -598,8 +598,10 @@ private static Object jsConstructor(Context cx, Scriptable scope, // Compile with explicit interpreter instance to force interpreter // mode. + // TODO: inherit strictMode from environment + boolean strictMode = false; return cx.compileFunction(global, source, evaluator, reporter, - sourceURI, 1, null); + sourceURI, 1, null, strictMode); } @Override diff --git a/src/org/mozilla/javascript/Context.java b/src/org/mozilla/javascript/Context.java index 295bdc5d03..f4fb688ee6 100644 --- a/src/org/mozilla/javascript/Context.java +++ b/src/org/mozilla/javascript/Context.java @@ -1326,7 +1326,8 @@ public final Script compileReader(Reader in, String sourceName, lineno = 0; } return (Script) compileImpl(null, in, null, sourceName, lineno, - securityDomain, false, null, null); + securityDomain, false, null, null, + false); } /** @@ -1354,19 +1355,20 @@ public final Script compileString(String source, lineno = 0; } return compileString(source, null, null, sourceName, lineno, - securityDomain); + securityDomain, false); } final Script compileString(String source, Evaluator compiler, ErrorReporter compilationErrorReporter, String sourceName, int lineno, - Object securityDomain) + Object securityDomain, boolean strictMode) { try { return (Script) compileImpl(null, null, source, sourceName, lineno, securityDomain, false, - compiler, compilationErrorReporter); + compiler, compilationErrorReporter, + strictMode); } catch (IOException ex) { // Should not happen when dealing with source as string throw new RuntimeException(); @@ -1395,19 +1397,20 @@ public final Function compileFunction(Scriptable scope, String source, Object securityDomain) { return compileFunction(scope, source, null, null, sourceName, lineno, - securityDomain); + securityDomain, false); } final Function compileFunction(Scriptable scope, String source, Evaluator compiler, ErrorReporter compilationErrorReporter, String sourceName, int lineno, - Object securityDomain) + Object securityDomain, boolean strictMode) { try { return (Function) compileImpl(scope, null, source, sourceName, lineno, securityDomain, true, - compiler, compilationErrorReporter); + compiler, compilationErrorReporter, + strictMode); } catch (IOException ioe) { // Should never happen because we just made the reader @@ -2342,7 +2345,8 @@ private Object compileImpl(Scriptable scope, String sourceName, int lineno, Object securityDomain, boolean returnFunction, Evaluator compiler, - ErrorReporter compilationErrorReporter) + ErrorReporter compilationErrorReporter, + boolean strictMode) throws IOException { if(sourceName == null) { @@ -2372,6 +2376,7 @@ private Object compileImpl(Scriptable scope, } Parser p = new Parser(compilerEnv, compilationErrorReporter); + p.inUseStrictDirective = strictMode; if (returnFunction) { p.calledByCompileFunction = true; } diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index d63c1d14e8..cdd3b4bf2f 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -1567,7 +1567,8 @@ private static Object interpretLoop(Context cx, CallFrame frame, stack[stackTop] = ScriptRuntime.callSpecial( cx, function, functionThis, outArgs, frame.scope, frame.thisObj, callType, - frame.idata.itsSourceFile, sourceLine); + frame.idata.itsSourceFile, sourceLine, + frame.idata.isStrict); } frame.pc += 4; continue Loop; diff --git a/src/org/mozilla/javascript/NativeGlobal.java b/src/org/mozilla/javascript/NativeGlobal.java index 8727625ecd..ef201f90e4 100644 --- a/src/org/mozilla/javascript/NativeGlobal.java +++ b/src/org/mozilla/javascript/NativeGlobal.java @@ -526,7 +526,7 @@ private Object js_unescape(Object[] args) private Object js_eval(Context cx, Scriptable scope, Object[] args) { Scriptable global = ScriptableObject.getTopLevelScope(scope); - return ScriptRuntime.evalSpecial(cx, global, global, args, "eval code", 1); + return ScriptRuntime.evalSpecial(cx, global, global, args, "eval code", 1, false); } static boolean isEvalFunction(Object functionObj) diff --git a/src/org/mozilla/javascript/NativeScript.java b/src/org/mozilla/javascript/NativeScript.java index 2022311772..9bddc1f735 100644 --- a/src/org/mozilla/javascript/NativeScript.java +++ b/src/org/mozilla/javascript/NativeScript.java @@ -191,8 +191,10 @@ private static Script compile(Context cx, String source) } ErrorReporter reporter; reporter = DefaultErrorReporter.forEval(cx.getErrorReporter()); + // TODO: which strict-mode setting needs to be applied here? + boolean strictMode = false; return cx.compileString(source, null, reporter, filename, - linep[0], null); + linep[0], null, strictMode); } // #string_id_map# diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index 750294dbb4..a71701229f 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -544,8 +544,6 @@ private AstRoot parse() throws IOException boolean inDirectivePrologue = true; boolean savedStrictMode = inUseStrictDirective; - // TODO: eval code should get strict mode from invoking code - inUseStrictDirective = false; ts.setOctalCharacterEscape(false); try { diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 07ea59dca9..0768f38210 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -2538,12 +2538,13 @@ public static Object callSpecial(Context cx, Callable fun, Scriptable thisObj, Object[] args, Scriptable scope, Scriptable callerThis, int callType, - String filename, int lineNumber) + String filename, int lineNumber, + boolean strictMode) { if (callType == Node.SPECIALCALL_EVAL) { if (thisObj.getParentScope() == null && NativeGlobal.isEvalFunction(fun)) { return evalSpecial(cx, scope, callerThis, args, - filename, lineNumber); + filename, lineNumber, strictMode); } } else if (callType == Node.SPECIALCALL_WITH) { if (NativeWith.isWithFunction(fun)) { @@ -2640,9 +2641,23 @@ static Callable getCallable(Object thisObj) * * See ECMA 15.1.2.1 */ + @Deprecated public static Object evalSpecial(Context cx, Scriptable scope, Object thisArg, Object[] args, String filename, int lineNumber) + { + return evalSpecial(cx, scope, thisArg, args, filename, lineNumber, false); + } + + /** + * The eval function property of the global object. + * + * See ECMA 15.1.2.1 + */ + public static Object evalSpecial(Context cx, Scriptable scope, + Object thisArg, Object[] args, + String filename, int lineNumber, + boolean strictMode) { if (args.length < 1) return Undefined.instance; @@ -2681,7 +2696,7 @@ public static Object evalSpecial(Context cx, Scriptable scope, // Compile with explicit interpreter instance to force interpreter // mode. Script script = cx.compileString(x.toString(), evaluator, - reporter, sourceName, 1, null); + reporter, sourceName, 1, null, strictMode); evaluator.setEvalScriptFlag(script); Callable c = (Callable)script; return c.call(cx, scope, (Scriptable)thisArg, ScriptRuntime.emptyArgs); diff --git a/src/org/mozilla/javascript/optimizer/OptRuntime.java b/src/org/mozilla/javascript/optimizer/OptRuntime.java index d34608ff8e..c56cd629b3 100644 --- a/src/org/mozilla/javascript/optimizer/OptRuntime.java +++ b/src/org/mozilla/javascript/optimizer/OptRuntime.java @@ -179,7 +179,7 @@ public static Object callSpecial(Context cx, Object fun, Callable c = ensureCallable(fun); return ScriptRuntime.callSpecial(cx, c, thisObj, args, scope, callerThis, callType, - fileName, lineNumber); + fileName, lineNumber, false); } public static Object newObjectSpecial(Context cx, Object fun, From b39e95e71a8021748c43e9353703625990e704fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 034/111] Also check variables for reserved names in strict-mode --- src/org/mozilla/javascript/Parser.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index a71701229f..4ab980848c 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -263,7 +263,7 @@ void reportError(String messageId, int position, int length) void reportError(String messageId, String messageArg, int position, int length) { - addError(messageId, position, length); + addError(messageId, messageArg, position, length); if (!compilerEnv.recoverFromErrors()) { throw new ParserException(); @@ -1937,7 +1937,9 @@ private VariableDeclaration variables(int declType, int pos, boolean isStatement name.setLineno(ts.getLineno()); if (inUseStrictDirective) { String id = ts.getString(); - if ("eval".equals(id) || "arguments".equals(ts.getString())) + if ("eval".equals(id) || + "arguments".equals(ts.getString()) || + TokenStream.isKeyword(id)) { reportError("msg.bad.id.strict", id); } From 1beedf521ff53e28778f16e70f3f5447c38a7cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 035/111] account for RESERVED -> NAME change in TokenStream --- src/org/mozilla/javascript/Parser.java | 29 +++++++++++---------- src/org/mozilla/javascript/TokenStream.java | 7 +++-- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index 4ab980848c..300c82a801 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -567,11 +567,13 @@ private AstRoot parse() throws IOException } else { n = statement(); if (inDirectivePrologue) { - String directive = getDirective(n); + StringLiteral directive = getDirective(n); if (directive == null) { inDirectivePrologue = false; - } else if (directive.equals("use strict")) { - setStrictMode(root); + } else if (directive.isEscapeFreeStringLiteral()){ + if ("use strict".equals(directive.getValue())) { + setStrictMode(root); + } } } } @@ -662,11 +664,13 @@ private AstNode parseFunctionBody(FunctionNode fnNode) default: n = statement(); if (inDirectivePrologue) { - String directive = getDirective(n); + StringLiteral directive = getDirective(n); if (directive == null) { inDirectivePrologue = false; - } else if (directive.equals("use strict")) { - setStrictMode(fnNode); + } else if (directive.isEscapeFreeStringLiteral()){ + if ("use strict".equals(directive.getValue())) { + setStrictMode(fnNode); + } } } break; @@ -689,14 +693,11 @@ private AstNode parseFunctionBody(FunctionNode fnNode) return pn; } - private String getDirective(AstNode n) { + private StringLiteral getDirective(AstNode n) { if (n instanceof ExpressionStatement) { AstNode e = ((ExpressionStatement) n).getExpression(); if (e instanceof StringLiteral) { - StringLiteral s = (StringLiteral) e; - if (s.isEscapeFreeStringLiteral()) { - return s.getValue(); - } + return (StringLiteral) e; } } return null; @@ -898,7 +899,7 @@ private void checkStrictFunction(FunctionNode fnNode) { "arguments".equals(id) || TokenStream.isKeyword(id)) { - reportError("msg.bad.id.strict", id); + addError("msg.bad.id.strict", id); } } @@ -911,7 +912,7 @@ private void checkStrictFunction(FunctionNode fnNode) { "arguments".equals(paramName) || TokenStream.isKeyword(paramName)) { - reportError("msg.bad.id.strict", paramName); + addError("msg.bad.id.strict", paramName); } if (paramNames.contains(paramName)) { addError("msg.dup.param.strict", paramName); @@ -1941,7 +1942,7 @@ private VariableDeclaration variables(int declType, int pos, boolean isStatement "arguments".equals(ts.getString()) || TokenStream.isKeyword(id)) { - reportError("msg.bad.id.strict", id); + addError("msg.bad.id.strict", id); } } defineSymbol(declType, ts.getString(), inForInit); diff --git a/src/org/mozilla/javascript/TokenStream.java b/src/org/mozilla/javascript/TokenStream.java index 278bb180fb..ae3fbdd773 100644 --- a/src/org/mozilla/javascript/TokenStream.java +++ b/src/org/mozilla/javascript/TokenStream.java @@ -407,7 +407,10 @@ final int getToken() throws IOException ? Token.RESERVED : Token.NAME; } - if (result != Token.RESERVED) { + if (result == Token.NAME) { + this.string = (String)allStrings.intern(str); + return result; + } else if (result != Token.RESERVED) { return result; } else if (!parser.compilerEnv. isReservedKeywordAsIdentifier()) @@ -597,7 +600,7 @@ final int getToken() throws IOException // Strict mode code allows only \0, then a non-digit. if (c != 0 || (c >= '0' && c <= '9')) { if (parser.inUseStrictDirective) { - parser.reportError("msg.no.octal.strict"); + parser.addError("msg.no.octal.strict"); } octalChar = true; } From acad1fd3baf744ca7f5aa4fdabf593eba54218eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 036/111] re-sync date methods with jsdate.cpp --- src/org/mozilla/javascript/NativeDate.java | 213 +++++++++++---------- 1 file changed, 114 insertions(+), 99 deletions(-) diff --git a/src/org/mozilla/javascript/NativeDate.java b/src/org/mozilla/javascript/NativeDate.java index 98e3b58f44..cd891de0e7 100644 --- a/src/org/mozilla/javascript/NativeDate.java +++ b/src/org/mozilla/javascript/NativeDate.java @@ -1461,9 +1461,14 @@ private static void appendWeekDayName(StringBuilder sb, int index) private static double makeTime(double date, Object[] args, int methodId) { if (args.length == 0) { - /* Seems like all the Date.setWhatever functions in ECMA are only - * varargs beyond the first argument; This means that - * "d = new Date(); d.setMilliseconds()" returns NaN. Blech. + /* + * Satisfy the ECMA rule that if a function is called with + * fewer arguments than the specified formal arguments, the + * remaining arguments are set to undefined. Seems like all + * the Date.setWhatever functions in ECMA are only varargs + * beyond the first argument; this should be set to undefined + * if it's not given. This means that "d = new Date(); + * d.setMilliseconds()" returns NaN. Blech. */ return ScriptRuntime.NaN; } @@ -1475,88 +1480,95 @@ private static double makeTime(double date, Object[] args, int methodId) local = false; // fallthrough case Id_setMilliseconds: - maxargs = 1; + maxargs = 1; break; case Id_setUTCSeconds: local = false; // fallthrough case Id_setSeconds: - maxargs = 2; + maxargs = 2; break; case Id_setUTCMinutes: local = false; // fallthrough case Id_setMinutes: - maxargs = 3; + maxargs = 3; break; case Id_setUTCHours: local = false; // fallthrough case Id_setHours: - maxargs = 4; + maxargs = 4; break; default: throw Kit.codeBug(); } - // [0: hour, 1: minute, 2: second, 3: millisecond] - double[] conv = new double[4]; - for (int i = 0; i < args.length && i < maxargs; ++i) { - // derive conv-index from args-index - int k = i + 4 - maxargs; - conv[k] = ScriptRuntime.toNumber(args[i]); - } - - if (date != date) { - // if base date is invalid, return NaN; must happen after arguments - // have been evaluated - return ScriptRuntime.NaN; - } - double t = local ? LocalTime(date) : date; - - for (int i = 0; i < 4; ++i) { - // derive args-index from conv-index - int k = i - 4 + maxargs; - if (k < 0 || k >= args.length) { - // if argument not supplied, use default - if (i == 0) { - conv[0] = HourFromTime(t); - } else if (i == 1) { - conv[1] = MinFromTime(t); - } else if (i == 2) { - conv[2] = SecFromTime(t); - } else { - conv[3] = msFromTime(t); - } + boolean hasNaN = false; + int numNums = args.length < maxargs ? args.length : maxargs; + assert numNums <= 4; + double[] nums = new double[4]; + for (int i = 0; i < numNums; i++) { + double d = ScriptRuntime.toNumber(args[i]); + if (d != d || Double.isInfinite(d)) { + hasNaN = true; } else { - // limit checks that happen in MakeTime in ECMA. - double x = conv[i]; - if (x != x || Double.isInfinite(x)) { - return ScriptRuntime.NaN; - } - conv[i] = ScriptRuntime.toInteger(x); + nums[i] = ScriptRuntime.toInteger(d); } } - date = MakeDate(Day(t), MakeTime(conv[0], conv[1], conv[2], conv[3])); - if (local) { - return TimeClip(internalUTC(date)); - } else { - return TimeClip(date); + // just return NaN if the date is already NaN, + // limit checks that happen in MakeTime in ECMA. + if (hasNaN || date != date) { + return ScriptRuntime.NaN; } + + int i = 0, stop = numNums; + double hour, min, sec, msec; + double lorutime; /* Local or UTC version of date */ + + if (local) + lorutime = LocalTime(date); + else + lorutime = date; + + if (maxargs >= 4 && i < stop) + hour = nums[i++]; + else + hour = HourFromTime(lorutime); + + if (maxargs >= 3 && i < stop) + min = nums[i++]; + else + min = MinFromTime(lorutime); + + if (maxargs >= 2 && i < stop) + sec = nums[i++]; + else + sec = SecFromTime(lorutime); + + if (maxargs >= 1 && i < stop) + msec = nums[i++]; + else + msec = msFromTime(lorutime); + + double time = MakeTime(hour, min, sec, msec); + double result = MakeDate(Day(lorutime), time); + + if (local) + result = internalUTC(result); + + return TimeClip(result); } private static double makeDate(double date, Object[] args, int methodId) { + /* see complaint about ECMA in date_MakeTime */ if (args.length == 0) { - /* Seems like all the Date.setWhatever functions in ECMA are only - * varargs beyond the first argument; This means that - * "d = new Date(); d.setMilliseconds()" returns NaN. Blech. - */ return ScriptRuntime.NaN; } @@ -1588,62 +1600,65 @@ private static double makeDate(double date, Object[] args, int methodId) throw Kit.codeBug(); } - // [0: year, 1: month, 2: day] - double[] conv = new double[3]; - for (int i = 0; i < args.length && i < maxargs; ++i) { - // derive conv-index from args-index - int k = i + 3 - maxargs; - conv[k] = ScriptRuntime.toNumber(args[i]); + boolean hasNaN = false; + int numNums = args.length < maxargs ? args.length : maxargs; + assert 1 <= numNums && numNums <= 3; + double[] nums = new double[3]; + for (int i = 0; i < numNums; i++) { + double d = ScriptRuntime.toNumber(args[i]); + if (d != d || Double.isInfinite(d)) { + hasNaN = true; + } else { + nums[i] = ScriptRuntime.toInteger(d); + } } - double t; - switch (methodId) { - case Id_setUTCFullYear: - case Id_setFullYear: - if (date != date) { - // if base date is NaN, use 0 for set[UTC]Years() - t = 0; - break; - } - // fallthrough - - default: - if (date != date) { - // if base date is invalid, return NaN; must happen after - // arguments have been evaluated - return ScriptRuntime.NaN; - } - t = local ? LocalTime(date) : date; + // limit checks that happen in MakeTime in ECMA. + if (hasNaN) { + return ScriptRuntime.NaN; } - for (int i = 0; i < 3; ++i) { - // derive args-index from conv-index - int k = i - 3 + maxargs; - if (k < 0 || k >= args.length) { - // if argument not supplied, use default - if (i == 0) { - conv[0] = YearFromTime(t); - } else if (i == 1) { - conv[1] = MonthFromTime(t); - } else { - conv[2] = DateFromTime(t); - } + int i = 0, stop = numNums; + double year, month, day; + double lorutime; /* Local or UTC version of date */ + + /* return NaN if date is NaN and we're not setting the year, + * If we are, use 0 as the time. */ + if (date != date) { + if (maxargs < 3) { + return ScriptRuntime.NaN; } else { - // limit checks that happen in MakeDate in ECMA. - double x = conv[i]; - if (x != x || Double.isInfinite(x)) { - return ScriptRuntime.NaN; - } - conv[i] = ScriptRuntime.toInteger(x); + lorutime = 0; } - } - - date = MakeDate(MakeDay(conv[0], conv[1], conv[2]), TimeWithinDay(t)); - if (local) { - return TimeClip(internalUTC(date)); } else { - return TimeClip(date); + if (local) + lorutime = LocalTime(date); + else + lorutime = date; } + + if (maxargs >= 3 && i < stop) + year = nums[i++]; + else + year = YearFromTime(lorutime); + + if (maxargs >= 2 && i < stop) + month = nums[i++]; + else + month = MonthFromTime(lorutime); + + if (maxargs >= 1 && i < stop) + day = nums[i++]; + else + day = DateFromTime(lorutime); + + day = MakeDay(year, month, day); /* day within year */ + double result = MakeDate(day, TimeWithinDay(lorutime)); + + if (local) + result = internalUTC(result); + + return TimeClip(result); } // #string_id_map# From c42394c823b098d499d336fabe232248d347797c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 037/111] Add missing entry for STRICT_SETNAME; Move YIELD to proper position per actual integer value --- src/org/mozilla/javascript/Token.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/Token.java b/src/org/mozilla/javascript/Token.java index b1b4ea4733..9728568bc9 100644 --- a/src/org/mozilla/javascript/Token.java +++ b/src/org/mozilla/javascript/Token.java @@ -361,6 +361,8 @@ public static String typeToName(int token) { case DEL_REF: return "DEL_REF"; case REF_CALL: return "REF_CALL"; case REF_SPECIAL: return "REF_SPECIAL"; + case YIELD: return "YIELD"; + case STRICT_SETNAME: return "STRICT_SETNAME"; case DEFAULTNAMESPACE:return "DEFAULTNAMESPACE"; case ESCXMLTEXT: return "ESCXMLTEXT"; case ESCXMLATTR: return "ESCXMLATTR"; @@ -442,7 +444,6 @@ public static String typeToName(int token) { case GET: return "GET"; case SET: return "SET"; case LET: return "LET"; - case YIELD: return "YIELD"; case CONST: return "CONST"; case SETCONST: return "SETCONST"; case ARRAYCOMP: return "ARRAYCOMP"; From 20c243012b50831e152009c813e1e00963d2b7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 038/111] No longer necessary to restrict strict-mode check to AstRoot --- src/org/mozilla/javascript/NodeTransformer.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/org/mozilla/javascript/NodeTransformer.java b/src/org/mozilla/javascript/NodeTransformer.java index 2ad6ce1754..2826a89d7c 100644 --- a/src/org/mozilla/javascript/NodeTransformer.java +++ b/src/org/mozilla/javascript/NodeTransformer.java @@ -89,8 +89,7 @@ private void transformCompilationUnit(ScriptNode tree) //uncomment to print tree before transformation if (Token.printTrees) System.out.println(tree.toStringTree(tree)); - boolean inStrictMode = tree instanceof AstRoot && - ((AstRoot)tree).isInStrictMode(); + boolean inStrictMode = tree.isInStrictMode(); transformCompilationUnit_r(tree, tree, tree, createScopeObjects, inStrictMode); } From c9515c7de67ac82ca05e98cbcca41ef0254abbb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 039/111] Add new token-types for strict [[Put]] operations similar to STRICT_SETNAME; For now they're only mapped to the non-strict operations --- src/org/mozilla/javascript/CodeGenerator.java | 8 +- src/org/mozilla/javascript/Interpreter.java | 20 ++ .../mozilla/javascript/NodeTransformer.java | 22 +- src/org/mozilla/javascript/Token.java | 194 +++++++++--------- .../mozilla/javascript/optimizer/Block.java | 2 + .../mozilla/javascript/optimizer/Codegen.java | 12 +- .../javascript/optimizer/Optimizer.java | 4 +- 7 files changed, 161 insertions(+), 101 deletions(-) diff --git a/src/org/mozilla/javascript/CodeGenerator.java b/src/org/mozilla/javascript/CodeGenerator.java index 6e7bd304f3..8aa88c83db 100644 --- a/src/org/mozilla/javascript/CodeGenerator.java +++ b/src/org/mozilla/javascript/CodeGenerator.java @@ -740,12 +740,14 @@ private void visitExpression(Node node, int contextFlags) case Token.SETPROP: case Token.SETPROP_OP: + case Token.STRICT_SETPROP: + case Token.STRICT_SETPROP_OP: { visitExpression(child, 0); child = child.getNext(); String property = child.getString(); child = child.getNext(); - if (type == Token.SETPROP_OP) { + if (type == Token.SETPROP_OP || type == Token.STRICT_SETPROP_OP) { addIcode(Icode_DUP); stackChange(1); addStringOp(Token.GETPROP, property); @@ -760,11 +762,13 @@ private void visitExpression(Node node, int contextFlags) case Token.SETELEM: case Token.SETELEM_OP: + case Token.STRICT_SETELEM: + case Token.STRICT_SETELEM_OP: visitExpression(child, 0); child = child.getNext(); visitExpression(child, 0); child = child.getNext(); - if (type == Token.SETELEM_OP) { + if (type == Token.SETELEM_OP || type == Token.STRICT_SETELEM_OP) { addIcode(Icode_DUP2); stackChange(2); addToken(Token.GETELEM); diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index cdd3b4bf2f..d8d6b3d37a 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -433,6 +433,26 @@ static void dumpICode(InterpreterData idata) pc += 2; break; } + case Icode_REG_STR_C0: { + String str = strings[0]; + out.println(tname + " \"" + str + '"'); + break; + } + case Icode_REG_STR_C1: { + String str = strings[1]; + out.println(tname + " \"" + str + '"'); + break; + } + case Icode_REG_STR_C2: { + String str = strings[2]; + out.println(tname + " \"" + str + '"'); + break; + } + case Icode_REG_STR_C3: { + String str = strings[3]; + out.println(tname + " \"" + str + '"'); + break; + } case Icode_REG_STR1: { String str = strings[0xFF & iCode[pc]]; out.println(tname + " \"" + str + '"'); diff --git a/src/org/mozilla/javascript/NodeTransformer.java b/src/org/mozilla/javascript/NodeTransformer.java index 2826a89d7c..de6638f8be 100644 --- a/src/org/mozilla/javascript/NodeTransformer.java +++ b/src/org/mozilla/javascript/NodeTransformer.java @@ -42,7 +42,6 @@ package org.mozilla.javascript; -import org.mozilla.javascript.ast.AstRoot; import org.mozilla.javascript.ast.FunctionNode; import org.mozilla.javascript.ast.Jump; import org.mozilla.javascript.ast.Scope; @@ -429,6 +428,27 @@ else if (last.getType() == Token.NAME && } break; } + + case Token.SETPROP: + if (inStrictMode) { + node.setType(Token.STRICT_SETPROP); + } + break; + case Token.SETPROP_OP: + if (inStrictMode) { + node.setType(Token.STRICT_SETPROP_OP); + } + break; + case Token.SETELEM: + if (inStrictMode) { + node.setType(Token.STRICT_SETELEM); + } + break; + case Token.SETELEM_OP: + if (inStrictMode) { + node.setType(Token.STRICT_SETELEM_OP); + } + break; } transformCompilationUnit_r(tree, node, diff --git a/src/org/mozilla/javascript/Token.java b/src/org/mozilla/javascript/Token.java index 9728568bc9..43190de4f7 100644 --- a/src/org/mozilla/javascript/Token.java +++ b/src/org/mozilla/javascript/Token.java @@ -151,121 +151,125 @@ public static enum CommentType { REF_SPECIAL = 71, // reference for special properties like __proto YIELD = 72, // JS 1.7 yield pseudo keyword STRICT_SETNAME = 73, + STRICT_SETPROP = 74, + STRICT_SETELEM = 75, // For XML support: - DEFAULTNAMESPACE = 74, // default xml namespace = - ESCXMLATTR = 75, - ESCXMLTEXT = 76, - REF_MEMBER = 77, // Reference for x.@y, x..y etc. - REF_NS_MEMBER = 78, // Reference for x.ns::y, x..ns::y etc. - REF_NAME = 79, // Reference for @y, @[y] etc. - REF_NS_NAME = 80; // Reference for ns::y, @ns::y@[y] etc. + DEFAULTNAMESPACE = 76, // default xml namespace = + ESCXMLATTR = 77, + ESCXMLTEXT = 78, + REF_MEMBER = 79, // Reference for x.@y, x..y etc. + REF_NS_MEMBER = 80, // Reference for x.ns::y, x..ns::y etc. + REF_NAME = 81, // Reference for @y, @[y] etc. + REF_NS_NAME = 82; // Reference for ns::y, @ns::y@[y] etc. // End of interpreter bytecodes public final static int LAST_BYTECODE_TOKEN = REF_NS_NAME, - TRY = 81, - SEMI = 82, // semicolon - LB = 83, // left and right brackets - RB = 84, - LC = 85, // left and right curlies (braces) - RC = 86, - LP = 87, // left and right parentheses - RP = 88, - COMMA = 89, // comma operator + TRY = 83, + SEMI = 84, // semicolon + LB = 85, // left and right brackets + RB = 86, + LC = 87, // left and right curlies (braces) + RC = 88, + LP = 89, // left and right parentheses + RP = 90, + COMMA = 91, // comma operator - ASSIGN = 90, // simple assignment (=) - ASSIGN_BITOR = 91, // |= - ASSIGN_BITXOR = 92, // ^= - ASSIGN_BITAND = 93, // |= - ASSIGN_LSH = 94, // <<= - ASSIGN_RSH = 95, // >>= - ASSIGN_URSH = 96, // >>>= - ASSIGN_ADD = 97, // += - ASSIGN_SUB = 98, // -= - ASSIGN_MUL = 99, // *= - ASSIGN_DIV = 100, // /= - ASSIGN_MOD = 101; // %= + ASSIGN = 92, // simple assignment (=) + ASSIGN_BITOR = 93, // |= + ASSIGN_BITXOR = 94, // ^= + ASSIGN_BITAND = 95, // |= + ASSIGN_LSH = 96, // <<= + ASSIGN_RSH = 97, // >>= + ASSIGN_URSH = 98, // >>>= + ASSIGN_ADD = 99, // += + ASSIGN_SUB = 100, // -= + ASSIGN_MUL = 101, // *= + ASSIGN_DIV = 102, // /= + ASSIGN_MOD = 103; // %= public final static int FIRST_ASSIGN = ASSIGN, LAST_ASSIGN = ASSIGN_MOD, - HOOK = 102, // conditional (?:) - COLON = 103, - OR = 104, // logical or (||) - AND = 105, // logical and (&&) - INC = 106, // increment/decrement (++ --) - DEC = 107, - DOT = 108, // member operator (.) - FUNCTION = 109, // function keyword - EXPORT = 110, // export keyword - IMPORT = 111, // import keyword - IF = 112, // if keyword - ELSE = 113, // else keyword - SWITCH = 114, // switch keyword - CASE = 115, // case keyword - DEFAULT = 116, // default keyword - WHILE = 117, // while keyword - DO = 118, // do keyword - FOR = 119, // for keyword - BREAK = 120, // break keyword - CONTINUE = 121, // continue keyword - VAR = 122, // var keyword - WITH = 123, // with keyword - CATCH = 124, // catch keyword - FINALLY = 125, // finally keyword - VOID = 126, // void keyword - RESERVED = 127, // reserved keywords - STRICT_RESERVED= 128, // reserved keywords in strict mode + HOOK = 104, // conditional (?:) + COLON = 105, + OR = 106, // logical or (||) + AND = 107, // logical and (&&) + INC = 108, // increment/decrement (++ --) + DEC = 109, + DOT = 110, // member operator (.) + FUNCTION = 111, // function keyword + EXPORT = 112, // export keyword + IMPORT = 113, // import keyword + IF = 114, // if keyword + ELSE = 115, // else keyword + SWITCH = 116, // switch keyword + CASE = 117, // case keyword + DEFAULT = 118, // default keyword + WHILE = 119, // while keyword + DO = 120, // do keyword + FOR = 121, // for keyword + BREAK = 122, // break keyword + CONTINUE = 123, // continue keyword + VAR = 124, // var keyword + WITH = 125, // with keyword + CATCH = 126, // catch keyword + FINALLY = 127, // finally keyword + VOID = 128, // void keyword + RESERVED = 129, // reserved keywords + STRICT_RESERVED= 130, // reserved keywords in strict mode - EMPTY = 129, + EMPTY = 131, /* types used for the parse tree - these never get returned * by the scanner. */ - BLOCK = 130, // statement block - LABEL = 131, // label - TARGET = 132, - LOOP = 133, - EXPR_VOID = 134, // expression statement in functions - EXPR_RESULT = 135, // expression statement in scripts - JSR = 136, - SCRIPT = 137, // top-level node for entire script - TYPEOFNAME = 138, // for typeof(simple-name) - USE_STACK = 139, - SETPROP_OP = 140, // x.y op= something - SETELEM_OP = 141, // x[y] op= something - LOCAL_BLOCK = 142, - SET_REF_OP = 143, // *reference op= something + BLOCK = 132, // statement block + LABEL = 133, // label + TARGET = 134, + LOOP = 135, + EXPR_VOID = 136, // expression statement in functions + EXPR_RESULT = 137, // expression statement in scripts + JSR = 138, + SCRIPT = 139, // top-level node for entire script + TYPEOFNAME = 140, // for typeof(simple-name) + USE_STACK = 141, + SETPROP_OP = 142, // x.y op= something + STRICT_SETPROP_OP = 143, + SETELEM_OP = 144, // x[y] op= something + STRICT_SETELEM_OP = 145, + LOCAL_BLOCK = 146, + SET_REF_OP = 147, // *reference op= something // For XML support: - DOTDOT = 144, // member operator (..) - COLONCOLON = 145, // namespace::name - XML = 146, // XML type - DOTQUERY = 147, // .() -- e.g., x.emps.emp.(name == "terry") - XMLATTR = 148, // @ - XMLEND = 149, + DOTDOT = 148, // member operator (..) + COLONCOLON = 149, // namespace::name + XML = 150, // XML type + DOTQUERY = 151, // .() -- e.g., x.emps.emp.(name == "terry") + XMLATTR = 152, // @ + XMLEND = 153, // Optimizer-only-tokens - TO_OBJECT = 150, - TO_DOUBLE = 151, + TO_OBJECT = 154, + TO_DOUBLE = 155, - GET = 152, // JS 1.5 get pseudo keyword - SET = 153, // JS 1.5 set pseudo keyword - LET = 154, // JS 1.7 let pseudo keyword - CONST = 155, - SETCONST = 156, - SETCONSTVAR = 157, - ARRAYCOMP = 158, // array comprehension - LETEXPR = 159, - WITHEXPR = 160, - DEBUGGER = 161, - COMMENT = 162, - GENEXPR = 163, - LAST_TOKEN = 164; + GET = 156, // JS 1.5 get pseudo keyword + SET = 157, // JS 1.5 set pseudo keyword + LET = 158, // JS 1.7 let pseudo keyword + CONST = 159, + SETCONST = 160, + SETCONSTVAR = 161, + ARRAYCOMP = 162, // array comprehension + LETEXPR = 163, + WITHEXPR = 164, + DEBUGGER = 165, + COMMENT = 166, + GENEXPR = 167, + LAST_TOKEN = 168; /** * Returns a name for the token. If Rhino is compiled with certain @@ -363,6 +367,8 @@ public static String typeToName(int token) { case REF_SPECIAL: return "REF_SPECIAL"; case YIELD: return "YIELD"; case STRICT_SETNAME: return "STRICT_SETNAME"; + case STRICT_SETPROP: return "STRICT_SETPROP"; + case STRICT_SETELEM: return "STRICT_SETELEM"; case DEFAULTNAMESPACE:return "DEFAULTNAMESPACE"; case ESCXMLTEXT: return "ESCXMLTEXT"; case ESCXMLATTR: return "ESCXMLATTR"; @@ -429,8 +435,10 @@ public static String typeToName(int token) { case SCRIPT: return "SCRIPT"; case TYPEOFNAME: return "TYPEOFNAME"; case USE_STACK: return "USE_STACK"; - case SETPROP_OP: return "SETPROP_OP"; - case SETELEM_OP: return "SETELEM_OP"; + case SETPROP_OP: return "SETPROP_OP"; + case STRICT_SETPROP_OP: return "STRICT_SETPROP_OP"; + case SETELEM_OP: return "SETELEM_OP"; + case STRICT_SETELEM_OP: return "SETELEM_OP"; case LOCAL_BLOCK: return "LOCAL_BLOCK"; case SET_REF_OP: return "SET_REF_OP"; case DOTDOT: return "DOTDOT"; diff --git a/src/org/mozilla/javascript/optimizer/Block.java b/src/org/mozilla/javascript/optimizer/Block.java index 09772eb5e0..44c79c6903 100644 --- a/src/org/mozilla/javascript/optimizer/Block.java +++ b/src/org/mozilla/javascript/optimizer/Block.java @@ -568,6 +568,8 @@ private static boolean findDefPoints(OptFunctionNode fn, Node n, break; case Token.SETPROP : case Token.SETPROP_OP : + case Token.STRICT_SETPROP : + case Token.STRICT_SETPROP_OP : if (child.getType() == Token.GETVAR) { int i = fn.getVarIndex(child); assignType(varTypes, i, Optimizer.AnyType); diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index d97825bc76..342cd2513a 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -2694,11 +2694,15 @@ private void generateExpression(Node node, Node parent) case Token.SETPROP: case Token.SETPROP_OP: + case Token.STRICT_SETPROP: + case Token.STRICT_SETPROP_OP: visitSetProp(type, node, child); break; case Token.SETELEM: case Token.SETELEM_OP: + case Token.STRICT_SETELEM: + case Token.STRICT_SETELEM_OP: visitSetElem(type, node, child); break; @@ -5057,13 +5061,13 @@ private void visitSetProp(int type, Node node, Node child) Node objectChild = child; generateExpression(child, node); child = child.getNext(); - if (type == Token.SETPROP_OP) { + if (type == Token.SETPROP_OP || type == Token.STRICT_SETPROP_OP) { cfw.add(ByteCode.DUP); } Node nameChild = child; generateExpression(child, node); child = child.getNext(); - if (type == Token.SETPROP_OP) { + if (type == Token.SETPROP_OP || type == Token.STRICT_SETPROP) { // stack: ... object object name -> ... object name object name cfw.add(ByteCode.DUP_X1); //for 'this.foo += ...' we call thisGet which can skip some @@ -5103,13 +5107,13 @@ private void visitSetElem(int type, Node node, Node child) { generateExpression(child, node); child = child.getNext(); - if (type == Token.SETELEM_OP) { + if (type == Token.SETELEM_OP || type == Token.STRICT_SETELEM_OP) { cfw.add(ByteCode.DUP); } generateExpression(child, node); child = child.getNext(); boolean indexIsNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1); - if (type == Token.SETELEM_OP) { + if (type == Token.SETELEM_OP || type == Token.STRICT_SETELEM_OP) { if (indexIsNumber) { // stack: ... object object number // -> ... object number object number diff --git a/src/org/mozilla/javascript/optimizer/Optimizer.java b/src/org/mozilla/javascript/optimizer/Optimizer.java index 2965f3ec7c..5a7d66701c 100644 --- a/src/org/mozilla/javascript/optimizer/Optimizer.java +++ b/src/org/mozilla/javascript/optimizer/Optimizer.java @@ -379,7 +379,9 @@ else if (convertParameter(rChild)) { } } case Token.SETELEM : - case Token.SETELEM_OP : { + case Token.SETELEM_OP : + case Token.STRICT_SETELEM : + case Token.STRICT_SETELEM_OP : { Node arrayBase = n.getFirstChild(); Node arrayIndex = arrayBase.getNext(); Node rValue = arrayIndex.getNext(); From 6ff3146f2b9742a92c8506781e9fbc49503bf256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 040/111] Handle STRICT_SET{PROP,ELEM} in interpreter --- src/org/mozilla/javascript/CodeGenerator.java | 10 ++++++++-- src/org/mozilla/javascript/Interpreter.java | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/org/mozilla/javascript/CodeGenerator.java b/src/org/mozilla/javascript/CodeGenerator.java index 8aa88c83db..30e978aa83 100644 --- a/src/org/mozilla/javascript/CodeGenerator.java +++ b/src/org/mozilla/javascript/CodeGenerator.java @@ -755,7 +755,10 @@ private void visitExpression(Node node, int contextFlags) stackChange(-1); } visitExpression(child, 0); - addStringOp(Token.SETPROP, property); + int op = (type == Token.SETPROP || type == Token.SETPROP_OP) + ? Token.SETPROP + : Token.STRICT_SETPROP; + addStringOp(op, property); stackChange(-1); } break; @@ -777,7 +780,10 @@ private void visitExpression(Node node, int contextFlags) stackChange(-1); } visitExpression(child, 0); - addToken(Token.SETELEM); + int op = (type == Token.SETELEM || type == Token.SETELEM_OP) + ? Token.SETELEM + : Token.STRICT_SETELEM; + addToken(op); stackChange(-2); break; diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index d8d6b3d37a..e515ca21d6 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -1418,7 +1418,9 @@ private static Object interpretLoop(Context cx, CallFrame frame, stack[stackTop] = ScriptRuntime.getObjectProp(lhs, stringReg, cx, frame.scope); continue Loop; } + case Token.STRICT_SETPROP : case Token.SETPROP : { + assert (op == Token.STRICT_SETPROP) == frame.idata.isStrict; Object rhs = stack[stackTop]; if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); --stackTop; @@ -1453,7 +1455,9 @@ private static Object interpretLoop(Context cx, CallFrame frame, stack[stackTop] = value; continue Loop; } + case Token.STRICT_SETELEM : case Token.SETELEM : { + assert (op == Token.STRICT_SETELEM) == frame.idata.isStrict; stackTop -= 2; Object rhs = stack[stackTop + 2]; if (rhs == DBL_MRK) { From 7a18e00191f6e3fef0f8f4d7f15dd4bf035cf1bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 041/111] Initial commit for strict assignment work --- src/org/mozilla/javascript/Interpreter.java | 12 ++++--- src/org/mozilla/javascript/NativeArray.java | 8 +++-- src/org/mozilla/javascript/Parser.java | 4 +-- src/org/mozilla/javascript/ScriptRuntime.java | 32 +++++++++++-------- src/org/mozilla/javascript/SpecialRef.java | 3 +- .../mozilla/javascript/optimizer/Codegen.java | 15 ++++++++- .../javascript/optimizer/OptRuntime.java | 6 ++-- 7 files changed, 54 insertions(+), 26 deletions(-) diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index e515ca21d6..f3570a6d36 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -1427,7 +1427,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); stack[stackTop] = ScriptRuntime.setObjectProp(lhs, stringReg, rhs, - cx); + frame.idata.isStrict, cx); continue Loop; } case Icode_PROP_INC_DEC : { @@ -1470,10 +1470,12 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object value; Object id = stack[stackTop + 1]; if (id != DBL_MRK) { - value = ScriptRuntime.setObjectElem(lhs, id, rhs, cx); + value = ScriptRuntime.setObjectElem(lhs, id, rhs, + frame.idata.isStrict, cx); } else { double d = sDbl[stackTop + 1]; - value = ScriptRuntime.setObjectIndex(lhs, d, rhs, cx); + value = ScriptRuntime.setObjectIndex(lhs, d, rhs, + frame.idata.isStrict, cx); } stack[stackTop] = value; continue Loop; @@ -1484,8 +1486,10 @@ private static Object interpretLoop(Context cx, CallFrame frame, --stackTop; Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + // TODO: checked flag! stack[stackTop] = ScriptRuntime.elemIncrDecr(lhs, rhs, cx, - iCode[frame.pc]); + iCode[frame.pc], + false); ++frame.pc; continue Loop; } diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index 48e0db1cb5..942a48c128 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -918,8 +918,9 @@ static long getLengthProperty(Context cx, Scriptable obj) { private static Object setLengthProperty(Context cx, Scriptable target, long length) { + // [[Put]](_, _, true) -> always throw error for invalid [[Put]] return ScriptRuntime.setObjectProp( - target, "length", ScriptRuntime.wrapNumber(length), cx); + target, "length", ScriptRuntime.wrapNumber(length), true, cx); } /* Utility functions to encapsulate index > Integer.MAX_VALUE @@ -966,11 +967,12 @@ private static void defineElem(Context cx, Scriptable target, long index, private static void setElem(Context cx, Scriptable target, long index, Object value) { + // [[Put]](_, _, true) -> always throw error for invalid [[Put]] if (index > Integer.MAX_VALUE) { String id = Long.toString(index); - ScriptRuntime.setObjectProp(target, id, value, cx); + ScriptRuntime.setObjectProp(target, id, value, true, cx); } else { - ScriptRuntime.setObjectIndex(target, (int)index, value, cx); + ScriptRuntime.setObjectIndex(target, (int)index, value, true, cx); } } diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index 300c82a801..6e46388a5f 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -3887,7 +3887,7 @@ protected Node simpleAssignment(Node left, Node right) { } int type; if (nodeType == Token.GETPROP) { - type = Token.SETPROP; + type = inUseStrictDirective ? Token.STRICT_SETPROP : Token.SETPROP; // TODO(stevey) - see https://bugzilla.mozilla.org/show_bug.cgi?id=492036 // The new AST code generates NAME tokens for GETPROP ids where the old parser // generated STRING nodes. If we don't set the type to STRING below, this will @@ -3895,7 +3895,7 @@ protected Node simpleAssignment(Node left, Node right) { // "var obj={p:3};[obj.p]=[9];" id.setType(Token.STRING); } else { - type = Token.SETELEM; + type = inUseStrictDirective ? Token.STRICT_SETELEM : Token.SETELEM; } return new Node(type, obj, id, right); } diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 0768f38210..0480e9e776 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -1598,20 +1598,21 @@ public static Object getObjectIndex(Scriptable obj, int index, } /* - * Call obj.[[Put]](id, value) + * Call obj.[[Put]](id, value, throw) */ public static Object setObjectElem(Object obj, Object elem, Object value, - Context cx) + boolean checked, Context cx) { Scriptable sobj = toObjectOrNull(cx, obj); if (sobj == null) { throw undefWriteError(obj, elem, value); } - return setObjectElem(sobj, elem, value, cx); + return setObjectElem(sobj, elem, value, checked, cx); } public static Object setObjectElem(Scriptable obj, Object elem, - Object value, Context cx) + Object value, boolean checked, + Context cx) { if (obj instanceof XMLObject) { ((XMLObject)obj).put(cx, elem, value); @@ -1632,17 +1633,19 @@ public static Object setObjectElem(Scriptable obj, Object elem, * Version of setObjectElem when elem is a valid JS identifier name. */ public static Object setObjectProp(Object obj, String property, - Object value, Context cx) + Object value, boolean checked, + Context cx) { Scriptable sobj = toObjectOrNull(cx, obj); if (sobj == null) { throw undefWriteError(obj, property, value); } - return setObjectProp(sobj, property, value, cx); + return setObjectProp(sobj, property, value, checked, cx); } public static Object setObjectProp(Scriptable obj, String property, - Object value, Context cx) + Object value, boolean checked, + Context cx) { ScriptableObject.putProperty(obj, property, value); return value; @@ -1653,7 +1656,8 @@ public static Object setObjectProp(Scriptable obj, String property, * types. */ public static Object setObjectIndex(Object obj, double dblIndex, - Object value, Context cx) + Object value, boolean checked, + Context cx) { Scriptable sobj = toObjectOrNull(cx, obj); if (sobj == null) { @@ -1662,15 +1666,15 @@ public static Object setObjectIndex(Object obj, double dblIndex, int index = (int)dblIndex; if (index == dblIndex) { - return setObjectIndex(sobj, index, value, cx); + return setObjectIndex(sobj, index, value, checked, cx); } else { String s = toString(dblIndex); - return setObjectProp(sobj, s, value, cx); + return setObjectProp(sobj, s, value, checked, cx); } } public static Object setObjectIndex(Scriptable obj, int index, Object value, - Context cx) + boolean checked, Context cx) { ScriptableObject.putProperty(obj, index, value); return value; @@ -2902,8 +2906,10 @@ private static Object doScriptableIncrDecr(Scriptable target, } } + // TODO: argument position for 'checked' public static Object elemIncrDecr(Object obj, Object index, - Context cx, int incrDecrMask) + Context cx, int incrDecrMask, + boolean checked) { Object value = getObjectElem(obj, index, cx); boolean post = ((incrDecrMask & Node.POST_FLAG) != 0); @@ -2923,7 +2929,7 @@ public static Object elemIncrDecr(Object obj, Object index, --number; } Number result = wrapNumber(number); - setObjectElem(obj, index, result, cx); + setObjectElem(obj, index, result, checked, cx); if (post) { return value; } else { diff --git a/src/org/mozilla/javascript/SpecialRef.java b/src/org/mozilla/javascript/SpecialRef.java index c393a9b0e4..93c471d277 100644 --- a/src/org/mozilla/javascript/SpecialRef.java +++ b/src/org/mozilla/javascript/SpecialRef.java @@ -105,7 +105,8 @@ public Object set(Context cx, Object value) { switch (type) { case SPECIAL_NONE: - return ScriptRuntime.setObjectProp(target, name, value, cx); + // TODO: default for 'checked' set to 'false' for now + return ScriptRuntime.setObjectProp(target, name, value, false, cx); case SPECIAL_PROTO: case SPECIAL_PARENT: { diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index 342cd2513a..8bb8fac841 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -4431,12 +4431,15 @@ private void visitIncDec(Node node) generateExpression(elemChild.getNext(), node); cfw.addALoad(contextLocal); cfw.addPush(incrDecrMask); + // TODO: checked flag! + cfw.add(ByteCode.ICONST_0); if (elemChild.getNext().getIntProp(Node.ISNUMBER_PROP, -1) != -1) { addOptRuntimeInvoke("elemIncrDecr", "(Ljava/lang/Object;" +"D" +"Lorg/mozilla/javascript/Context;" +"I" + +"Z" +")Ljava/lang/Object;"); } else { addScriptRuntimeInvoke("elemIncrDecr", @@ -4444,6 +4447,7 @@ private void visitIncDec(Node node) +"Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" +"I" + +"Z" +")Ljava/lang/Object;"); } break; @@ -5067,7 +5071,7 @@ private void visitSetProp(int type, Node node, Node child) Node nameChild = child; generateExpression(child, node); child = child.getNext(); - if (type == Token.SETPROP_OP || type == Token.STRICT_SETPROP) { + if (type == Token.SETPROP_OP || type == Token.STRICT_SETPROP_OP) { // stack: ... object object name -> ... object name object name cfw.add(ByteCode.DUP_X1); //for 'this.foo += ...' we call thisGet which can skip some @@ -5093,12 +5097,16 @@ private void visitSetProp(int type, Node node, Node child) } } generateExpression(child, node); + assert (type == Token.STRICT_SETPROP || type == Token.STRICT_SETPROP_OP) + == scriptOrFn.isInStrictMode(); + cfw.add(scriptOrFn.isInStrictMode() ? ByteCode.ICONST_1 : ByteCode.ICONST_0); cfw.addALoad(contextLocal); addScriptRuntimeInvoke( "setObjectProp", "(Ljava/lang/Object;" +"Ljava/lang/String;" +"Ljava/lang/Object;" + +"Z" +"Lorg/mozilla/javascript/Context;" +")Ljava/lang/Object;"); } @@ -5138,6 +5146,9 @@ private void visitSetElem(int type, Node node, Node child) } } generateExpression(child, node); + assert (type == Token.STRICT_SETELEM || type == Token.STRICT_SETELEM_OP) + == scriptOrFn.isInStrictMode(); + cfw.add(scriptOrFn.isInStrictMode() ? ByteCode.ICONST_1 : ByteCode.ICONST_0); cfw.addALoad(contextLocal); if (indexIsNumber) { addScriptRuntimeInvoke( @@ -5145,6 +5156,7 @@ private void visitSetElem(int type, Node node, Node child) "(Ljava/lang/Object;" +"D" +"Ljava/lang/Object;" + +"Z" +"Lorg/mozilla/javascript/Context;" +")Ljava/lang/Object;"); } else { @@ -5153,6 +5165,7 @@ private void visitSetElem(int type, Node node, Node child) "(Ljava/lang/Object;" +"Ljava/lang/Object;" +"Ljava/lang/Object;" + +"Z" +"Lorg/mozilla/javascript/Context;" +")Ljava/lang/Object;"); } diff --git a/src/org/mozilla/javascript/optimizer/OptRuntime.java b/src/org/mozilla/javascript/optimizer/OptRuntime.java index c56cd629b3..75f43be62e 100644 --- a/src/org/mozilla/javascript/optimizer/OptRuntime.java +++ b/src/org/mozilla/javascript/optimizer/OptRuntime.java @@ -151,11 +151,13 @@ public static Object add(double val1, Object val2) return new ConsString(toString(val1), (CharSequence)val2); } + // TODO: argument position for 'checked' public static Object elemIncrDecr(Object obj, double index, - Context cx, int incrDecrMask) + Context cx, int incrDecrMask, + boolean checked) { return ScriptRuntime.elemIncrDecr(obj, new Double(index), cx, - incrDecrMask); + incrDecrMask, checked); } public static Object[] padStart(Object[] currentArgs, int count) { From 6149ac57cb04e11616fc4d7cf740d4a5516ceb32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 042/111] Add static [[Put]] method with 'throw'-flag per ES5 to ScriptableObject; Deprecated the old implementation --- .../javascript/xml/impl/xmlbeans/XMLCtor.java | 2 +- examples/RunScript2.java | 2 +- src/org/mozilla/javascript/NativeError.java | 14 ++--- src/org/mozilla/javascript/ScriptRuntime.java | 14 ++--- .../mozilla/javascript/ScriptableObject.java | 48 +++++++++++++++++ .../javascript/commonjs/module/Require.java | 4 +- .../mozilla/javascript/tests/Bug688023.java | 52 +++++++++++++++++++ .../tests/WriteReadOnlyPropertyTest.java | 2 +- .../tests/commonjs/module/ComplianceTest.java | 2 +- .../tests/commonjs/module/RequireJarTest.java | 2 +- .../tests/commonjs/module/RequireTest.java | 2 +- .../javascript/tools/shell/Global.java | 4 +- .../mozilla/javascript/xmlimpl/XMLCtor.java | 2 +- 13 files changed, 125 insertions(+), 25 deletions(-) create mode 100755 testsrc/org/mozilla/javascript/tests/Bug688023.java diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLCtor.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLCtor.java index 6c74f96cbe..0c55619442 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLCtor.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLCtor.java @@ -61,7 +61,7 @@ private void writeSetting(Scriptable target) int id = super.getMaxInstanceId() + i; String name = getInstanceIdName(id); Object value = getInstanceIdValue(id); - ScriptableObject.putProperty(target, name, value); + ScriptableObject.putProperty(target, name, value, false); } } diff --git a/examples/RunScript2.java b/examples/RunScript2.java index cb02896507..e2d0e076bf 100644 --- a/examples/RunScript2.java +++ b/examples/RunScript2.java @@ -52,7 +52,7 @@ public static void main(String args[]) // Add a global variable "out" that is a JavaScript reflection // of System.out Object jsOut = Context.javaToJS(System.out, scope); - ScriptableObject.putProperty(scope, "out", jsOut); + ScriptableObject.putProperty(scope, "out", jsOut, false); String s = ""; for (int i=0; i < args.length; i++) { diff --git a/src/org/mozilla/javascript/NativeError.java b/src/org/mozilla/javascript/NativeError.java index a221b9aa35..6f27611943 100644 --- a/src/org/mozilla/javascript/NativeError.java +++ b/src/org/mozilla/javascript/NativeError.java @@ -57,10 +57,10 @@ final class NativeError extends IdScriptableObject static void init(Scriptable scope, boolean sealed) { NativeError obj = new NativeError(); - ScriptableObject.putProperty(obj, "name", "Error"); - ScriptableObject.putProperty(obj, "message", ""); - ScriptableObject.putProperty(obj, "fileName", ""); - ScriptableObject.putProperty(obj, "lineNumber", Integer.valueOf(0)); + ScriptableObject.putProperty(obj, "name", "Error", false); + ScriptableObject.putProperty(obj, "message", "", false); + ScriptableObject.putProperty(obj, "fileName", "", false); + ScriptableObject.putProperty(obj, "lineNumber", Integer.valueOf(0), false); obj.setAttributes("name", ScriptableObject.DONTENUM); obj.setAttributes("message", ScriptableObject.DONTENUM); obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); @@ -79,14 +79,14 @@ static NativeError make(Context cx, Scriptable scope, if (arglen >= 1) { if (args[0] != Undefined.instance) { ScriptableObject.putProperty(obj, "message", - ScriptRuntime.toString(args[0])); + ScriptRuntime.toString(args[0]), false); } if (arglen >= 2) { - ScriptableObject.putProperty(obj, "fileName", args[1]); + ScriptableObject.putProperty(obj, "fileName", args[1], false); if (arglen >= 3) { int line = ScriptRuntime.toInt32(args[2]); ScriptableObject.putProperty(obj, "lineNumber", - Integer.valueOf(line)); + Integer.valueOf(line), false); } } } diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 0480e9e776..1ff3b61a89 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -1620,9 +1620,9 @@ public static Object setObjectElem(Scriptable obj, Object elem, String s = toStringIdOrIndex(cx, elem); if (s == null) { int index = lastIndexResult(cx); - ScriptableObject.putProperty(obj, index, value); + ScriptableObject.putProperty(obj, index, value, checked); } else { - ScriptableObject.putProperty(obj, s, value); + ScriptableObject.putProperty(obj, s, value, checked); } } @@ -1647,7 +1647,7 @@ public static Object setObjectProp(Scriptable obj, String property, Object value, boolean checked, Context cx) { - ScriptableObject.putProperty(obj, property, value); + ScriptableObject.putProperty(obj, property, value, checked); return value; } @@ -1676,7 +1676,7 @@ public static Object setObjectIndex(Object obj, double dblIndex, public static Object setObjectIndex(Scriptable obj, int index, Object value, boolean checked, Context cx) { - ScriptableObject.putProperty(obj, index, value); + ScriptableObject.putProperty(obj, index, value, checked); return value; } @@ -1951,7 +1951,7 @@ public static Object setName(Scriptable bound, Object value, if (bound != null) { // TODO: we used to special-case XMLObject here, but putProperty // seems to work for E4X and it's better to optimize the common case - ScriptableObject.putProperty(bound, id, value); + ScriptableObject.putProperty(bound, id, value, false); } else { // "newname = 7;", where 'newname' has not yet // been defined, creates a new property in the @@ -1983,7 +1983,7 @@ public static Object strictSetName(Scriptable bound, Object value, // false. In these cases a TypeError exception is thrown (11.13.1). // TODO: we used to special-case XMLObject here, but putProperty // seems to work for E4X and we should optimize the common case - ScriptableObject.putProperty(bound, id, value); + ScriptableObject.putProperty(bound, id, value, true); return value; } else { // See ES5 8.7.2 @@ -3555,7 +3555,7 @@ public static Scriptable newCatchScope(Throwable t, } Scriptable errorObject = cx.newObject(scope, errorName, args); - ScriptableObject.putProperty(errorObject, "name", errorName); + ScriptableObject.putProperty(errorObject, "name", errorName, false); // set exception in Error objects to enable non-ECMA "stack" property if (errorObject instanceof NativeError) { ((NativeError) errorObject).setStackProvider(re); diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 0373ba190d..935fffec70 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -2151,8 +2151,32 @@ public static boolean hasProperty(Scriptable obj, int index) * @param name a property name * @param value any JavaScript value accepted by Scriptable.put * @since 1.5R2 + * @deprecated use {@link #putProperty(Scriptable, String, Object, boolean)} instead */ + @Deprecated public static void putProperty(Scriptable obj, String name, Object value) + { + putProperty(obj, name, value, false); + } + + /** + * Puts a named property in an object or in an object in its prototype chain. + *

+ * Searches for the named property in the prototype chain. If it is found, + * the value of the property in obj is changed through a call + * to {@link Scriptable#put(String, Scriptable, Object)} on the + * prototype passing obj as the start argument. + * This allows the prototype to veto the property setting in case the + * prototype defines the property with [[ReadOnly]] attribute. If the + * property is not found, it is added in obj. + * @param obj a JavaScript object + * @param name a property name + * @param value any JavaScript value accepted by Scriptable.put + * @param checked controls whether to throw an error if the attribute is [[ReadOnly]] + * @since 1.7R4 + */ + public static void putProperty(Scriptable obj, String name, Object value, + boolean checked) { Scriptable base = getBase(obj, name); if (base == null) @@ -2198,8 +2222,32 @@ public static void putConstProperty(Scriptable obj, String name, Object value) * @param index a property index * @param value any JavaScript value accepted by Scriptable.put * @since 1.5R2 + * @deprecated use {@link #putProperty(Scriptable, int, Object, boolean)} instead */ + @Deprecated public static void putProperty(Scriptable obj, int index, Object value) + { + putProperty(obj, index, value, false); + } + + /** + * Puts an indexed property in an object or in an object in its prototype chain. + *

+ * Searches for the indexed property in the prototype chain. If it is found, + * the value of the property in obj is changed through a call + * to {@link Scriptable#put(int, Scriptable, Object)} on the prototype + * passing obj as the start argument. This allows + * the prototype to veto the property setting in case the prototype defines + * the property with [[ReadOnly]] attribute. If the property is not found, + * it is added in obj. + * @param obj a JavaScript object + * @param index a property index + * @param value any JavaScript value accepted by Scriptable.put + * @param checked controls whether to throw an error if the attribute is [[ReadOnly]] + * @since 1.7R4 + */ + public static void putProperty(Scriptable obj, int index, Object value, + boolean checked) { Scriptable base = getBase(obj, index); if (base == null) diff --git a/src/org/mozilla/javascript/commonjs/module/Require.java b/src/org/mozilla/javascript/commonjs/module/Require.java index 5734a27510..61bb372b6e 100644 --- a/src/org/mozilla/javascript/commonjs/module/Require.java +++ b/src/org/mozilla/javascript/commonjs/module/Require.java @@ -166,7 +166,7 @@ public Scriptable requireMain(Context cx, String mainModuleId) { * @param scope the scope where the require() function is to be installed. */ public void install(Scriptable scope) { - ScriptableObject.putProperty(scope, "require", this); + ScriptableObject.putProperty(scope, "require", this, false); } @Override @@ -350,7 +350,7 @@ private static void executeOptionalScript(Script script, Context cx, private static void defineReadOnlyProperty(ScriptableObject obj, String name, Object value) { - ScriptableObject.putProperty(obj, name, value); + ScriptableObject.putProperty(obj, name, value, false); obj.setAttributes(name, ScriptableObject.READONLY | ScriptableObject.PERMANENT); } diff --git a/testsrc/org/mozilla/javascript/tests/Bug688023.java b/testsrc/org/mozilla/javascript/tests/Bug688023.java new file mode 100755 index 0000000000..4669afbb84 --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/Bug688023.java @@ -0,0 +1,52 @@ +/** + * + */ +package org.mozilla.javascript.tests; + +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mozilla.javascript.CompilerEnvirons; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ErrorReporter; +import org.mozilla.javascript.Parser; +import org.mozilla.javascript.ast.AstRoot; + +/** + * @author André Bargull + * + */ +public class Bug688023 { + private Context cx; + + @Before + public void setUp() { + cx = Context.enter(); + cx.setLanguageVersion(Context.VERSION_1_8); + } + + @After + public void tearDown() { + Context.exit(); + } + + private AstRoot parse(CharSequence cs) { + CompilerEnvirons compilerEnv = new CompilerEnvirons(); + compilerEnv.initFromContext(cx); + ErrorReporter compilationErrorReporter = compilerEnv.getErrorReporter(); + Parser p = new Parser(compilerEnv, compilationErrorReporter); + return p.parse(cs.toString(), "", 1); + } + + private String toSource(CharSequence cs) { + return parse(cs).toSource(); + } + + @Test + public void testToSource() { + assertEquals("if (c) \na = 1;\n", toSource("if (c) a=1;")); + } + +} diff --git a/testsrc/org/mozilla/javascript/tests/WriteReadOnlyPropertyTest.java b/testsrc/org/mozilla/javascript/tests/WriteReadOnlyPropertyTest.java index ac99950b7b..7a3218ee5e 100644 --- a/testsrc/org/mozilla/javascript/tests/WriteReadOnlyPropertyTest.java +++ b/testsrc/org/mozilla/javascript/tests/WriteReadOnlyPropertyTest.java @@ -52,7 +52,7 @@ void testWriteReadOnly(final boolean acceptWriteReadOnly) throws Exception { public Object run(final Context cx) { final ScriptableObject top = cx.initStandardObjects(); - ScriptableObject.putProperty(top, "foo", foo); + ScriptableObject.putProperty(top, "foo", foo, false); cx.evaluateString(top, script, "script", 0, null); return null; diff --git a/testsrc/org/mozilla/javascript/tests/commonjs/module/ComplianceTest.java b/testsrc/org/mozilla/javascript/tests/commonjs/module/ComplianceTest.java index 1a2cbada6a..4a2b12cb1c 100644 --- a/testsrc/org/mozilla/javascript/tests/commonjs/module/ComplianceTest.java +++ b/testsrc/org/mozilla/javascript/tests/commonjs/module/ComplianceTest.java @@ -62,7 +62,7 @@ public void runBare() throws Throwable { try { cx.setOptimizationLevel(-1); final Scriptable scope = cx.initStandardObjects(); - ScriptableObject.putProperty(scope, "print", new Print(scope)); + ScriptableObject.putProperty(scope, "print", new Print(scope), false); createRequire(testDir, cx, scope).requireMain(cx, "program"); } finally { diff --git a/testsrc/org/mozilla/javascript/tests/commonjs/module/RequireJarTest.java b/testsrc/org/mozilla/javascript/tests/commonjs/module/RequireJarTest.java index c4d45efaaa..b5bd5216c6 100644 --- a/testsrc/org/mozilla/javascript/tests/commonjs/module/RequireJarTest.java +++ b/testsrc/org/mozilla/javascript/tests/commonjs/module/RequireJarTest.java @@ -49,7 +49,7 @@ public void testNonSandboxed() throws Exception final Scriptable scope = cx.initStandardObjects(); final Require require = getSandboxedRequire(cx, scope, false); final String jsFile = getClass().getResource("testNonSandboxed.js").toExternalForm(); - ScriptableObject.putProperty(scope, "moduleUri", jsFile); + ScriptableObject.putProperty(scope, "moduleUri", jsFile, false); require.requireMain(cx, "testNonSandboxed"); } diff --git a/testsrc/org/mozilla/javascript/tests/commonjs/module/RequireTest.java b/testsrc/org/mozilla/javascript/tests/commonjs/module/RequireTest.java index 233cb47fd3..08354dbdb6 100644 --- a/testsrc/org/mozilla/javascript/tests/commonjs/module/RequireTest.java +++ b/testsrc/org/mozilla/javascript/tests/commonjs/module/RequireTest.java @@ -51,7 +51,7 @@ public void testNonSandboxed() throws Exception final Scriptable scope = cx.initStandardObjects(); final Require require = getSandboxedRequire(cx, scope, false); final String jsFile = getClass().getResource("testNonSandboxed.js").toExternalForm(); - ScriptableObject.putProperty(scope, "moduleUri", jsFile); + ScriptableObject.putProperty(scope, "moduleUri", jsFile, false); require.requireMain(cx, "testNonSandboxed"); } diff --git a/toolsrc/org/mozilla/javascript/tools/shell/Global.java b/toolsrc/org/mozilla/javascript/tools/shell/Global.java index 2a136599f7..c85f3c7949 100644 --- a/toolsrc/org/mozilla/javascript/tools/shell/Global.java +++ b/toolsrc/org/mozilla/javascript/tools/shell/Global.java @@ -761,11 +761,11 @@ public static Object runCommand(Context cx, Scriptable thisObj, int exitCode = runProcess(cmd, environment, in, out, err); if (outBytes != null) { String s = ScriptRuntime.toString(outObj) + outBytes.toString(); - ScriptableObject.putProperty(params, "output", s); + ScriptableObject.putProperty(params, "output", s, false); } if (errBytes != null) { String s = ScriptRuntime.toString(errObj) + errBytes.toString(); - ScriptableObject.putProperty(params, "err", s); + ScriptableObject.putProperty(params, "err", s, false); } return new Integer(exitCode); diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java index 5ddfa013e5..2fabc3e22f 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java @@ -64,7 +64,7 @@ private void writeSetting(Scriptable target) int id = super.getMaxInstanceId() + i; String name = getInstanceIdName(id); Object value = getInstanceIdValue(id); - ScriptableObject.putProperty(target, name, value); + ScriptableObject.putProperty(target, name, value, false); } } From 9f6b535cce1e58732778d279763a7930bce9b182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 043/111] API-Update: Add 'throw' flag from 8.12.5 [[Put]] to Scriptable#put(...) Scriptable#put(int, Scriptable, Object) -> #put(int, Scriptable, Object, boolean) Scriptable#put(String, Scriptable, Object) -> #put(String, Scriptable, Object, boolean) ScriptableObject#put(_, Scriptable, Object) is now marked final and delegates to the new method with the 'throw' flag set to false. Other callers to the old put() may either call with 'throw' set to 'true' or 'false'. The decision which one is to be used, was made using the following rules: - code in "deprecatedsrc", "examples", "testsrc", "toolsrc" and "xmlimplsrc" calls put() with 'throw' to false, since no special rules need to be applied for this code - installing default values generally uses 'false' (e.g. 'constructor' or 'prototype' property) - Java mapper classes set throw to 'false' as well - yet unknown situations are marked with TODO Arguments#putIntoActivation(): 'throw' is set to false, because this code is only valid for non-strict mode BaseFunction#setInstanceIdValue(): 'arguments' can only be set for non-strict code, so 'throw' is always false at this point NativeArray#{define,set}Elem(): 'throw' flag is set according to spec NativeJSON: 'throw' flag is set to false per spec JSONParser: 'throw' flag is set to false per spec PropertyDescriptor: 'throw' flag is set to false per spec ScriptRuntime: NativeRegExp: RegExp.prototype.exec defaults 'throw' flag to true RegExpImpl: String.prototype.{match,split) default throw to false --- .../javascript/xml/impl/xmlbeans/XML.java | 2 +- .../javascript/xml/impl/xmlbeans/XMLList.java | 2 +- .../xml/impl/xmlbeans/XMLObjectImpl.java | 4 +- examples/DynamicScopes.java | 2 +- examples/Matrix.java | 4 +- examples/RunScript4.java | 2 +- examples/Shell.java | 2 +- src/org/mozilla/javascript/Arguments.java | 7 +- src/org/mozilla/javascript/BaseFunction.java | 5 +- src/org/mozilla/javascript/ConsString.java | 3 +- src/org/mozilla/javascript/Delegator.java | 12 +- .../mozilla/javascript/FunctionObject.java | 4 +- .../mozilla/javascript/IdFunctionObject.java | 2 +- .../javascript/IdScriptableObject.java | 19 +-- .../mozilla/javascript/ImporterTopLevel.java | 2 +- src/org/mozilla/javascript/Interpreter.java | 3 +- .../javascript/JavaScriptException.java | 4 +- src/org/mozilla/javascript/NativeArray.java | 18 ++- src/org/mozilla/javascript/NativeCall.java | 10 +- src/org/mozilla/javascript/NativeError.java | 2 +- src/org/mozilla/javascript/NativeGlobal.java | 12 +- .../mozilla/javascript/NativeIterator.java | 2 +- src/org/mozilla/javascript/NativeJSON.java | 12 +- .../mozilla/javascript/NativeJavaArray.java | 4 +- .../mozilla/javascript/NativeJavaClass.java | 2 +- .../mozilla/javascript/NativeJavaObject.java | 6 +- .../mozilla/javascript/NativeJavaPackage.java | 8 +- .../javascript/NativeJavaTopPackage.java | 4 +- src/org/mozilla/javascript/NativeMath.java | 2 +- src/org/mozilla/javascript/NativeNumber.java | 10 +- src/org/mozilla/javascript/NativeString.java | 4 +- src/org/mozilla/javascript/NativeWith.java | 8 +- .../javascript/PropertyDescriptor.java | 12 +- .../mozilla/javascript/RhinoException.java | 1 - src/org/mozilla/javascript/ScriptRuntime.java | 53 ++++--- .../javascript/ScriptStackElement.java | 1 + src/org/mozilla/javascript/Scriptable.java | 6 +- .../mozilla/javascript/ScriptableObject.java | 131 ++++++++++++++++-- .../ast/ArrayComprehensionLoop.java | 2 + src/org/mozilla/javascript/ast/AstNode.java | 2 + src/org/mozilla/javascript/ast/Name.java | 2 + .../javascript/commonjs/module/Require.java | 6 +- .../mozilla/javascript/json/JsonParser.java | 4 +- .../javascript/optimizer/OptRuntime.java | 2 +- .../javascript/regexp/NativeRegExp.java | 14 +- .../mozilla/javascript/regexp/RegExpImpl.java | 8 +- .../tests/ContinuationsApiTest.java | 6 +- .../CustomSetterAcceptNullScriptableTest.java | 2 +- .../javascript/tests/DefineClassTest.java | 4 +- .../mozilla/javascript/tests/Evaluator.java | 2 +- .../tests/GeneratedMethodNameTest.java | 2 +- .../javascript/tests/NativeArrayTest.java | 30 ++-- .../PrimitiveTypeScopeResolutionTest.java | 6 +- .../mozilla/javascript/tests/TypeOfTest.java | 4 +- .../ObjectGetOwnPropertyDescriptorTest.java | 4 +- .../es5/ObjectGetOwnPropertyNamesTest.java | 4 +- .../javascript/tests/es5/ObjectKeysTest.java | 6 +- .../javascript/tools/shell/Environment.java | 4 +- .../javascript/tools/shell/Global.java | 4 +- .../mozilla/javascript/tools/shell/Main.java | 4 +- .../org/mozilla/javascript/xmlimpl/XML.java | 2 +- .../mozilla/javascript/xmlimpl/XMLList.java | 2 +- .../javascript/xmlimpl/XMLObjectImpl.java | 4 +- 63 files changed, 324 insertions(+), 193 deletions(-) diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java index cffbc06aeb..f95c94187d 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java @@ -1313,7 +1313,7 @@ else if (xmlName.uri() == null && * @param start * @param value */ - public void put(int index, Scriptable start, Object value) + public void put(int index, Scriptable start, Object value, boolean checked) { // Spec says assignment to indexed XML object should return type error throw ScriptRuntime.typeError("Assignment to indexed XML is not allowed"); diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java index 24152c3cc3..d8640a0c0f 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java @@ -428,7 +428,7 @@ Object getXMLProperty(XMLName name) * @param index * @param value */ - public void put(int index, Scriptable start, Object value) + public void put(int index, Scriptable start, Object value, boolean checked) { Object parent = Undefined.instance; // Convert text into XML if needed. diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java index fd65f97f25..bcb54cfa0b 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java @@ -233,14 +233,14 @@ public final void put(Context cx, Object id, Object value) if (xmlName == null) { long index = ScriptRuntime.lastUint32Result(cx); // XXX Fix this cast - put((int)index, this, value); + put((int)index, this, value, false); return; } putXMLProperty(xmlName, value); } @Override - public void put(String name, Scriptable start, Object value) { + public void put(String name, Scriptable start, Object value, boolean checked) { Context cx = Context.getCurrentContext(); putXMLProperty(lib.toXMLNameFromString(cx, name), value); } diff --git a/examples/DynamicScopes.java b/examples/DynamicScopes.java index 0e3760bd7e..99a5239b0f 100644 --- a/examples/DynamicScopes.java +++ b/examples/DynamicScopes.java @@ -190,7 +190,7 @@ public void run() { // Create a JavaScript property of the thread scope named // 'x' and save a value for it. - threadScope.put("x", threadScope, x); + threadScope.put("x", threadScope, x, false); cx.evaluateString(threadScope, source, "threadScript", 1, null); } finally { Context.exit(); diff --git a/examples/Matrix.java b/examples/Matrix.java index ad56709c5f..8d0fed0f56 100644 --- a/examples/Matrix.java +++ b/examples/Matrix.java @@ -177,7 +177,7 @@ public Object get(int index, Scriptable start) { * * We do nothing here, so all properties are effectively read-only. */ - public void put(String name, Scriptable start, Object value) { + public void put(String name, Scriptable start, Object value, boolean checked) { } /** @@ -185,7 +185,7 @@ public void put(String name, Scriptable start, Object value) { * * We do nothing here, so all properties are effectively read-only. */ - public void put(int index, Scriptable start, Object value) { + public void put(int index, Scriptable start, Object value, boolean checked) { } /** diff --git a/examples/RunScript4.java b/examples/RunScript4.java index bd3d6f4181..914ea405ea 100644 --- a/examples/RunScript4.java +++ b/examples/RunScript4.java @@ -61,7 +61,7 @@ public static void main(String args[]) // myCounter = new Counter(7); Object[] arg = { new Integer(7) }; Scriptable myCounter = cx.newObject(scope, "Counter", arg); - scope.put("myCounter", scope, myCounter); + scope.put("myCounter", scope, myCounter, false); String s = ""; for (int i=0; i < args.length; i++) { diff --git a/examples/Shell.java b/examples/Shell.java index 8b8023a5a6..e5406530a2 100644 --- a/examples/Shell.java +++ b/examples/Shell.java @@ -93,7 +93,7 @@ public static void main(String args[]) { } Scriptable argsObj = cx.newArray(shell, array); shell.defineProperty("arguments", argsObj, - ScriptableObject.DONTENUM); + ScriptableObject.DONTENUM, false); shell.processSource(cx, args.length == 0 ? null : args[0]); } finally { diff --git a/src/org/mozilla/javascript/Arguments.java b/src/org/mozilla/javascript/Arguments.java index 10ba243e51..4b76212e56 100644 --- a/src/org/mozilla/javascript/Arguments.java +++ b/src/org/mozilla/javascript/Arguments.java @@ -95,7 +95,8 @@ private Object arg(int index) { private void putIntoActivation(int index, Object value) { String argName = activation.function.getParamOrVarName(index); - activation.put(argName, activation, value); + // checked = false because we're never in strict-mode for this call + activation.put(argName, activation, value, false); } private Object getFromActivation(int index) { @@ -173,10 +174,10 @@ private boolean sharedWithActivation(int index) } @Override - public void put(int index, Scriptable start, Object value) + public void put(int index, Scriptable start, Object value, boolean checked) { if (arg(index) == NOT_FOUND) { - super.put(index, start, value); + super.put(index, start, value, checked); } else { replaceArg(index, value); } diff --git a/src/org/mozilla/javascript/BaseFunction.java b/src/org/mozilla/javascript/BaseFunction.java index 1fc3e74297..4c8efde51e 100644 --- a/src/org/mozilla/javascript/BaseFunction.java +++ b/src/org/mozilla/javascript/BaseFunction.java @@ -215,7 +215,8 @@ protected void setInstanceIdValue(int id, Object value) Kit.codeBug(); } if (defaultHas("arguments")) { - defaultPut("arguments", value); + // check=false since we cannot reach this code in strict-mode + defaultPut("arguments", value, false); } else if ((argumentsAttributes & READONLY) == 0) { argumentsObj = value; } @@ -507,7 +508,7 @@ private synchronized Object setupDefaultPrototype() { } NativeObject obj = new NativeObject(); final int attr = ScriptableObject.DONTENUM; - obj.defineProperty("constructor", this, attr); + obj.defineProperty("constructor", this, attr, false); // put the prototype property into the object now, then in the // wacky case of a user defining a function Object(), we don't // get an infinite loop trying to find the prototype. diff --git a/src/org/mozilla/javascript/ConsString.java b/src/org/mozilla/javascript/ConsString.java index cc4685ec6d..869a42ae7f 100644 --- a/src/org/mozilla/javascript/ConsString.java +++ b/src/org/mozilla/javascript/ConsString.java @@ -85,7 +85,8 @@ public ConsString(CharSequence str1, CharSequence str2) { private Object writeReplace() { return this.toString(); } - + + @Override public String toString() { return depth == 0 ? (String)s1 : flatten(); } diff --git a/src/org/mozilla/javascript/Delegator.java b/src/org/mozilla/javascript/Delegator.java index 89841e6396..459e4d3764 100644 --- a/src/org/mozilla/javascript/Delegator.java +++ b/src/org/mozilla/javascript/Delegator.java @@ -143,16 +143,16 @@ public boolean has(int index, Scriptable start) { return obj.has(index,start); } /** - * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object) + * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object, boolean) */ - public void put(String name, Scriptable start, Object value) { - obj.put(name,start,value); + public void put(String name, Scriptable start, Object value, boolean checked) { + obj.put(name,start,value,checked); } /** - * @see org.mozilla.javascript.Scriptable#put(int, Scriptable, Object) + * @see org.mozilla.javascript.Scriptable#put(int, Scriptable, Object, boolean) */ - public void put(int index, Scriptable start, Object value) { - obj.put(index,start,value); + public void put(int index, Scriptable start, Object value, boolean checked) { + obj.put(index,start,value,checked); } /** * @see org.mozilla.javascript.Scriptable#delete(String) diff --git a/src/org/mozilla/javascript/FunctionObject.java b/src/org/mozilla/javascript/FunctionObject.java index 8c18a21b7d..f5389c565c 100644 --- a/src/org/mozilla/javascript/FunctionObject.java +++ b/src/org/mozilla/javascript/FunctionObject.java @@ -358,7 +358,7 @@ public void addAsConstructor(Scriptable scope, Scriptable prototype) { initAsConstructor(scope, prototype); defineProperty(scope, prototype.getClassName(), - this, ScriptableObject.DONTENUM); + this, ScriptableObject.DONTENUM, false); } void initAsConstructor(Scriptable scope, Scriptable prototype) @@ -371,7 +371,7 @@ void initAsConstructor(Scriptable scope, Scriptable prototype) defineProperty(prototype, "constructor", this, ScriptableObject.DONTENUM | ScriptableObject.PERMANENT | - ScriptableObject.READONLY); + ScriptableObject.READONLY, false); setParentScope(scope); } diff --git a/src/org/mozilla/javascript/IdFunctionObject.java b/src/org/mozilla/javascript/IdFunctionObject.java index 0930b7d615..fd7b28db97 100644 --- a/src/org/mozilla/javascript/IdFunctionObject.java +++ b/src/org/mozilla/javascript/IdFunctionObject.java @@ -101,7 +101,7 @@ public final void markAsConstructor(Scriptable prototypeProperty) public final void addAsProperty(Scriptable target) { ScriptableObject.defineProperty(target, functionName, this, - ScriptableObject.DONTENUM); + ScriptableObject.DONTENUM, false); } public void exportAsScopeProperty() diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index c2a2277070..abcdc8ea91 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -193,7 +193,7 @@ final Object get(int id) return value; } - final void set(int id, Scriptable start, Object value) + final void set(int id, Scriptable start, Object value, boolean checked) { if (value == NOT_FOUND) throw new IllegalArgumentException(); ensureId(id); @@ -211,7 +211,7 @@ final void set(int id, Scriptable start, Object value) else { int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; String name = (String)valueArray[nameSlot]; - start.put(name, start, value); + start.put(name, start, value, checked); } } } @@ -332,9 +332,9 @@ protected final Object defaultGet(String name) return super.get(name, this); } - protected final void defaultPut(String name, Object value) + protected final void defaultPut(String name, Object value, boolean checked) { - super.put(name, this, value); + super.put(name, this, value, checked); } @Override @@ -384,7 +384,7 @@ public Object get(String name, Scriptable start) } @Override - public void put(String name, Scriptable start, Object value) + public void put(String name, Scriptable start, Object value, boolean checked) { int info = findInstanceIdInfo(name); if (info != 0) { @@ -399,7 +399,7 @@ public void put(String name, Scriptable start, Object value) setInstanceIdValue(id, value); } else { - start.put(name, start, value); + start.put(name, start, value, checked); } } return; @@ -411,11 +411,11 @@ public void put(String name, Scriptable start, Object value) throw Context.reportRuntimeError1("msg.modify.sealed", name); } - prototypeValues.set(id, start, value); + prototypeValues.set(id, start, value, checked); return; } } - super.put(name, start, value); + super.put(name, start, value, checked); } @Override @@ -797,7 +797,8 @@ protected void updateOwnProperty(String name, PropertyDescriptor desc, prototypeValues.delete(id); // it will be replaced with a slot } else { if (desc.hasValue()) { - prototypeValues.set(id, this, desc.getValue()); + // save to pass 'false' b/c read-only check already passed + prototypeValues.set(id, this, desc.getValue(), false); } prototypeValues.setAttributes(id, attributes); return; diff --git a/src/org/mozilla/javascript/ImporterTopLevel.java b/src/org/mozilla/javascript/ImporterTopLevel.java index 3a4a96560b..e28d05d6ce 100644 --- a/src/org/mozilla/javascript/ImporterTopLevel.java +++ b/src/org/mozilla/javascript/ImporterTopLevel.java @@ -238,7 +238,7 @@ private void importClass(NativeJavaClass cl) throw Context.reportRuntimeError1("msg.prop.defined", n); } //defineProperty(n, cl, DONTENUM); - put(n, this, cl); + put(n, this, cl, false); } @Override diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index f3570a6d36..9ac28f7ef2 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -1853,7 +1853,8 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object val = stack[stackTop]; if (val == DBL_MRK) val = ScriptRuntime.wrapNumber(sDbl[stackTop]); stringReg = frame.idata.argNames[indexReg]; - frame.scope.put(stringReg, frame.scope, val); + // TODO: check -> 'false'? + frame.scope.put(stringReg, frame.scope, val, false); } continue Loop; case Icode_GETVAR1: diff --git a/src/org/mozilla/javascript/JavaScriptException.java b/src/org/mozilla/javascript/JavaScriptException.java index d42e768f6c..448df47389 100644 --- a/src/org/mozilla/javascript/JavaScriptException.java +++ b/src/org/mozilla/javascript/JavaScriptException.java @@ -77,10 +77,10 @@ public JavaScriptException(Object value, String sourceName, int lineNumber) .hasFeature(Context.FEATURE_LOCATION_INFORMATION_IN_ERROR)) { NativeError error = (NativeError) value; if (!error.has("fileName", error)) { - error.put("fileName", error, sourceName); + error.put("fileName", error, sourceName, false); } if (!error.has("lineNumber", error)) { - error.put("lineNumber", error, Integer.valueOf(lineNumber)); + error.put("lineNumber", error, Integer.valueOf(lineNumber), false); } // set stack property, see bug #549604 error.setStackProvider(this); diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index 942a48c128..c8733d85b7 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -416,9 +416,9 @@ private static int toDenseIndex(Object id) { } @Override - public void put(String id, Scriptable start, Object value) + public void put(String id, Scriptable start, Object value, boolean checked) { - super.put(id, start, value); + super.put(id, start, value, checked); if (start == this) { // If the object is sealed, super will throw exception long index = toArrayIndex(id); @@ -447,7 +447,7 @@ private boolean ensureCapacity(int capacity) } @Override - public void put(int index, Scriptable start, Object value) + public void put(int index, Scriptable start, Object value, boolean checked) { if (start == this && !isSealed() && dense != null && 0 <= index && (denseOnly || !isGetterOrSetter(null, index, true))) @@ -469,7 +469,7 @@ public void put(int index, Scriptable start, Object value) denseOnly = false; } } - super.put(index, start, value); + super.put(index, start, value, checked); if (start == this && (lengthAttr & READONLY) == 0) { // only set the array length if given an array index (ECMA 15.4.0) if (this.length <= index) { @@ -646,7 +646,7 @@ private boolean defineOwnPropertyIndex(String name, denseOnly = false; for (int i = 0; i < values.length; i++) { if (values[i] != NOT_FOUND) { - put(i, this, values[i]); + put(i, this, values[i], checked); } } } @@ -956,11 +956,15 @@ private static Object getRawElem(Scriptable target, long index) { private static void defineElem(Context cx, Scriptable target, long index, Object value) { + // [[DefineOwnProperty]](_, _, false) -> always ignore error for + // invalid [[DefineOwnProperty]] if (index > Integer.MAX_VALUE) { String id = Long.toString(index); - ScriptableObject.defineProperty(target, id, value, ScriptableObject.EMPTY); + ScriptableObject.defineProperty(target, id, value, + ScriptableObject.EMPTY, false); } else { - ScriptableObject.defineProperty(target, (int)index, value, ScriptableObject.EMPTY); + ScriptableObject.defineProperty(target, (int)index, value, + ScriptableObject.EMPTY, false); } } diff --git a/src/org/mozilla/javascript/NativeCall.java b/src/org/mozilla/javascript/NativeCall.java index 1b5868cf89..6d8882419e 100644 --- a/src/org/mozilla/javascript/NativeCall.java +++ b/src/org/mozilla/javascript/NativeCall.java @@ -69,6 +69,8 @@ static void init(Scriptable scope, boolean sealed) // leave prototype null this.originalArgs = (args == null) ? ScriptRuntime.emptyArgs : args; + // TODO: which strict-mode setting is required here? + boolean strict = false; // initialize values of arguments int paramAndVarCount = function.getParamAndVarCount(); @@ -78,14 +80,14 @@ static void init(Scriptable scope, boolean sealed) String name = function.getParamOrVarName(i); Object val = i < args.length ? args[i] : Undefined.instance; - defineProperty(name, val, PERMANENT); + defineProperty(name, val, PERMANENT, strict); } } // initialize "arguments" property but only if it was not overridden by // the parameter with the same name if (!super.has("arguments", this)) { - defineProperty("arguments", new Arguments(this), PERMANENT); + defineProperty("arguments", new Arguments(this), PERMANENT, strict); } if (paramAndVarCount != 0) { @@ -93,9 +95,9 @@ static void init(Scriptable scope, boolean sealed) String name = function.getParamOrVarName(i); if (!super.has(name, this)) { if (function.getParamOrVarConst(i)) - defineProperty(name, Undefined.instance, CONST); + defineProperty(name, Undefined.instance, CONST, strict); else - defineProperty(name, Undefined.instance, PERMANENT); + defineProperty(name, Undefined.instance, PERMANENT, strict); } } } diff --git a/src/org/mozilla/javascript/NativeError.java b/src/org/mozilla/javascript/NativeError.java index 6f27611943..bdf608e319 100644 --- a/src/org/mozilla/javascript/NativeError.java +++ b/src/org/mozilla/javascript/NativeError.java @@ -174,7 +174,7 @@ public void setStack(Object value) { stackProvider = null; delete("stack"); } - put("stack", this, value); + put("stack", this, value, false); } private static Object js_toString(Context cx, Scriptable scope, diff --git a/src/org/mozilla/javascript/NativeGlobal.java b/src/org/mozilla/javascript/NativeGlobal.java index ef201f90e4..c1d6976059 100644 --- a/src/org/mozilla/javascript/NativeGlobal.java +++ b/src/org/mozilla/javascript/NativeGlobal.java @@ -120,14 +120,14 @@ public static void init(Context cx, Scriptable scope, boolean sealed) { ScriptableObject.defineProperty( scope, "NaN", ScriptRuntime.NaNobj, - READONLY|DONTENUM|PERMANENT); + READONLY|DONTENUM|PERMANENT, false); ScriptableObject.defineProperty( scope, "Infinity", ScriptRuntime.wrapNumber(Double.POSITIVE_INFINITY), - READONLY|DONTENUM|PERMANENT); + READONLY|DONTENUM|PERMANENT, false); ScriptableObject.defineProperty( scope, "undefined", Undefined.instance, - READONLY|DONTENUM|PERMANENT); + READONLY|DONTENUM|PERMANENT, false); String[] errorMethods = { "EvalError", @@ -149,13 +149,13 @@ public static void init(Context cx, Scriptable scope, boolean sealed) { ScriptableObject errorProto = (ScriptableObject) ScriptRuntime.newObject(cx, scope, "Error", ScriptRuntime.emptyArgs); - errorProto.put("name", errorProto, name); - errorProto.put("message", errorProto, ""); + errorProto.put("name", errorProto, name, false); + errorProto.put("message", errorProto, "", false); IdFunctionObject ctor = new IdFunctionObject(obj, FTAG, Id_new_CommonError, name, 1, scope); ctor.markAsConstructor(errorProto); - errorProto.put("constructor", errorProto, ctor); + errorProto.put("constructor", errorProto, ctor, false); errorProto.setAttributes("constructor", ScriptableObject.DONTENUM); if (sealed) { errorProto.sealObject(); diff --git a/src/org/mozilla/javascript/NativeIterator.java b/src/org/mozilla/javascript/NativeIterator.java index fc9dc9aee2..4727f99df5 100644 --- a/src/org/mozilla/javascript/NativeIterator.java +++ b/src/org/mozilla/javascript/NativeIterator.java @@ -56,7 +56,7 @@ static void init(ScriptableObject scope, boolean sealed) { obj.setParentScope(scope); if (sealed) { obj.sealObject(); } ScriptableObject.defineProperty(scope, STOP_ITERATION, obj, - ScriptableObject.DONTENUM); + ScriptableObject.DONTENUM, false); // Use "associateValue" so that generators can continue to // throw StopIteration even if the property of the global // scope is replaced or deleted. diff --git a/src/org/mozilla/javascript/NativeJSON.java b/src/org/mozilla/javascript/NativeJSON.java index fd2f989e55..cfa6e64816 100644 --- a/src/org/mozilla/javascript/NativeJSON.java +++ b/src/org/mozilla/javascript/NativeJSON.java @@ -70,7 +70,7 @@ static void init(Scriptable scope, boolean sealed) obj.setParentScope(scope); if (sealed) { obj.sealObject(); } ScriptableObject.defineProperty(scope, "JSON", obj, - ScriptableObject.DONTENUM); + ScriptableObject.DONTENUM, false); } private NativeJSON() @@ -152,7 +152,7 @@ public static Object parse(Context cx, Scriptable scope, String jtext, { Object unfiltered = parse(cx, scope, jtext); Scriptable root = cx.newObject(scope); - root.put("", root, unfiltered); + root.put("", root, unfiltered, false); return walk(cx, scope, reviver, root, ""); } @@ -175,7 +175,7 @@ private static Object walk(Context cx, Scriptable scope, Callable reviver, if (newElement == Undefined.instance) { val.delete(i); } else { - val.put(i, val, newElement); + val.put(i, val, newElement, false); } } } else { @@ -189,9 +189,9 @@ private static Object walk(Context cx, Scriptable scope, Callable reviver, val.delete((String) p); } else { if (p instanceof Number) - val.put(((Number) p).intValue(), val, newElement); + val.put(((Number) p).intValue(), val, newElement, false); else - val.put((String) p, val, newElement); + val.put((String) p, val, newElement, false); } } } @@ -284,7 +284,7 @@ public static Object stringify(Context cx, Scriptable scope, Object value, ScriptableObject wrapper = new NativeObject(); wrapper.setParentScope(scope); wrapper.setPrototype(ScriptableObject.getObjectPrototype(scope)); - wrapper.defineProperty("", value, 0); + wrapper.defineProperty("", value, 0, false); return str("", wrapper, state); } diff --git a/src/org/mozilla/javascript/NativeJavaArray.java b/src/org/mozilla/javascript/NativeJavaArray.java index 2e07765b18..5ab006b666 100644 --- a/src/org/mozilla/javascript/NativeJavaArray.java +++ b/src/org/mozilla/javascript/NativeJavaArray.java @@ -117,7 +117,7 @@ public Object get(int index, Scriptable start) { } @Override - public void put(String id, Scriptable start, Object value) { + public void put(String id, Scriptable start, Object value, boolean checked) { // Ignore assignments to "length"--it's readonly. if (!id.equals("length")) throw Context.reportRuntimeError1( @@ -125,7 +125,7 @@ public void put(String id, Scriptable start, Object value) { } @Override - public void put(int index, Scriptable start, Object value) { + public void put(int index, Scriptable start, Object value, boolean checked) { if (0 <= index && index < length) { Array.set(array, index, Context.jsToJava(value, cls)); } diff --git a/src/org/mozilla/javascript/NativeJavaClass.java b/src/org/mozilla/javascript/NativeJavaClass.java index e0770bafba..efc4d226be 100644 --- a/src/org/mozilla/javascript/NativeJavaClass.java +++ b/src/org/mozilla/javascript/NativeJavaClass.java @@ -137,7 +137,7 @@ public Object get(String name, Scriptable start) { } @Override - public void put(String name, Scriptable start, Object value) { + public void put(String name, Scriptable start, Object value, boolean checked) { members.put(this, name, javaObject, value, true); } diff --git a/src/org/mozilla/javascript/NativeJavaObject.java b/src/org/mozilla/javascript/NativeJavaObject.java index 52e946edf4..22f1a42e67 100644 --- a/src/org/mozilla/javascript/NativeJavaObject.java +++ b/src/org/mozilla/javascript/NativeJavaObject.java @@ -117,17 +117,17 @@ public Object get(int index, Scriptable start) { throw members.reportMemberNotFound(Integer.toString(index)); } - public void put(String name, Scriptable start, Object value) { + public void put(String name, Scriptable start, Object value, boolean checked) { // We could be asked to modify the value of a property in the // prototype. Since we can't add a property to a Java object, // we modify it in the prototype rather than copy it down. if (prototype == null || members.has(name, false)) members.put(this, name, javaObject, value, false); else - prototype.put(name, prototype, value); + prototype.put(name, prototype, value, checked); } - public void put(int index, Scriptable start, Object value) { + public void put(int index, Scriptable start, Object value, boolean checked) { throw members.reportMemberNotFound(Integer.toString(index)); } diff --git a/src/org/mozilla/javascript/NativeJavaPackage.java b/src/org/mozilla/javascript/NativeJavaPackage.java index 0b49b0fe50..24413aa775 100644 --- a/src/org/mozilla/javascript/NativeJavaPackage.java +++ b/src/org/mozilla/javascript/NativeJavaPackage.java @@ -102,12 +102,12 @@ public boolean has(int index, Scriptable start) { } @Override - public void put(String id, Scriptable start, Object value) { + public void put(String id, Scriptable start, Object value, boolean checked) { // Can't add properties to Java packages. Sorry. } @Override - public void put(int index, Scriptable start, Object value) { + public void put(int index, Scriptable start, Object value, boolean checked) { throw Context.reportRuntimeError0("msg.pkg.int"); } @@ -134,7 +134,7 @@ NativeJavaPackage forcePackage(String name, Scriptable scope) : packageName + "." + name; NativeJavaPackage pkg = new NativeJavaPackage(true, newPackage, classLoader); ScriptRuntime.setObjectProtoAndParent(pkg, scope); - super.put(name, this, pkg); + super.put(name, this, pkg, false); return pkg; } } @@ -184,7 +184,7 @@ synchronized Object getPkgProperty(String name, Scriptable start, if (newValue != null) { // Make it available for fast lookup and sharing of // lazily-reflected constructors and static members. - super.put(name, start, newValue); + super.put(name, start, newValue, false); } return newValue; } diff --git a/src/org/mozilla/javascript/NativeJavaTopPackage.java b/src/org/mozilla/javascript/NativeJavaTopPackage.java index a324dd14ac..cbd83def24 100644 --- a/src/org/mozilla/javascript/NativeJavaTopPackage.java +++ b/src/org/mozilla/javascript/NativeJavaTopPackage.java @@ -138,10 +138,10 @@ public static void init(Context cx, Scriptable scope, boolean sealed) getClass.sealObject(); } getClass.exportAsScopeProperty(); - global.defineProperty("Packages", top, ScriptableObject.DONTENUM); + global.defineProperty("Packages", top, ScriptableObject.DONTENUM, false); for (int i=0; i < topNames.length; i++) { global.defineProperty(topNames[i], topPackages[i], - ScriptableObject.DONTENUM); + ScriptableObject.DONTENUM, false); } } diff --git a/src/org/mozilla/javascript/NativeMath.java b/src/org/mozilla/javascript/NativeMath.java index 8d4f0e96b5..35b4bfb5e2 100644 --- a/src/org/mozilla/javascript/NativeMath.java +++ b/src/org/mozilla/javascript/NativeMath.java @@ -59,7 +59,7 @@ static void init(Scriptable scope, boolean sealed) obj.setParentScope(scope); if (sealed) { obj.sealObject(); } ScriptableObject.defineProperty(scope, "Math", obj, - ScriptableObject.DONTENUM); + ScriptableObject.DONTENUM, false); } private NativeMath() diff --git a/src/org/mozilla/javascript/NativeNumber.java b/src/org/mozilla/javascript/NativeNumber.java index 9e0e7902cd..182c31addf 100644 --- a/src/org/mozilla/javascript/NativeNumber.java +++ b/src/org/mozilla/javascript/NativeNumber.java @@ -77,19 +77,19 @@ protected void fillConstructorProperties(IdFunctionObject ctor) ScriptableObject.PERMANENT | ScriptableObject.READONLY; - ctor.defineProperty("NaN", ScriptRuntime.NaNobj, attr); + ctor.defineProperty("NaN", ScriptRuntime.NaNobj, attr, false); ctor.defineProperty("POSITIVE_INFINITY", ScriptRuntime.wrapNumber(Double.POSITIVE_INFINITY), - attr); + attr, false); ctor.defineProperty("NEGATIVE_INFINITY", ScriptRuntime.wrapNumber(Double.NEGATIVE_INFINITY), - attr); + attr, false); ctor.defineProperty("MAX_VALUE", ScriptRuntime.wrapNumber(Double.MAX_VALUE), - attr); + attr, false); ctor.defineProperty("MIN_VALUE", ScriptRuntime.wrapNumber(Double.MIN_VALUE), - attr); + attr, false); super.fillConstructorProperties(ctor); } diff --git a/src/org/mozilla/javascript/NativeString.java b/src/org/mozilla/javascript/NativeString.java index 8949026167..094b67eedd 100644 --- a/src/org/mozilla/javascript/NativeString.java +++ b/src/org/mozilla/javascript/NativeString.java @@ -505,11 +505,11 @@ public Object get(int index, Scriptable start) { } @Override - public void put(int index, Scriptable start, Object value) { + public void put(int index, Scriptable start, Object value, boolean checked) { if (0 <= index && index < string.length()) { return; } - super.put(index, start, value); + super.put(index, start, value, checked); } @Override diff --git a/src/org/mozilla/javascript/NativeWith.java b/src/org/mozilla/javascript/NativeWith.java index dcdbbbf163..626e3fe6d4 100644 --- a/src/org/mozilla/javascript/NativeWith.java +++ b/src/org/mozilla/javascript/NativeWith.java @@ -103,18 +103,18 @@ public Object get(int index, Scriptable start) return prototype.get(index, start); } - public void put(String id, Scriptable start, Object value) + public void put(String id, Scriptable start, Object value, boolean checked) { if (start == this) start = prototype; - prototype.put(id, start, value); + prototype.put(id, start, value, checked); } - public void put(int index, Scriptable start, Object value) + public void put(int index, Scriptable start, Object value, boolean checked) { if (start == this) start = prototype; - prototype.put(index, start, value); + prototype.put(index, start, value, checked); } public void delete(String id) diff --git a/src/org/mozilla/javascript/PropertyDescriptor.java b/src/org/mozilla/javascript/PropertyDescriptor.java index 57376d6219..2b7d0f98f8 100755 --- a/src/org/mozilla/javascript/PropertyDescriptor.java +++ b/src/org/mozilla/javascript/PropertyDescriptor.java @@ -117,14 +117,14 @@ static Object fromPropertyDescriptor(Context cx, Scriptable scope, ScriptableObject obj = ensureScriptableObject(cx.newObject(scope)); if (desc.isDataDescriptor()) { - obj.defineProperty("value", desc.getValue(), EMPTY); - obj.defineProperty("writable", desc.isWritable(), EMPTY); + obj.defineProperty("value", desc.getValue(), EMPTY, false); + obj.defineProperty("writable", desc.isWritable(), EMPTY, false); } else { - obj.defineProperty("get", desc.getGetter(), EMPTY); - obj.defineProperty("set", desc.getSetter(), EMPTY); + obj.defineProperty("get", desc.getGetter(), EMPTY, false); + obj.defineProperty("set", desc.getSetter(), EMPTY, false); } - obj.defineProperty("enumerable", desc.isEnumerable(), EMPTY); - obj.defineProperty("configurable", desc.isConfigurable(), EMPTY); + obj.defineProperty("enumerable", desc.isEnumerable(), EMPTY, false); + obj.defineProperty("configurable", desc.isConfigurable(), EMPTY, false); return obj; } diff --git a/src/org/mozilla/javascript/RhinoException.java b/src/org/mozilla/javascript/RhinoException.java index b5372a933c..4f39e3b24b 100644 --- a/src/org/mozilla/javascript/RhinoException.java +++ b/src/org/mozilla/javascript/RhinoException.java @@ -42,7 +42,6 @@ package org.mozilla.javascript; import java.io.CharArrayWriter; -import java.io.File; import java.io.FilenameFilter; import java.io.PrintStream; import java.io.PrintWriter; diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 1ff3b61a89..d1b2d457a6 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -1258,9 +1258,9 @@ public static Object setDefaultNamespace(Object namespace, Context cx) // XXX: this is racy of cause ScriptableObject.defineProperty(scope, DEFAULT_NS_TAG, ns, ScriptableObject.PERMANENT - | ScriptableObject.DONTENUM); + | ScriptableObject.DONTENUM, false); } else { - scope.put(DEFAULT_NS_TAG, scope, ns); + scope.put(DEFAULT_NS_TAG, scope, ns, false); } return Undefined.instance; @@ -1967,7 +1967,7 @@ public static Object setName(Scriptable bound, Object value, if (cx.useDynamicScope) { bound = checkDynamicScope(cx.topCallScope, bound); } - bound.put(id, bound, value); + bound.put(id, bound, value, false); } return value; } @@ -1995,8 +1995,9 @@ public static Object strictSetName(Scriptable bound, Object value, public static Object setConst(Scriptable bound, Object value, Context cx, String id) { + // TODO: strict mode flag? if (bound instanceof XMLObject) { - bound.put(id, bound, value); + bound.put(id, bound, value, false); } else { ScriptableObject.putConstProperty(bound, id, value); } @@ -2331,8 +2332,8 @@ private IllegalStateException error() { public Object get(int index, Scriptable start) { throw error(); } public boolean has(String name, Scriptable start) { throw error(); } public boolean has(int index, Scriptable start) { throw error(); } - public void put(String name, Scriptable start, Object value) { throw error(); } - public void put(int index, Scriptable start, Object value) { throw error(); } + public void put(String name, Scriptable start, Object value, boolean checked) { throw error(); } + public void put(int index, Scriptable start, Object value, boolean checked) { throw error(); } public void delete(String name) { throw error(); } public void delete(int index) { throw error(); } public Scriptable getPrototype() { throw error(); } @@ -2868,7 +2869,8 @@ public static Object propIncrDecr(Object obj, String id, } target = target.getPrototype(); } while (target != null); - start.put(id, start, NaNobj); + // TODO: strict mode flag? + start.put(id, start, NaNobj, false); return NaNobj; } return doScriptableIncrDecr(target, id, start, value, @@ -2898,7 +2900,8 @@ private static Object doScriptableIncrDecr(Scriptable target, --number; } Number result = wrapNumber(number); - target.put(id, protoChainStart, result); + // TODO: strict mode flag? + target.put(id, protoChainStart, result, false); if (post) { return value; } else { @@ -3422,6 +3425,8 @@ public static void initScript(NativeFunction funObj, Scriptable thisObj, varScope = varScope.getParentScope(); } + // TODO: strict mode flag? + boolean strict = false; for (int i = varCount; i-- != 0;) { String name = funObj.getParamOrVarName(i); boolean isConst = funObj.getParamOrVarConst(i); @@ -3435,9 +3440,9 @@ public static void initScript(NativeFunction funObj, Scriptable thisObj, else ScriptableObject.defineProperty( varScope, name, Undefined.instance, - ScriptableObject.PERMANENT); + ScriptableObject.PERMANENT, strict); } else { - varScope.put(name, varScope, Undefined.instance); + varScope.put(name, varScope, Undefined.instance, strict); } } else { ScriptableObject.redefineProperty(scope, name, isConst); @@ -3566,13 +3571,15 @@ public static Scriptable newCatchScope(Throwable t, null); ScriptableObject.defineProperty( errorObject, "javaException", wrap, - ScriptableObject.PERMANENT | ScriptableObject.READONLY); + ScriptableObject.PERMANENT | ScriptableObject.READONLY, + false); } if (isVisible(cx, re)) { Object wrap = cx.getWrapFactory().wrap(cx, scope, re, null); ScriptableObject.defineProperty( errorObject, "rhinoException", wrap, - ScriptableObject.PERMANENT | ScriptableObject.READONLY); + ScriptableObject.PERMANENT | ScriptableObject.READONLY, + false); } obj = errorObject; } @@ -3580,7 +3587,7 @@ public static Scriptable newCatchScope(Throwable t, NativeObject catchScopeObject = new NativeObject(); // See ECMA 12.4 catchScopeObject.defineProperty( - exceptionName, obj, ScriptableObject.PERMANENT); + exceptionName, obj, ScriptableObject.PERMANENT, false); if (isVisible(cx, t)) { // Add special Rhino object __exception__ defined in the catch @@ -3588,7 +3595,7 @@ public static Scriptable newCatchScope(Throwable t, // with the JavaScript exception (to get stack trace info, etc.) catchScopeObject.defineProperty( "__exception__", Context.javaToJS(t, scope), - ScriptableObject.PERMANENT|ScriptableObject.DONTENUM); + ScriptableObject.PERMANENT|ScriptableObject.DONTENUM, false); } if (cacheObj) { @@ -3677,16 +3684,19 @@ public static void initFunction(Context cx, Scriptable scope, NativeFunction function, int type, boolean fromEvalCode) { + // TODO: strict mode flag? + boolean strict = false; if (type == FunctionNode.FUNCTION_STATEMENT) { String name = function.getFunctionName(); if (name != null && name.length() != 0) { if (!fromEvalCode) { // ECMA specifies that functions defined in global and // function scope outside eval should have DONTDELETE set. - ScriptableObject.defineProperty - (scope, name, function, ScriptableObject.PERMANENT); + ScriptableObject.defineProperty(scope, name, function, + ScriptableObject.PERMANENT, + strict); } else { - scope.put(name, scope, function); + scope.put(name, scope, function, strict); } } } else if (type == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { @@ -3698,7 +3708,7 @@ public static void initFunction(Context cx, Scriptable scope, while (scope instanceof NativeWith) { scope = scope.getParentScope(); } - scope.put(name, scope, function); + scope.put(name, scope, function, strict); } } else { throw Kit.codeBug(); @@ -3745,7 +3755,8 @@ public static Scriptable newArrayLiteral(Object[] objects, ++skip; continue; } - ScriptableObject.defineProperty(array, i, objects[j], ScriptableObject.EMPTY); + ScriptableObject.defineProperty(array, i, objects[j], + ScriptableObject.EMPTY, false); ++j; } return array; @@ -3781,7 +3792,7 @@ public static Scriptable newObjectLiteral(Object[] propertyIds, if (isSpecialProperty((String)id)) { specialRef(object, (String)id, cx).set(cx, value); } else { - object.put((String)id, object, value); + object.put((String)id, object, value, false); } } else { ScriptableObject so = (ScriptableObject)object; @@ -3791,7 +3802,7 @@ public static Scriptable newObjectLiteral(Object[] propertyIds, } } else { int index = ((Integer)id).intValue(); - object.put(index, object, value); + object.put(index, object, value, false); } } return object; diff --git a/src/org/mozilla/javascript/ScriptStackElement.java b/src/org/mozilla/javascript/ScriptStackElement.java index 4f2984f2c0..3198eb714f 100644 --- a/src/org/mozilla/javascript/ScriptStackElement.java +++ b/src/org/mozilla/javascript/ScriptStackElement.java @@ -22,6 +22,7 @@ public ScriptStackElement(String fileName, String functionName, int lineNumber) this.lineNumber = lineNumber; } + @Override public String toString() { StringBuilder sb = new StringBuilder(); renderMozillaStyle(sb); diff --git a/src/org/mozilla/javascript/Scriptable.java b/src/org/mozilla/javascript/Scriptable.java index 3f14601d06..9440b10230 100644 --- a/src/org/mozilla/javascript/Scriptable.java +++ b/src/org/mozilla/javascript/Scriptable.java @@ -202,12 +202,13 @@ public interface Scriptable { * @param name the name of the property * @param start the object whose property is being set * @param value value to set the property to + * @param checked controls error handling * @see org.mozilla.javascript.Scriptable#has(String, Scriptable) * @see org.mozilla.javascript.Scriptable#get(String, Scriptable) * @see org.mozilla.javascript.ScriptableObject#putProperty(Scriptable, String, Object) * @see org.mozilla.javascript.Context#toObject(Object, Scriptable) */ - public void put(String name, Scriptable start, Object value); + public void put(String name, Scriptable start, Object value, boolean checked); /** * Sets an indexed property in this object. @@ -221,12 +222,13 @@ public interface Scriptable { * @param index the numeric index for the property * @param start the object whose property is being set * @param value value to set the property to + * @param checked controls error handling * @see org.mozilla.javascript.Scriptable#has(int, Scriptable) * @see org.mozilla.javascript.Scriptable#get(int, Scriptable) * @see org.mozilla.javascript.ScriptableObject#putProperty(Scriptable, int, Object) * @see org.mozilla.javascript.Context#toObject(Object, Scriptable) */ - public void put(int index, Scriptable start, Object value); + public void put(int index, Scriptable start, Object value, boolean checked); /** * Removes a property from this object. diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 935fffec70..c5ad13c5ac 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -492,13 +492,48 @@ public Object get(int index, Scriptable start) * @param start the object whose property is being set * @param value value to set the property to */ - public void put(String name, Scriptable start, Object value) + @Deprecated + public final void put(String name, Scriptable start, Object value) + { + put(name, start, value, false); + } + + /** + * Sets the value of the named property, creating it if need be. + * + * If the property was created using defineProperty, the + * appropriate setter method is called.

+ * + * If the property's attributes include READONLY, no action is + * taken. + * This method will actually set the property in the start + * object. + * + * @param name the name of the property + * @param start the object whose property is being set + * @param value value to set the property to + * @param checked controls error handling + */ + public void put(String name, Scriptable start, Object value, boolean checked) { if (putImpl(name, 0, start, value)) return; if (start == this) throw Kit.codeBug(); - start.put(name, start, value); + start.put(name, start, value, checked); + } + + /** + * Sets the value of the indexed property, creating it if need be. + * + * @param index the numeric index for the property + * @param start the object whose property is being set + * @param value value to set the property to + */ + @Deprecated + public final void put(int index, Scriptable start, Object value) + { + put(index, start, value, false); } /** @@ -507,14 +542,15 @@ public void put(String name, Scriptable start, Object value) * @param index the numeric index for the property * @param start the object whose property is being set * @param value value to set the property to + * @param checked controls error handling */ - public void put(int index, Scriptable start, Object value) + public void put(int index, Scriptable start, Object value, boolean checked) { if (putImpl(null, index, start, value)) return; if (start == this) throw Kit.codeBug(); - start.put(index, start, value); + start.put(index, start, value, checked); } /** @@ -569,7 +605,8 @@ public void putConst(String name, Scriptable start, Object value) if (start instanceof ConstProperties) ((ConstProperties)start).putConst(name, start, value); else - start.put(name, start, value); + // TODO: 'throw' flag? + start.put(name, start, value, false); } public void defineConst(String name, Scriptable start) @@ -1519,11 +1556,29 @@ private static Class extendsScriptable(Class c) * @param attributes the attributes of the JavaScript property * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object) */ + @Deprecated + public final void defineProperty(int index, Object value, + int attributes) + { + defineProperty(index, value, attributes, false); + } + + /** + * Define a JavaScript property. + * + * Creates the property with an initial value and sets its attributes. + * + * @param propertyName the name of the property to define. + * @param value the initial value of the property + * @param attributes the attributes of the JavaScript property + * @param checked controls error handling + * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object) + */ public void defineProperty(int index, Object value, - int attributes) + int attributes, boolean checked) { checkNotSealed(null, index); - put(index, this, value); + put(index, this, value, checked); setAttributes(index, attributes); } @@ -1533,16 +1588,47 @@ public void defineProperty(int index, Object value, * defineProperty there, otherwise calls put in destination * ignoring attributes */ + @Deprecated public static void defineProperty(Scriptable destination, int index, Object value, int attributes) + { + defineProperty(destination, index, value, attributes, false); + } + + /** + * Utility method to add properties to arbitrary Scriptable object. + * If destination is instance of ScriptableObject, calls + * defineProperty there, otherwise calls put in destination + * ignoring attributes + */ + public static void defineProperty(Scriptable destination, + int index, Object value, + int attributes, boolean checked) { if (!(destination instanceof ScriptableObject)) { - destination.put(index, destination, value); + destination.put(index, destination, value, checked); return; } ScriptableObject so = (ScriptableObject)destination; - so.defineProperty(index, value, attributes); + so.defineProperty(index, value, attributes, checked); + } + + /** + * Define a JavaScript property. + * + * Creates the property with an initial value and sets its attributes. + * + * @param propertyName the name of the property to define. + * @param value the initial value of the property + * @param attributes the attributes of the JavaScript property + * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object) + */ + @Deprecated + public final void defineProperty(String propertyName, Object value, + int attributes) + { + defineProperty(propertyName, value, attributes, false); } /** @@ -1553,13 +1639,14 @@ public static void defineProperty(Scriptable destination, * @param propertyName the name of the property to define. * @param value the initial value of the property * @param attributes the attributes of the JavaScript property + * @param checked controls error handling * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object) */ public void defineProperty(String propertyName, Object value, - int attributes) + int attributes, boolean checked) { checkNotSealed(propertyName, 0); - put(propertyName, this, value); + put(propertyName, this, value, checked); setAttributes(propertyName, attributes); } @@ -1569,16 +1656,30 @@ public void defineProperty(String propertyName, Object value, * defineProperty there, otherwise calls put in destination * ignoring attributes */ + @Deprecated public static void defineProperty(Scriptable destination, String propertyName, Object value, int attributes) + { + defineProperty(destination, propertyName, value, attributes, false); + } + + /** + * Utility method to add properties to arbitrary Scriptable object. + * If destination is instance of ScriptableObject, calls + * defineProperty there, otherwise calls put in destination + * ignoring attributes + */ + public static void defineProperty(Scriptable destination, + String propertyName, Object value, + int attributes, boolean checked) { if (!(destination instanceof ScriptableObject)) { - destination.put(propertyName, destination, value); + destination.put(propertyName, destination, value, checked); return; } ScriptableObject so = (ScriptableObject)destination; - so.defineProperty(propertyName, value, attributes); + so.defineProperty(propertyName, value, attributes, checked); } /** @@ -2181,7 +2282,7 @@ public static void putProperty(Scriptable obj, String name, Object value, Scriptable base = getBase(obj, name); if (base == null) base = obj; - base.put(name, obj, value); + base.put(name, obj, value, checked); } /** @@ -2252,7 +2353,7 @@ public static void putProperty(Scriptable obj, int index, Object value, Scriptable base = getBase(obj, index); if (base == null) base = obj; - base.put(index, obj, value); + base.put(index, obj, value, checked); } /** diff --git a/src/org/mozilla/javascript/ast/ArrayComprehensionLoop.java b/src/org/mozilla/javascript/ast/ArrayComprehensionLoop.java index 270af5bbe9..e2f6f46fe1 100644 --- a/src/org/mozilla/javascript/ast/ArrayComprehensionLoop.java +++ b/src/org/mozilla/javascript/ast/ArrayComprehensionLoop.java @@ -63,6 +63,7 @@ public ArrayComprehensionLoop(int pos, int len) { * Returns {@code null} for loop body * @return loop body (always {@code null} for this node type) */ + @Override public AstNode getBody() { return null; } @@ -72,6 +73,7 @@ public AstNode getBody() { * @param body loop body * @throws UnsupportedOperationException */ + @Override public void setBody(AstNode body) { throw new UnsupportedOperationException("this node type has no body"); } diff --git a/src/org/mozilla/javascript/ast/AstNode.java b/src/org/mozilla/javascript/ast/AstNode.java index e9c7891bb2..2bcbae0850 100644 --- a/src/org/mozilla/javascript/ast/AstNode.java +++ b/src/org/mozilla/javascript/ast/AstNode.java @@ -386,6 +386,7 @@ public static String operatorToString(int op) { public abstract void visit(NodeVisitor visitor); // subclasses with potential side effects should override this + @Override public boolean hasSideEffects() { switch (getType()) { @@ -580,6 +581,7 @@ protected static class DebugPrintVisitor implements NodeVisitor { public DebugPrintVisitor(StringBuilder buf) { buffer = buf; } + @Override public String toString() { return buffer.toString(); } diff --git a/src/org/mozilla/javascript/ast/Name.java b/src/org/mozilla/javascript/ast/Name.java index 287b02b4f2..24aa3ada95 100644 --- a/src/org/mozilla/javascript/ast/Name.java +++ b/src/org/mozilla/javascript/ast/Name.java @@ -113,6 +113,7 @@ public void setIdentifier(String identifier) { * @param s the scope. Can be null. Doesn't set any fields in the * scope. */ + @Override public void setScope(Scope s) { scope = s; } @@ -124,6 +125,7 @@ public void setScope(Scope s) { * to find the lexical {@code Scope} in which this {@code Name} is defined, * if any. */ + @Override public Scope getScope() { return scope; } diff --git a/src/org/mozilla/javascript/commonjs/module/Require.java b/src/org/mozilla/javascript/commonjs/module/Require.java index 61bb372b6e..a45628a688 100644 --- a/src/org/mozilla/javascript/commonjs/module/Require.java +++ b/src/org/mozilla/javascript/commonjs/module/Require.java @@ -326,9 +326,9 @@ private Scriptable executeModuleScript(Context cx, String id, // This means we're currently using the "MGN" approach (ModuleScript // with Global Natives) as specified here: // - executionScope.put("exports", executionScope, exports); - executionScope.put("module", executionScope, moduleObject); - moduleObject.put("exports", moduleObject, exports); + executionScope.put("exports", executionScope, exports, false); + executionScope.put("module", executionScope, moduleObject, false); + moduleObject.put("exports", moduleObject, exports, false); install(executionScope); if(isMain) { defineReadOnlyProperty(this, "main", moduleObject); diff --git a/src/org/mozilla/javascript/json/JsonParser.java b/src/org/mozilla/javascript/json/JsonParser.java index c8d3d91f34..4064783600 100644 --- a/src/org/mozilla/javascript/json/JsonParser.java +++ b/src/org/mozilla/javascript/json/JsonParser.java @@ -146,9 +146,9 @@ private Object readObject() throws ParseException { long index = ScriptRuntime.indexFromString(id); if (index < 0) { - object.put(id, object, value); + object.put(id, object, value, false); } else { - object.put((int)index, object, value); + object.put((int)index, object, value, false); } needsComma = true; diff --git a/src/org/mozilla/javascript/optimizer/OptRuntime.java b/src/org/mozilla/javascript/optimizer/OptRuntime.java index 75f43be62e..da7c44557d 100644 --- a/src/org/mozilla/javascript/optimizer/OptRuntime.java +++ b/src/org/mozilla/javascript/optimizer/OptRuntime.java @@ -265,7 +265,7 @@ public Object run(Context cx) System.arraycopy(args, 0, argsCopy, 0, args.length); Scriptable argsObj = cx.newArray(global, argsCopy); global.defineProperty("arguments", argsObj, - ScriptableObject.DONTENUM); + ScriptableObject.DONTENUM, false); script.exec(cx, global); return null; } diff --git a/src/org/mozilla/javascript/regexp/NativeRegExp.java b/src/org/mozilla/javascript/regexp/NativeRegExp.java index 923369ede9..ef84842bf0 100644 --- a/src/org/mozilla/javascript/regexp/NativeRegExp.java +++ b/src/org/mozilla/javascript/regexp/NativeRegExp.java @@ -155,7 +155,7 @@ public static void init(Context cx, Scriptable scope, boolean sealed) NativeRegExpCtor ctor = new NativeRegExpCtor(); // Bug #324006: ECMA-262 15.10.6.1 says "The initial value of // RegExp.prototype.constructor is the builtin RegExp constructor." - proto.defineProperty("constructor", ctor, ScriptableObject.DONTENUM); + proto.defineProperty("constructor", ctor, ScriptableObject.DONTENUM, false); ScriptRuntime.setFunctionProtoAndParent(ctor, scope); @@ -166,7 +166,7 @@ public static void init(Context cx, Scriptable scope, boolean sealed) ctor.sealObject(); } - defineProperty(scope, "RegExp", ctor, ScriptableObject.DONTENUM); + defineProperty(scope, "RegExp", ctor, ScriptableObject.DONTENUM, false); } NativeRegExp(Scriptable scope, Object regexpCompiled) @@ -2315,7 +2315,7 @@ Object executeRegExp(Context cx, Scriptable scope, RegExpImpl res, obj = (Scriptable) result; String matchstr = str.substring(index, index + matchlen); - obj.put(0, obj, matchstr); + obj.put(0, obj, matchstr, true); } if (re.parenCount == 0) { @@ -2334,11 +2334,11 @@ Object executeRegExp(Context cx, Scriptable scope, RegExpImpl res, res.parens[num] = parsub; if (matchType == TEST) continue; parstr = parsub.toString(); - obj.put(num+1, obj, parstr); + obj.put(num+1, obj, parstr, true); } else { if (matchType != TEST) - obj.put(num+1, obj, Undefined.instance); + obj.put(num+1, obj, Undefined.instance, true); } } res.lastParen = parsub; @@ -2349,8 +2349,8 @@ Object executeRegExp(Context cx, Scriptable scope, RegExpImpl res, * Define the index and input properties last for better for/in loop * order (so they come after the elements). */ - obj.put("index", obj, Integer.valueOf(start + gData.skipped)); - obj.put("input", obj, str); + obj.put("index", obj, Integer.valueOf(start + gData.skipped), true); + obj.put("input", obj, str, true); } if (res.lastMatch == null) { diff --git a/src/org/mozilla/javascript/regexp/RegExpImpl.java b/src/org/mozilla/javascript/regexp/RegExpImpl.java index 745dc2df63..b49f43dace 100644 --- a/src/org/mozilla/javascript/regexp/RegExpImpl.java +++ b/src/org/mozilla/javascript/regexp/RegExpImpl.java @@ -326,7 +326,7 @@ private static void match_glob(GlobData mdata, Context cx, } SubString matchsub = reImpl.lastMatch; String matchstr = matchsub.toString(); - mdata.arrayobj.put(count, mdata.arrayobj, matchstr); + mdata.arrayobj.put(count, mdata.arrayobj, matchstr, false); } /* @@ -559,7 +559,7 @@ public Object js_split(Context cx, Scriptable scope, // return an array consisting of the target if no separator given if (args.length < 1 || args[0] == Undefined.instance) { - result.put(0, result, target); + result.put(0, result, target, false); return result; } @@ -601,7 +601,7 @@ public Object js_split(Context cx, Scriptable scope, else substr = target.substring(ip[0], match); - result.put(len, result, substr); + result.put(len, result, substr, false); len++; /* * Imitate perl's feature of including parenthesized substrings @@ -613,7 +613,7 @@ public Object js_split(Context cx, Scriptable scope, for (int num = 0; num < size; num++) { if (limited && len >= limit) break; - result.put(len, result, parens[0][num]); + result.put(len, result, parens[0][num], false); len++; } matched[0] = false; diff --git a/testsrc/org/mozilla/javascript/tests/ContinuationsApiTest.java b/testsrc/org/mozilla/javascript/tests/ContinuationsApiTest.java index c93a7dc9ce..2a35b5a0eb 100644 --- a/testsrc/org/mozilla/javascript/tests/ContinuationsApiTest.java +++ b/testsrc/org/mozilla/javascript/tests/ContinuationsApiTest.java @@ -107,7 +107,7 @@ public void setUp() { globalScope = cx.initStandardObjects(); cx.setOptimizationLevel(-1); // must use interpreter mode globalScope.put("myObject", globalScope, - Context.javaToJS(new MyClass(), globalScope)); + Context.javaToJS(new MyClass(), globalScope), false); } finally { Context.exit(); } @@ -283,7 +283,7 @@ public void testContinuationsPrototypesAndSerialization() throws IOException, Cl try { globalScope = cx.initStandardObjects(); cx.setOptimizationLevel(-1); // must use interpreter mode - globalScope.put("myObject", globalScope, Context.javaToJS(new MyClass(), globalScope)); + globalScope.put("myObject", globalScope, Context.javaToJS(new MyClass(), globalScope), false); } finally { Context.exit(); } @@ -343,7 +343,7 @@ public void testContinuationsInlineFunctionsSerialization() throws IOException, try { globalScope = cx.initStandardObjects(); cx.setOptimizationLevel(-1); // must use interpreter mode - globalScope.put("myObject", globalScope, Context.javaToJS(new MyClass(), globalScope)); + globalScope.put("myObject", globalScope, Context.javaToJS(new MyClass(), globalScope), false); } finally { Context.exit(); } diff --git a/testsrc/org/mozilla/javascript/tests/CustomSetterAcceptNullScriptableTest.java b/testsrc/org/mozilla/javascript/tests/CustomSetterAcceptNullScriptableTest.java index a032b60b90..75e2c8bd11 100644 --- a/testsrc/org/mozilla/javascript/tests/CustomSetterAcceptNullScriptableTest.java +++ b/testsrc/org/mozilla/javascript/tests/CustomSetterAcceptNullScriptableTest.java @@ -55,7 +55,7 @@ public void testSetNullForScriptableSetter() throws Exception { final Method setMyPropMethod = Foo.class.getMethod("setMyProp", Foo2.class); foo.defineProperty("myProp", null, null, setMyPropMethod, ScriptableObject.EMPTY); - topScope.put("foo", topScope, foo); + topScope.put("foo", topScope, foo, false); ScriptableObject.defineClass(topScope, Foo2.class); diff --git a/testsrc/org/mozilla/javascript/tests/DefineClassTest.java b/testsrc/org/mozilla/javascript/tests/DefineClassTest.java index 48e93206c0..32a142f57a 100644 --- a/testsrc/org/mozilla/javascript/tests/DefineClassTest.java +++ b/testsrc/org/mozilla/javascript/tests/DefineClassTest.java @@ -86,7 +86,7 @@ public String getClassName() { @JSConstructor public void jsConstructorMethod() { - put("initialized", this, Boolean.TRUE); + put("initialized", this, Boolean.TRUE, false); } @JSFunction @@ -141,7 +141,7 @@ public String getClassName() { } public void jsConstructor() { - put("initialized", this, Boolean.TRUE); + put("initialized", this, Boolean.TRUE, false); } public Object jsFunction_instanceFunction() { diff --git a/testsrc/org/mozilla/javascript/tests/Evaluator.java b/testsrc/org/mozilla/javascript/tests/Evaluator.java index 242d69ccd4..b4ae36c086 100644 --- a/testsrc/org/mozilla/javascript/tests/Evaluator.java +++ b/testsrc/org/mozilla/javascript/tests/Evaluator.java @@ -21,7 +21,7 @@ public static Object eval(String source, Map bindings) { for (Map.Entry entry : bindings.entrySet()) { final Scriptable object = entry.getValue(); object.setParentScope(scope); - scope.put(entry.getKey(), scope, object); + scope.put(entry.getKey(), scope, object, false); } } return cx.evaluateString(scope, source, "source", 1, null); diff --git a/testsrc/org/mozilla/javascript/tests/GeneratedMethodNameTest.java b/testsrc/org/mozilla/javascript/tests/GeneratedMethodNameTest.java index 64826b13d2..09386e479c 100644 --- a/testsrc/org/mozilla/javascript/tests/GeneratedMethodNameTest.java +++ b/testsrc/org/mozilla/javascript/tests/GeneratedMethodNameTest.java @@ -70,7 +70,7 @@ public void doTest(final String scriptCode) throws Exception { final Context cx = ContextFactory.getGlobal().enterContext(); try { Scriptable topScope = cx.initStandardObjects(); - topScope.put("javaNameGetter", topScope, new JavaNameGetter()); + topScope.put("javaNameGetter", topScope, new JavaNameGetter(), false); Script script = cx.compileString(scriptCode, "myScript", 1, null); script.exec(cx, topScope); } finally { diff --git a/testsrc/org/mozilla/javascript/tests/NativeArrayTest.java b/testsrc/org/mozilla/javascript/tests/NativeArrayTest.java index ec1015ae7d..748a7c6830 100644 --- a/testsrc/org/mozilla/javascript/tests/NativeArrayTest.java +++ b/testsrc/org/mozilla/javascript/tests/NativeArrayTest.java @@ -18,48 +18,48 @@ public void init() { @Test public void getIdsShouldIncludeBothIndexAndNormalProperties() { - array.put(0, array, "index"); - array.put("a", array, "normal"); + array.put(0, array, "index", false); + array.put("a", array, "normal", false); assertThat(array.getIds(), is(new Object[]{0, "a"})); } @Test public void deleteShouldRemoveIndexProperties() { - array.put(0, array, "a"); + array.put(0, array, "a", false); array.delete(0); assertThat(array.has(0, array), is(false)); } @Test public void deleteShouldRemoveNormalProperties() { - array.put("p", array, "a"); + array.put("p", array, "a", false); array.delete("p"); assertThat(array.has("p", array), is(false)); } @Test public void putShouldAddIndexProperties() { - array.put(0, array, "a"); + array.put(0, array, "a", false); assertThat(array.has(0, array), is(true)); } @Test public void putShouldAddNormalProperties() { - array.put("p", array, "a"); + array.put("p", array, "a", false); assertThat(array.has("p", array), is(true)); } @Test public void getShouldReturnIndexProperties() { - array.put(0, array, "a"); - array.put("p", array, "b"); + array.put(0, array, "a", false); + array.put("p", array, "b", false); assertThat((String) array.get(0, array), is("a")); } @Test public void getShouldReturnNormalProperties() { - array.put("p", array, "a"); + array.put("p", array, "a", false); assertThat((String) array.get("p", array), is("a")); } @@ -75,38 +75,38 @@ public void getIndexIdsShouldBeEmptyForEmptyArray() { @Test public void getIndexIdsShouldBeAZeroForSimpleSingletonArray() { - array.put(0, array, "a"); + array.put(0, array, "a", false); assertThat(array.getIndexIds(), is(new Integer[]{ 0 })); } @Test public void getIndexIdsShouldWorkWhenIndicesSetAsString() { - array.put("0", array, "a"); + array.put("0", array, "a", false); assertThat(array.getIndexIds(), is(new Integer[]{ 0 })); } @Test public void getIndexIdsShouldNotIncludeNegativeIds() { - array.put(-1, array, "a"); + array.put(-1, array, "a", false); assertThat(array.getIndexIds(), is(new Integer[]{})); } @Test public void getIndexIdsShouldIncludeIdsLessThan2ToThe32() { int maxIndex = (int) (1L << 31) - 1; - array.put(maxIndex, array, "a"); + array.put(maxIndex, array, "a", false); assertThat(array.getIndexIds(), is(new Integer[]{ maxIndex })); } @Test public void getIndexIdsShouldNotIncludeIdsGreaterThanOrEqualTo2ToThe32() { - array.put((1L<<31)+"", array, "a"); + array.put((1L<<31)+"", array, "a", false); assertThat(array.getIndexIds(), is(new Integer[]{})); } @Test public void getIndexIdsShouldNotReturnNonNumericIds() { - array.put("x", array, "a"); + array.put("x", array, "a", false); assertThat(array.getIndexIds(), is(new Integer[]{})); } diff --git a/testsrc/org/mozilla/javascript/tests/PrimitiveTypeScopeResolutionTest.java b/testsrc/org/mozilla/javascript/tests/PrimitiveTypeScopeResolutionTest.java index 58d4eeea12..895b9deeb9 100644 --- a/testsrc/org/mozilla/javascript/tests/PrimitiveTypeScopeResolutionTest.java +++ b/testsrc/org/mozilla/javascript/tests/PrimitiveTypeScopeResolutionTest.java @@ -70,7 +70,7 @@ public Object run(final Context cx) new MySimpleScriptableObject("scope2")); cx.evaluateString(scope2, scriptScope2, "source2", 1, null); - scope1.put("scope2", scope1, scope2); + scope1.put("scope2", scope1, scope2, false); return cx.evaluateString(scope1, scriptScope1, "source1", 1, null); @@ -149,10 +149,10 @@ public Object run(final Context cx) final Scriptable scope2 = cx.initStandardObjects( new MySimpleScriptableObject("scope2")); - scope2.put("myObject", scope2, myObject); + scope2.put("myObject", scope2, myObject, false); cx.evaluateString(scope2, scriptScope2, "source2", 1, null); - scope1.put("scope2", scope1, scope2); + scope1.put("scope2", scope1, scope2, false); return cx.evaluateString(scope1, scriptScope1, "source1", 1, null); } diff --git a/testsrc/org/mozilla/javascript/tests/TypeOfTest.java b/testsrc/org/mozilla/javascript/tests/TypeOfTest.java index c8ad2c0ab0..4459db7a74 100644 --- a/testsrc/org/mozilla/javascript/tests/TypeOfTest.java +++ b/testsrc/org/mozilla/javascript/tests/TypeOfTest.java @@ -68,7 +68,7 @@ public Object call(Context _cx, Scriptable _scope, Object _thisObj, public Object run(final Context context) { final Scriptable scope = context.initStandardObjects(); - scope.put("myObj", scope, f); + scope.put("myObj", scope, f, false); return context.evaluateString(scope, "typeof myObj", "test script", 1, null); } }; @@ -82,7 +82,7 @@ private void testCustomizeTypeOf(final String expected, final Scriptable obj) public Object run(final Context context) { final Scriptable scope = context.initStandardObjects(); - scope.put("myObj", scope, obj); + scope.put("myObj", scope, obj, false); return context.evaluateString(scope, "typeof myObj", "test script", 1, null); } }; diff --git a/testsrc/org/mozilla/javascript/tests/es5/ObjectGetOwnPropertyDescriptorTest.java b/testsrc/org/mozilla/javascript/tests/es5/ObjectGetOwnPropertyDescriptorTest.java index 9eaf7a9565..331f574215 100644 --- a/testsrc/org/mozilla/javascript/tests/es5/ObjectGetOwnPropertyDescriptorTest.java +++ b/testsrc/org/mozilla/javascript/tests/es5/ObjectGetOwnPropertyDescriptorTest.java @@ -13,8 +13,8 @@ public class ObjectGetOwnPropertyDescriptorTest { public void testContentsOfPropertyDescriptorShouldReflectAttributesOfProperty() { NativeObject descriptor; NativeObject object = new NativeObject(); - object.defineProperty("a", "1", ScriptableObject.EMPTY); - object.defineProperty("b", "2", ScriptableObject.DONTENUM | ScriptableObject.READONLY | ScriptableObject.PERMANENT); + object.defineProperty("a", "1", ScriptableObject.EMPTY, false); + object.defineProperty("b", "2", ScriptableObject.DONTENUM | ScriptableObject.READONLY | ScriptableObject.PERMANENT, false); descriptor = (NativeObject) eval("Object.getOwnPropertyDescriptor(obj, 'a')", "obj", object); assertEquals("1", descriptor.get("value")); diff --git a/testsrc/org/mozilla/javascript/tests/es5/ObjectGetOwnPropertyNamesTest.java b/testsrc/org/mozilla/javascript/tests/es5/ObjectGetOwnPropertyNamesTest.java index 3ba1f9da27..0c73ea8af8 100644 --- a/testsrc/org/mozilla/javascript/tests/es5/ObjectGetOwnPropertyNamesTest.java +++ b/testsrc/org/mozilla/javascript/tests/es5/ObjectGetOwnPropertyNamesTest.java @@ -12,8 +12,8 @@ public class ObjectGetOwnPropertyNamesTest { @Test public void testShouldReturnAllPropertiesOfArg() { NativeObject object = new NativeObject(); - object.defineProperty("a", "1", ScriptableObject.EMPTY); - object.defineProperty("b", "2", ScriptableObject.DONTENUM); + object.defineProperty("a", "1", ScriptableObject.EMPTY, false); + object.defineProperty("b", "2", ScriptableObject.DONTENUM, false); Object result = eval("Object.getOwnPropertyNames(obj)", "obj", object); diff --git a/testsrc/org/mozilla/javascript/tests/es5/ObjectKeysTest.java b/testsrc/org/mozilla/javascript/tests/es5/ObjectKeysTest.java index 9584c9fa06..c99cbc6001 100644 --- a/testsrc/org/mozilla/javascript/tests/es5/ObjectKeysTest.java +++ b/testsrc/org/mozilla/javascript/tests/es5/ObjectKeysTest.java @@ -12,9 +12,9 @@ public class ObjectKeysTest { @Test public void shouldReturnOnlyEnumerablePropertiesOfArg() { NativeObject object = new NativeObject(); - object.defineProperty("a", "1", ScriptableObject.EMPTY); - object.defineProperty("b", "2", ScriptableObject.EMPTY); - object.defineProperty("c", "3", ScriptableObject.DONTENUM); + object.defineProperty("a", "1", ScriptableObject.EMPTY, false); + object.defineProperty("b", "2", ScriptableObject.EMPTY, false); + object.defineProperty("c", "3", ScriptableObject.DONTENUM, false); Object result = eval("Object.keys(obj)", "obj", object); diff --git a/toolsrc/org/mozilla/javascript/tools/shell/Environment.java b/toolsrc/org/mozilla/javascript/tools/shell/Environment.java index 6036d071c5..dbfd8fed6a 100644 --- a/toolsrc/org/mozilla/javascript/tools/shell/Environment.java +++ b/toolsrc/org/mozilla/javascript/tools/shell/Environment.java @@ -111,9 +111,9 @@ public Object get(String name, Scriptable start) { } @Override - public void put(String name, Scriptable start, Object value) { + public void put(String name, Scriptable start, Object value, boolean checked) { if (this == thePrototypeInstance) - super.put(name, start, value); + super.put(name, start, value, checked); else System.getProperties().put(name, ScriptRuntime.toString(value)); } diff --git a/toolsrc/org/mozilla/javascript/tools/shell/Global.java b/toolsrc/org/mozilla/javascript/tools/shell/Global.java index c85f3c7949..b0d9d93495 100644 --- a/toolsrc/org/mozilla/javascript/tools/shell/Global.java +++ b/toolsrc/org/mozilla/javascript/tools/shell/Global.java @@ -151,10 +151,10 @@ public void init(Context cx) Environment.defineClass(this); Environment environment = new Environment(this); defineProperty("environment", environment, - ScriptableObject.DONTENUM); + ScriptableObject.DONTENUM, false); history = (NativeArray) cx.newArray(this, 0); - defineProperty("history", history, ScriptableObject.DONTENUM); + defineProperty("history", history, ScriptableObject.DONTENUM, false); initialized = true; } diff --git a/toolsrc/org/mozilla/javascript/tools/shell/Main.java b/toolsrc/org/mozilla/javascript/tools/shell/Main.java index f87519fde3..ae7fca74cb 100644 --- a/toolsrc/org/mozilla/javascript/tools/shell/Main.java +++ b/toolsrc/org/mozilla/javascript/tools/shell/Main.java @@ -211,7 +211,7 @@ static void processFiles(Context cx, String[] args) System.arraycopy(args, 0, array, 0, args.length); Scriptable argsObj = cx.newArray(global, array); global.defineProperty("arguments", argsObj, - ScriptableObject.DONTENUM); + ScriptableObject.DONTENUM, false); for (String file: fileList) { processSource(cx, file); @@ -513,7 +513,7 @@ public static void processSource(Context cx, String filename) } } NativeArray h = global.history; - h.put((int)h.getLength(), h, source); + h.put((int)h.getLength(), h, source, false); } } ps.println(); diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java index ed5c33d07b..eeea3cce0e 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java @@ -110,7 +110,7 @@ public boolean has(int index, Scriptable start) { } @Override - public void put(int index, Scriptable start, Object value) { + public void put(int index, Scriptable start, Object value, boolean checked) { // TODO Clarify the following comment and add a reference to the spec // The comment: Spec says assignment to indexed XML object should return type error throw ScriptRuntime.typeError("Assignment to indexed XML is not allowed"); diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java index 4a01db87ee..ebb44d35f8 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java @@ -209,7 +209,7 @@ private void replaceNode(XML xml, XML with) { } @Override - public void put(int index, Scriptable start, Object value) { + public void put(int index, Scriptable start, Object value, boolean checked) { Object parent = Undefined.instance; // Convert text into XML if needed. XMLObject xmlValue; diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java index a6eeee5642..1cb0d36a08 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java @@ -291,14 +291,14 @@ public final void put(Context cx, Object id, Object value) { if (xmlName == null) { long index = ScriptRuntime.lastUint32Result(cx); // XXX Fix this cast - put((int)index, this, value); + put((int)index, this, value, false); return; } putXMLProperty(xmlName, value); } @Override - public void put(String name, Scriptable start, Object value) { + public void put(String name, Scriptable start, Object value, boolean checked) { Context cx = Context.getCurrentContext(); putXMLProperty(lib.toXMLNameFromString(cx, name), value); } From 6edf9420e4e80340543ad2172d4c17d5d40d6e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 044/111] Add missing @Deprecated and @Override annotations --- .../javascript/ClassDefinitionException.java | 1 + .../javascript/NotAFunctionException.java | 1 + .../mozilla/javascript/PropertyException.java | 1 + .../xml/impl/xmlbeans/LogicalEquality.java | 12 ++-- .../xml/impl/xmlbeans/Namespace.java | 12 ++++ .../javascript/xml/impl/xmlbeans/QName.java | 12 ++++ .../javascript/xml/impl/xmlbeans/XML.java | 55 +++++++++++++++++++ .../javascript/xml/impl/xmlbeans/XMLCtor.java | 7 +++ .../xml/impl/xmlbeans/XMLLibImpl.java | 6 ++ .../javascript/xml/impl/xmlbeans/XMLList.java | 55 +++++++++++++++++++ .../javascript/xml/impl/xmlbeans/XMLName.java | 5 ++ .../xml/impl/xmlbeans/XMLObjectImpl.java | 11 ++++ .../xml/impl/xmlbeans/XMLWithScope.java | 1 + src/org/mozilla/javascript/ClassCache.java | 2 + src/org/mozilla/javascript/Context.java | 10 ++++ .../mozilla/javascript/ContextFactory.java | 2 + .../mozilla/javascript/ContextListener.java | 3 + src/org/mozilla/javascript/EcmaError.java | 6 ++ .../javascript/EvaluatorException.java | 4 ++ .../mozilla/javascript/FunctionObject.java | 1 + .../mozilla/javascript/ImporterTopLevel.java | 1 + .../javascript/JavaScriptException.java | 3 + src/org/mozilla/javascript/NativeArray.java | 1 + .../mozilla/javascript/NativeFunction.java | 1 + src/org/mozilla/javascript/NativeGlobal.java | 2 + .../mozilla/javascript/NativeJavaObject.java | 2 + .../mozilla/javascript/NativeJavaPackage.java | 2 + src/org/mozilla/javascript/NativeObject.java | 3 + .../mozilla/javascript/RhinoException.java | 1 + .../javascript/RhinoSecurityManager.java | 4 +- src/org/mozilla/javascript/ScriptRuntime.java | 6 ++ .../mozilla/javascript/ScriptableObject.java | 4 ++ .../javascript/SecurityController.java | 1 + .../mozilla/javascript/SecurityUtilities.java | 2 +- .../mozilla/javascript/WrappedException.java | 1 + src/org/mozilla/javascript/v8dtoa/DiyFp.java | 1 + .../javascript/tests/Bug448816Test.java | 2 +- .../javascript/tests/Bug466207Test.java | 4 +- .../tests/commonjs/module/RequireJarTest.java | 5 ++ .../javascript/tools/debugger/Main.java | 6 ++ .../org/mozilla/javascript/xmlimpl/QName.java | 1 + .../javascript/xmlimpl/XMLLibImpl.java | 13 +++++ .../mozilla/javascript/xmlimpl/XMLName.java | 3 + .../mozilla/javascript/xmlimpl/XmlNode.java | 1 + 44 files changed, 265 insertions(+), 12 deletions(-) diff --git a/deprecatedsrc/org/mozilla/javascript/ClassDefinitionException.java b/deprecatedsrc/org/mozilla/javascript/ClassDefinitionException.java index 2197063614..71f5832b4c 100644 --- a/deprecatedsrc/org/mozilla/javascript/ClassDefinitionException.java +++ b/deprecatedsrc/org/mozilla/javascript/ClassDefinitionException.java @@ -43,6 +43,7 @@ * @deprecated The exception is no longer thrown by Rhino runtime as * {@link EvaluatorException} is used instead. */ +@Deprecated public class ClassDefinitionException extends RuntimeException { static final long serialVersionUID = -5637830967241712746L; diff --git a/deprecatedsrc/org/mozilla/javascript/NotAFunctionException.java b/deprecatedsrc/org/mozilla/javascript/NotAFunctionException.java index 3de53a8b0f..7d7ec862d7 100644 --- a/deprecatedsrc/org/mozilla/javascript/NotAFunctionException.java +++ b/deprecatedsrc/org/mozilla/javascript/NotAFunctionException.java @@ -44,6 +44,7 @@ * @deprecated The exception is no longer thrown by Rhino runtime as * {@link EvaluatorException} is used instead. */ +@Deprecated public class NotAFunctionException extends RuntimeException { static final long serialVersionUID = 6461524852170711724L; diff --git a/deprecatedsrc/org/mozilla/javascript/PropertyException.java b/deprecatedsrc/org/mozilla/javascript/PropertyException.java index 8d786c7948..05d3ff1b81 100644 --- a/deprecatedsrc/org/mozilla/javascript/PropertyException.java +++ b/deprecatedsrc/org/mozilla/javascript/PropertyException.java @@ -43,6 +43,7 @@ /** * @deprecated This exception is no longer thrown by Rhino runtime. */ +@Deprecated public class PropertyException extends RuntimeException { static final long serialVersionUID = -8221564865490676219L; diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/LogicalEquality.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/LogicalEquality.java index b525affd38..7aa936aa60 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/LogicalEquality.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/LogicalEquality.java @@ -172,8 +172,8 @@ else if (xmlOne.isStart()) private static boolean attributeListsEqual(XmlCursor xmlOne, XmlCursor xmlTwo) { boolean result = true; - TreeMap mapOne = loadAttributeMap(xmlOne); - TreeMap mapTwo = loadAttributeMap(xmlTwo); + TreeMap mapOne = loadAttributeMap(xmlOne); + TreeMap mapTwo = loadAttributeMap(xmlTwo); if (mapOne.size() != mapTwo.size()) { @@ -181,10 +181,10 @@ private static boolean attributeListsEqual(XmlCursor xmlOne, XmlCursor xmlTwo) } else { - Set keysOne = mapOne.keySet(); - Set keysTwo = mapTwo.keySet(); - Iterator itOne = keysOne.iterator(); - Iterator itTwo = keysTwo.iterator(); + Set keysOne = mapOne.keySet(); + Set keysTwo = mapTwo.keySet(); + Iterator itOne = keysOne.iterator(); + Iterator itTwo = keysTwo.iterator(); while (result && itOne.hasNext()) { diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/Namespace.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/Namespace.java index a01dca85ab..a2783362ab 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/Namespace.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/Namespace.java @@ -115,6 +115,7 @@ public String prefix() * * @return */ + @Override public String toString () { return uri(); @@ -129,17 +130,20 @@ public String toLocaleString () return toString(); } + @Override public boolean equals(Object obj) { if (!(obj instanceof Namespace)) return false; return equals((Namespace)obj); } + @Override public int hashCode() { return uri().hashCode(); } + @Override protected Object equivalentValues(Object value) { if (!(value instanceof Namespace)) return Scriptable.NOT_FOUND; @@ -156,6 +160,7 @@ private boolean equals(Namespace n) * * @return */ + @Override public String getClassName () { return "Namespace"; @@ -166,6 +171,7 @@ public String getClassName () * @param hint * @return */ + @Override public Object getDefaultValue (Class hint) { return uri(); @@ -177,11 +183,13 @@ public Object getDefaultValue (Class hint) Id_uri = 2, MAX_INSTANCE_ID = 2; + @Override protected int getMaxInstanceId() { return super.getMaxInstanceId() + MAX_INSTANCE_ID; } + @Override protected int findInstanceIdInfo(String s) { int id; @@ -208,6 +216,7 @@ protected int findInstanceIdInfo(String s) } // #/string_id_map# + @Override protected String getInstanceIdName(int id) { switch (id - super.getMaxInstanceId()) { @@ -217,6 +226,7 @@ protected String getInstanceIdName(int id) return super.getInstanceIdName(id); } + @Override protected Object getInstanceIdValue(int id) { switch (id - super.getMaxInstanceId()) { @@ -237,6 +247,7 @@ protected Object getInstanceIdValue(int id) Id_toSource = 3, MAX_PROTOTYPE_ID = 3; + @Override protected int findPrototypeId(String s) { int id; @@ -256,6 +267,7 @@ protected int findPrototypeId(String s) } // #/string_id_map# + @Override protected void initPrototypeId(int id) { String s; diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/QName.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/QName.java index 836ab1418e..b2e7f47f91 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/QName.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/QName.java @@ -76,6 +76,7 @@ void exportAsJSClass(boolean sealed) * * @return */ + @Override public String toString() { String result; @@ -111,17 +112,20 @@ String uri() return uri; } + @Override public boolean equals(Object obj) { if(!(obj instanceof QName)) return false; return equals((QName)obj); } + @Override public int hashCode() { return localName.hashCode() ^ (uri == null ? 0 : uri.hashCode()); } + @Override protected Object equivalentValues(Object value) { if(!(value instanceof QName)) return Scriptable.NOT_FOUND; @@ -146,6 +150,7 @@ private boolean equals(QName q) * * @return */ + @Override public String getClassName () { return "QName"; @@ -156,6 +161,7 @@ public String getClassName () * @param hint * @return */ + @Override public Object getDefaultValue (Class hint) { return toString(); @@ -167,11 +173,13 @@ public Object getDefaultValue (Class hint) Id_uri = 2, MAX_INSTANCE_ID = 2; + @Override protected int getMaxInstanceId() { return super.getMaxInstanceId() + MAX_INSTANCE_ID; } + @Override protected int findInstanceIdInfo(String s) { int id; @@ -198,6 +206,7 @@ protected int findInstanceIdInfo(String s) } // #/string_id_map# + @Override protected String getInstanceIdName(int id) { switch (id - super.getMaxInstanceId()) { @@ -207,6 +216,7 @@ protected String getInstanceIdName(int id) return super.getInstanceIdName(id); } + @Override protected Object getInstanceIdValue(int id) { switch (id - super.getMaxInstanceId()) { @@ -223,6 +233,7 @@ protected Object getInstanceIdValue(int id) Id_toSource = 3, MAX_PROTOTYPE_ID = 3; + @Override protected int findPrototypeId(String s) { int id; @@ -242,6 +253,7 @@ protected int findPrototypeId(String s) } // #/string_id_map# + @Override protected void initPrototypeId(int id) { String s; diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java index f95c94187d..42a6eed9cd 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java @@ -1117,6 +1117,7 @@ private XML createAttributeObject (XmlCursor attrCurs) // // + @Override public String getClassName () { return "XML"; @@ -1135,6 +1136,7 @@ public String getClassName () * @param start * @return */ + @Override public Object get(int index, Scriptable start) { //Log("get index: " + index); @@ -1155,6 +1157,7 @@ public Object get(int index, Scriptable start) * @param xmlName * @return */ + @Override boolean hasXMLProperty(XMLName xmlName) { // Has now should return true if the property would have results > 0 @@ -1168,6 +1171,7 @@ boolean hasXMLProperty(XMLName xmlName) * @param start * @return */ + @Override public boolean has(int index, Scriptable start) { return (index == 0); @@ -1177,6 +1181,7 @@ public boolean has(int index, Scriptable start) * * @return */ + @Override public Object[] getIds() { Object[] enumObjs; @@ -1210,6 +1215,7 @@ public Object[] getIds() * @param xmlName * @return */ + @Override Object getXMLProperty(XMLName xmlName) { return getPropertyList(xmlName); @@ -1220,6 +1226,7 @@ Object getXMLProperty(XMLName xmlName) * @param xmlName * @param value */ + @Override void putXMLProperty(XMLName xmlName, Object value) { //Log("put property: " + name + " value: " + value.getClass()); @@ -1313,6 +1320,7 @@ else if (xmlName.uri() == null && * @param start * @param value */ + @Override public void put(int index, Scriptable start, Object value, boolean checked) { // Spec says assignment to indexed XML object should return type error @@ -1324,6 +1332,7 @@ public void put(int index, Scriptable start, Object value, boolean checked) * * @param name */ + @Override void deleteXMLProperty(XMLName name) { if (!name.isDescendants() && name.isAttributeName()) @@ -1365,6 +1374,7 @@ void deleteXMLProperty(XMLName name) * * @param index */ + @Override public void delete(int index) { if (index == 0) @@ -1833,6 +1843,7 @@ private boolean qnameMatches(XMLName template, javax.xml.namespace.QName match) * * @param toAdd */ + @Override XML addNamespace(Namespace ns) { // When a namespace is used it will be added automatically @@ -1899,6 +1910,7 @@ XML addNamespace(Namespace ns) * @param xml * @return */ + @Override XML appendChild(Object xml) { XmlCursor curs = newCursor(); @@ -1926,6 +1938,7 @@ XML appendChild(Object xml) * @param name * @return */ + @Override XMLList attribute(XMLName xmlName) { return matchAttributes(xmlName); @@ -1935,12 +1948,14 @@ XMLList attribute(XMLName xmlName) * * @return */ + @Override XMLList attributes() { XMLName xmlName = XMLName.formStar(); return matchAttributes(xmlName); } + @Override XMLList child(long index) { XMLList result = new XMLList(lib); @@ -1949,6 +1964,7 @@ XMLList child(long index) return result; } + @Override XMLList child(XMLName xmlName) { if (xmlName == null) @@ -1991,6 +2007,7 @@ XML getXmlChild(long index) * * @return */ + @Override int childIndex() { int index = 0; @@ -2050,6 +2067,7 @@ else if (tt.isComment() || tt.isProcinst()) * * @return */ + @Override XMLList children() { return allChildNodes(null); @@ -2059,6 +2077,7 @@ XMLList children() * * @return */ + @Override XMLList comments() { return matchChildren(XmlCursor.TokenType.COMMENT); @@ -2069,6 +2088,7 @@ XMLList comments() * @param xml * @return */ + @Override boolean contains(Object xml) { boolean result = false; @@ -2085,6 +2105,7 @@ boolean contains(Object xml) * * @return */ + @Override Object copy() { XmlCursor srcCurs = newCursor(); @@ -2112,6 +2133,7 @@ Object copy() * @param name * @return */ + @Override XMLList descendants(XMLName xmlName) { XMLList result; @@ -2134,6 +2156,7 @@ XMLList descendants(XMLName xmlName) * * @return Array of all Namespaces in scope for this XML Object. */ + @Override Object[] inScopeNamespaces() { XmlCursor cursor = newCursor(); @@ -2147,6 +2170,7 @@ Object[] inScopeNamespaces() * @param child * @param xml */ + @Override XML insertChildAfter(Object child, Object xml) { if (child == null) @@ -2167,6 +2191,7 @@ else if (child instanceof XML) * @param child * @param xml */ + @Override XML insertChildBefore(Object child, Object xml) { if (child == null) @@ -2186,6 +2211,7 @@ else if (child instanceof XML) * * @return */ + @Override boolean hasOwnProperty(XMLName xmlName) { boolean hasProperty = false; @@ -2207,6 +2233,7 @@ boolean hasOwnProperty(XMLName xmlName) * * @return */ + @Override boolean hasComplexContent() { return !hasSimpleContent(); @@ -2216,6 +2243,7 @@ boolean hasComplexContent() * * @return */ + @Override boolean hasSimpleContent() { boolean simpleContent = false; @@ -2243,6 +2271,7 @@ boolean hasSimpleContent() * * @return */ + @Override int length() { return 1; @@ -2252,6 +2281,7 @@ int length() * * @return */ + @Override String localName() { XmlCursor cursor = newCursor(); @@ -2277,6 +2307,7 @@ String localName() * * @return The qualified name associated with this XML object. */ + @Override QName name() { XmlCursor cursor = newCursor(); @@ -2312,6 +2343,7 @@ QName name() * @param prefix * @return */ + @Override Object namespace(String prefix) { XmlCursor cursor = newCursor(); @@ -2354,6 +2386,7 @@ Object namespace(String prefix) * * @return */ + @Override Object[] namespaceDeclarations() { XmlCursor cursor = newCursor(); @@ -2366,6 +2399,7 @@ Object[] namespaceDeclarations() * * @return */ + @Override Object nodeKind() { String result; @@ -2403,6 +2437,7 @@ else if (tt == XmlCursor.TokenType.START) /** * */ + @Override void normalize() { XmlCursor curs = newCursor(); @@ -2479,6 +2514,7 @@ else if (tt.isEnddoc()) * * @return */ + @Override Object parent() { Object parent; @@ -2521,6 +2557,7 @@ Object parent() * @param xml * @return */ + @Override XML prependChild (Object xml) { XmlCursor curs = newCursor(); @@ -2544,6 +2581,7 @@ XML prependChild (Object xml) * * @return */ + @Override Object processingInstructions(XMLName xmlName) { return matchChildren(XmlCursor.TokenType.PROCINST, xmlName); @@ -2554,6 +2592,7 @@ Object processingInstructions(XMLName xmlName) * @param name * @return */ + @Override boolean propertyIsEnumerable(Object name) { boolean result; @@ -2573,6 +2612,7 @@ boolean propertyIsEnumerable(Object name) * * @param namespace */ + @Override XML removeNamespace(Namespace ns) { XmlCursor cursor = newCursor(); @@ -2679,6 +2719,7 @@ else if(nsURI.equals(prefixToURI.get(nsPrefix))) return this; } + @Override XML replace(long index, Object xml) { XMLList xlChildToReplace = child(index); @@ -2698,6 +2739,7 @@ XML replace(long index, Object xml) * @param xml * @return */ + @Override XML replace(XMLName xmlName, Object xml) { putXMLProperty(xmlName, xml); @@ -2708,6 +2750,7 @@ XML replace(XMLName xmlName, Object xml) * * @param xml */ + @Override XML setChildren(Object xml) { // remove all children @@ -2725,6 +2768,7 @@ XML setChildren(Object xml) * * @param name */ + @Override void setLocalName(String localName) { XmlCursor cursor = newCursor(); @@ -2751,6 +2795,7 @@ void setLocalName(String localName) * * @param name */ + @Override void setName(QName qname) { XmlCursor cursor = newCursor(); @@ -2785,6 +2830,7 @@ void setName(QName qname) * * @param ns */ + @Override void setNamespace(Namespace ns) { XmlCursor cursor = newCursor(); @@ -2815,6 +2861,7 @@ void setNamespace(Namespace ns) * * @return */ + @Override XMLList text() { return matchChildren(XmlCursor.TokenType.TEXT); @@ -2824,6 +2871,7 @@ XMLList text() * * @return */ + @Override public String toString() { String result; @@ -2850,6 +2898,7 @@ else if (curs.isStart() && hasSimpleContent()) return result; } + @Override String toSource(int indent) { // XXX Does toXMLString always return valid XML literal? @@ -2860,6 +2909,7 @@ String toSource(int indent) * * @return */ + @Override String toXMLString(int indent) { // XXX indent is ignored @@ -2919,6 +2969,7 @@ else if (curs.isComment() || curs.isProcinst()) * * @return */ + @Override Object valueOf() { return this; @@ -2933,6 +2984,7 @@ Object valueOf() * @param target * @return */ + @Override boolean equivalentXml(Object target) { boolean result = false; @@ -3013,6 +3065,7 @@ else if (name.isAttributeName()) return result; } + @Override protected Object jsConstructor(Context cx, boolean inNewExpr, Object[] args) { @@ -3031,6 +3084,7 @@ protected Object jsConstructor(Context cx, boolean inNewExpr, /** * See ECMA 357, 11_2_2_1, Semantics, 3_f. */ + @Override public Scriptable getExtraMethodSource(Context cx) { if (hasSimpleContent()) { @@ -3040,6 +3094,7 @@ public Scriptable getExtraMethodSource(Context cx) return null; } + @Override XmlObject getXmlObject() { XmlObject xo; diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLCtor.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLCtor.java index 0c55619442..460dc2268a 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLCtor.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLCtor.java @@ -106,11 +106,13 @@ private void readSettings(Scriptable source) MAX_INSTANCE_ID = 5; + @Override protected int getMaxInstanceId() { return super.getMaxInstanceId() + MAX_INSTANCE_ID; } + @Override protected int findInstanceIdInfo(String s) { int id; // #generated# Last update: 2004-07-19 13:03:52 CEST @@ -146,6 +148,7 @@ protected int findInstanceIdInfo(String s) { // #/string_id_map# + @Override protected String getInstanceIdName(int id) { switch (id - super.getMaxInstanceId()) { @@ -158,6 +161,7 @@ protected String getInstanceIdName(int id) return super.getInstanceIdName(id); } + @Override protected Object getInstanceIdValue(int id) { switch (id - super.getMaxInstanceId()) { @@ -175,6 +179,7 @@ protected Object getInstanceIdValue(int id) return super.getInstanceIdValue(id); } + @Override protected void setInstanceIdValue(int id, Object value) { switch (id - super.getMaxInstanceId()) { @@ -204,6 +209,7 @@ protected void setInstanceIdValue(int id, Object value) Id_setSettings = 3, MAX_FUNCTION_ID = 3; + @Override protected int findPrototypeId(String s) { int id; @@ -220,6 +226,7 @@ protected int findPrototypeId(String s) } // #/string_id_map# + @Override protected void initPrototypeId(int id) { String s; diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLLibImpl.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLLibImpl.java index 90da7d446a..0ca45e357c 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLLibImpl.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLLibImpl.java @@ -517,6 +517,7 @@ Object addXMLObjects(Context cx, XMLObject obj1, XMLObject obj2) /** * See E4X 13.1.2.1. */ + @Override public boolean isXMLName(Context cx, Object nameObj) { String name; @@ -636,6 +637,7 @@ XMLName toQualifiedName(Context cx, Object namespaceValue, return XMLName.formProperty(uri, localName); } + @Override public Ref nameRef(Context cx, Object name, Scriptable scope, int memberTypeFlags) { @@ -647,6 +649,7 @@ public Ref nameRef(Context cx, Object name, return xmlPrimaryReference(cx, xmlName, scope); } + @Override public Ref nameRef(Context cx, Object namespace, Object name, Scriptable scope, int memberTypeFlags) { @@ -697,6 +700,7 @@ private Ref xmlPrimaryReference(Context cx, XMLName xmlName, * @param value Unescaped text * @return The escaped text */ + @Override public String escapeAttributeValue(Object value) { String text = ScriptRuntime.toString(value); @@ -723,6 +727,7 @@ public String escapeAttributeValue(Object value) * @param value Unescaped text * @return The escaped text */ + @Override public String escapeTextValue(Object value) { if (value instanceof XMLObjectImpl) { @@ -747,6 +752,7 @@ public String escapeTextValue(Object value) return (begin < end) ? elementText.substring(begin, end) : ""; } + @Override public Object toDefaultXmlNamespace(Context cx, Object uriValue) { return constructNamespace(cx, uriValue); diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java index d8640a0c0f..5e60969b76 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java @@ -288,6 +288,7 @@ private void insert(int index, XML xml) // // + @Override public String getClassName () { return "XMLList"; @@ -305,6 +306,7 @@ public String getClassName () * @param start * @return */ + @Override public Object get(int index, Scriptable start) { //Log("get index: " + index); @@ -324,6 +326,7 @@ public Object get(int index, Scriptable start) * @param xmlName * @return */ + @Override boolean hasXMLProperty(XMLName xmlName) { // Has now should return true if the property would have results > 0 @@ -337,6 +340,7 @@ boolean hasXMLProperty(XMLName xmlName) * @param start * @return */ + @Override public boolean has(int index, Scriptable start) { return 0 <= index && index < length(); @@ -347,6 +351,7 @@ public boolean has(int index, Scriptable start) * @param name * @param value */ + @Override void putXMLProperty(XMLName xmlName, Object value) { //Log("put property: " + name); @@ -418,6 +423,7 @@ else if(xmlName.isAttributeName()) * @param name * @return */ + @Override Object getXMLProperty(XMLName name) { return getPropertyList(name); @@ -428,6 +434,7 @@ Object getXMLProperty(XMLName name) * @param index * @param value */ + @Override public void put(int index, Scriptable start, Object value, boolean checked) { Object parent = Undefined.instance; @@ -554,6 +561,7 @@ else if (xmlValue instanceof XMLList) * * @param name */ + @Override void deleteXMLProperty(XMLName name) { for (int i = 0; i < length(); i++) @@ -571,6 +579,7 @@ void deleteXMLProperty(XMLName name) * * @param index */ + @Override public void delete(int index) { if (index >= 0 && index < length()) @@ -588,6 +597,7 @@ public void delete(int index) * * @return */ + @Override public Object[] getIds() { Object enumObjs[]; @@ -701,6 +711,7 @@ else if (toAdd instanceof XML.XScriptAnnotation) * * @param toAdd */ + @Override XML addNamespace(Namespace ns) { if(length() == 1) @@ -718,6 +729,7 @@ XML addNamespace(Namespace ns) * @param xml * @return */ + @Override XML appendChild(Object xml) { if (length() == 1) @@ -735,6 +747,7 @@ XML appendChild(Object xml) * @param attr * @return */ + @Override XMLList attribute(XMLName xmlName) { XMLList result = new XMLList(lib); @@ -752,6 +765,7 @@ XMLList attribute(XMLName xmlName) * * @return */ + @Override XMLList attributes() { XMLList result = new XMLList(lib); @@ -765,6 +779,7 @@ XMLList attributes() return result; } + @Override XMLList child(long index) { XMLList result = new XMLList(lib); @@ -777,6 +792,7 @@ XMLList child(long index) return result; } + @Override XMLList child(XMLName xmlName) { XMLList result = new XMLList(lib); @@ -793,6 +809,7 @@ XMLList child(XMLName xmlName) * * @return */ + @Override int childIndex() { if (length() == 1) @@ -809,6 +826,7 @@ int childIndex() * * @return */ + @Override XMLList children() { Vector v = new Vector(); @@ -848,6 +866,7 @@ XMLList children() * * @return */ + @Override XMLList comments() { XMLList result = new XMLList(lib); @@ -867,6 +886,7 @@ XMLList comments() * @param xml * @return */ + @Override boolean contains(Object xml) { boolean result = false; @@ -889,6 +909,7 @@ boolean contains(Object xml) * * @return */ + @Override Object copy() { XMLList result = new XMLList(lib); @@ -906,6 +927,7 @@ Object copy() * * @return */ + @Override XMLList descendants(XMLName xmlName) { XMLList result = new XMLList(lib); @@ -923,6 +945,7 @@ XMLList descendants(XMLName xmlName) * * @return */ + @Override Object[] inScopeNamespaces() { if(length() == 1) @@ -940,6 +963,7 @@ Object[] inScopeNamespaces() * @param child * @param xml */ + @Override XML insertChildAfter(Object child, Object xml) { if (length() == 1) @@ -957,6 +981,7 @@ XML insertChildAfter(Object child, Object xml) * @param child * @param xml */ + @Override XML insertChildBefore(Object child, Object xml) { if (length() == 1) @@ -973,6 +998,7 @@ XML insertChildBefore(Object child, Object xml) * * @return */ + @Override boolean hasOwnProperty(XMLName xmlName) { boolean hasProperty = false; @@ -994,6 +1020,7 @@ boolean hasOwnProperty(XMLName xmlName) * * @return */ + @Override boolean hasComplexContent() { boolean complexContent; @@ -1029,6 +1056,7 @@ else if (length == 1) * * @return */ + @Override boolean hasSimpleContent() { boolean simpleContent; @@ -1064,6 +1092,7 @@ else if (length == 1) * * @return */ + @Override int length() { int result = 0; @@ -1080,6 +1109,7 @@ int length() * * @return */ + @Override String localName() { if (length() == 1) @@ -1096,6 +1126,7 @@ String localName() * * @return */ + @Override QName name() { if (length() == 1) @@ -1113,6 +1144,7 @@ QName name() * @param prefix * @return */ + @Override Object namespace(String prefix) { if (length() == 1) @@ -1129,6 +1161,7 @@ Object namespace(String prefix) * * @return */ + @Override Object[] namespaceDeclarations() { if (length() == 1) @@ -1145,6 +1178,7 @@ Object[] namespaceDeclarations() * * @return */ + @Override Object nodeKind() { if (length() == 1) @@ -1160,6 +1194,7 @@ Object nodeKind() /** * */ + @Override void normalize() { for (int i = 0; i < length(); i++) @@ -1174,6 +1209,7 @@ void normalize() * * @return */ + @Override Object parent() { Object sameParent = Undefined.instance; @@ -1210,6 +1246,7 @@ else if (sameParent != currParent) * @param xml * @return */ + @Override XML prependChild(Object xml) { if (length() == 1) @@ -1226,6 +1263,7 @@ XML prependChild(Object xml) * * @return */ + @Override Object processingInstructions(XMLName xmlName) { XMLList result = new XMLList(lib); @@ -1245,6 +1283,7 @@ Object processingInstructions(XMLName xmlName) * @param name * @return */ + @Override boolean propertyIsEnumerable(Object name) { long index; @@ -1271,6 +1310,7 @@ boolean propertyIsEnumerable(Object name) * * @param ns */ + @Override XML removeNamespace(Namespace ns) { if(length() == 1) @@ -1283,6 +1323,7 @@ XML removeNamespace(Namespace ns) } } + @Override XML replace(long index, Object xml) { if (length() == 1) @@ -1301,6 +1342,7 @@ XML replace(long index, Object xml) * @param xml * @return */ + @Override XML replace(XMLName xmlName, Object xml) { if (length() == 1) @@ -1317,6 +1359,7 @@ XML replace(XMLName xmlName, Object xml) * * @param xml */ + @Override XML setChildren(Object xml) { if (length() == 1) @@ -1333,6 +1376,7 @@ XML setChildren(Object xml) * * @param name */ + @Override void setLocalName(String localName) { if (length() == 1) @@ -1349,6 +1393,7 @@ void setLocalName(String localName) * * @param name */ + @Override void setName(QName qname) { if (length() == 1) @@ -1365,6 +1410,7 @@ void setName(QName qname) * * @param ns */ + @Override void setNamespace(Namespace ns) { if (length() == 1) @@ -1381,6 +1427,7 @@ void setNamespace(Namespace ns) * * * @return */ + @Override XMLList text() { XMLList result = new XMLList(lib); @@ -1397,6 +1444,7 @@ XMLList text() * * @return */ + @Override public String toString() { if (hasSimpleContent()) @@ -1417,6 +1465,7 @@ public String toString() } } + @Override String toSource(int indent) { // XXX indent is ignored @@ -1427,6 +1476,7 @@ String toSource(int indent) * * @return */ + @Override String toXMLString(int indent) { StringBuffer sb = new StringBuffer(); @@ -1448,6 +1498,7 @@ String toXMLString(int indent) * * @return */ + @Override Object valueOf() { return this; @@ -1462,6 +1513,7 @@ Object valueOf() * @param target * @return */ + @Override boolean equivalentXml(Object target) { boolean result = false; @@ -1539,6 +1591,7 @@ private Object applyOrCall(boolean isApply, return ScriptRuntime.applyOrCall(isApply, cx, scope, thisObj, args); } + @Override protected Object jsConstructor(Context cx, boolean inNewExpr, Object[] args) { @@ -1554,6 +1607,7 @@ protected Object jsConstructor(Context cx, boolean inNewExpr, } } + @Override org.apache.xmlbeans.XmlObject getXmlObject() { if (length() == 1) { @@ -1566,6 +1620,7 @@ org.apache.xmlbeans.XmlObject getXmlObject() /** * See ECMA 357, 11_2_2_1, Semantics, 3_e. */ + @Override public Scriptable getExtraMethodSource(Context cx) { if (length() == 1) { diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLName.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLName.java index 8ff972ec42..a5cd65c006 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLName.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLName.java @@ -110,6 +110,7 @@ void setIsDescendants() isDescendants = true; } + @Override public boolean has(Context cx) { if (xmlObject == null) { @@ -118,6 +119,7 @@ public boolean has(Context cx) return xmlObject.hasXMLProperty(this); } + @Override public Object get(Context cx) { if (xmlObject == null) { @@ -127,6 +129,7 @@ public Object get(Context cx) return xmlObject.getXMLProperty(this); } + @Override public Object set(Context cx, Object value) { if (xmlObject == null) { @@ -141,6 +144,7 @@ public Object set(Context cx, Object value) return value; } + @Override public boolean delete(Context cx) { if (xmlObject == null) { @@ -150,6 +154,7 @@ public boolean delete(Context cx) return !xmlObject.hasXMLProperty(this); } + @Override public String toString() { //return qname.localName(); diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java index bcb54cfa0b..190f91e417 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java @@ -129,6 +129,7 @@ protected XMLObjectImpl(XMLLibImpl lib, XMLObject prototype) abstract void setName(QName xmlName); abstract void setNamespace(Namespace ns); abstract XMLList text(); + @Override public abstract String toString(); abstract String toSource(int indent); abstract String toXMLString(int indent); @@ -149,6 +150,7 @@ protected abstract Object jsConstructor(Context cx, boolean inNewExpr, // // + @Override public final Object getDefaultValue(Class hint) { return toString(); @@ -160,6 +162,7 @@ public final Object getDefaultValue(Class hint) * never returns {@link Scriptable#NOT_FOUND} for them but rather * calls equivalentXml(value) and wrap the result as Boolean. */ + @Override protected final Object equivalentValues(Object value) { boolean result = equivalentXml(value); @@ -180,6 +183,7 @@ public final XMLLib lib() /** * Implementation of ECMAScript [[Has]] */ + @Override public final boolean has(Context cx, Object id) { if (cx == null) cx = Context.getCurrentContext(); @@ -294,6 +298,7 @@ public Object getFunctionProperty(Context cx, String name) { return NOT_FOUND; } + @Override public Ref memberRef(Context cx, Object elem, int memberTypeFlags) { XMLName xmlName; @@ -318,6 +323,7 @@ public Ref memberRef(Context cx, Object elem, int memberTypeFlags) /** * Generic reference to implement x::ns, x.@ns::y, x..@ns::y etc. */ + @Override public Ref memberRef(Context cx, Object namespace, Object elem, int memberTypeFlags) { @@ -334,11 +340,13 @@ public Ref memberRef(Context cx, Object namespace, Object elem, return xmlName; } + @Override public NativeWith enterWith(Scriptable scope) { return new XMLWithScope(lib, scope, this); } + @Override public NativeWith enterDotQuery(Scriptable scope) { XMLWithScope xws = new XMLWithScope(lib, scope, this); @@ -346,6 +354,7 @@ public NativeWith enterDotQuery(Scriptable scope) return xws; } + @Override public final Object addValues(Context cx, boolean thisIsLeft, Object value) { @@ -428,6 +437,7 @@ final void exportAsJSClass(boolean sealed) MAX_PROTOTYPE_ID = 41; + @Override protected int findPrototypeId(String s) { int id; @@ -505,6 +515,7 @@ protected int findPrototypeId(String s) } // #/string_id_map# + @Override protected void initPrototypeId(int id) { String s; diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLWithScope.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLWithScope.java index 67a778d570..d0baef9bc9 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLWithScope.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLWithScope.java @@ -82,6 +82,7 @@ void initAsDotQuery() _xmlList = new XMLList(lib); } + @Override protected Object updateDotQuery(boolean value) { // Return null to continue looping diff --git a/src/org/mozilla/javascript/ClassCache.java b/src/org/mozilla/javascript/ClassCache.java index 1260dd3c93..5e06530313 100644 --- a/src/org/mozilla/javascript/ClassCache.java +++ b/src/org/mozilla/javascript/ClassCache.java @@ -179,6 +179,7 @@ Map> getInterfaceAdapterCacheMap() * The method always returns false. * @see #setInvokerOptimizationEnabled(boolean enabled) */ + @Deprecated public boolean isInvokerOptimizationEnabled() { return false; @@ -193,6 +194,7 @@ public boolean isInvokerOptimizationEnabled() * small speed increase that can be gained using generated proxy class * to replace reflection. */ + @Deprecated public synchronized void setInvokerOptimizationEnabled(boolean enabled) { } diff --git a/src/org/mozilla/javascript/Context.java b/src/org/mozilla/javascript/Context.java index f4fb688ee6..020c2f97b4 100644 --- a/src/org/mozilla/javascript/Context.java +++ b/src/org/mozilla/javascript/Context.java @@ -215,6 +215,7 @@ public class Context * @deprecated In previous releases, this name was given to * FEATURE_PARENT_PROTO_PROPERTIES. */ + @Deprecated public static final int FEATURE_PARENT_PROTO_PROPRTIES = 5; /** @@ -336,6 +337,7 @@ public class Context * this class, consider using {@link #Context(ContextFactory)} constructor * instead in the subclasses' constructors. */ + @Deprecated public Context() { this(ContextFactory.getGlobal()); @@ -407,6 +409,7 @@ public static Context enter() * @see ContextFactory#enterContext(Context) * @see ContextFactory#call(ContextAction) */ + @Deprecated public static Context enter(Context cx) { return enter(cx, ContextFactory.getGlobal()); @@ -478,6 +481,7 @@ public static void exit() * ContextFactory. * @return The result of {@link ContextAction#run(Context)}. */ + @Deprecated public static Object call(ContextAction action) { return call(ContextFactory.getGlobal(), action); @@ -530,6 +534,7 @@ static Object call(ContextFactory factory, ContextAction action) { * @see ContextFactory#addListener(org.mozilla.javascript.ContextFactory.Listener) * @see ContextFactory#getGlobal() */ + @Deprecated public static void addContextListener(ContextListener listener) { // Special workaround for the debugger @@ -559,6 +564,7 @@ public static void addContextListener(ContextListener listener) * @see ContextFactory#removeListener(org.mozilla.javascript.ContextFactory.Listener) * @see ContextFactory#getGlobal() */ + @Deprecated public static void removeContextListener(ContextListener listener) { ContextFactory.getGlobal().addListener(listener); @@ -1292,6 +1298,7 @@ public final boolean stringIsCompilableUnit(String source) * @see #compileReader(Reader in, String sourceName, int lineno, * Object securityDomain) */ + @Deprecated public final Script compileReader(Scriptable scope, Reader in, String sourceName, int lineno, Object securityDomain) @@ -1667,6 +1674,7 @@ public static Scriptable toObject(Object value, Scriptable scope) * @deprecated * @see #toObject(Object, Scriptable) */ + @Deprecated public static Scriptable toObject(Object value, Scriptable scope, Class staticType) { @@ -1739,6 +1747,7 @@ public static Object jsToJava(Object value, Class desiredType) * Note that {@link #jsToJava(Object, Class)} throws * {@link EvaluatorException} instead. */ + @Deprecated public static Object toType(Object value, Class desiredType) throws IllegalArgumentException { @@ -2066,6 +2075,7 @@ public final void removeThreadLocal(Object key) * @see ClassCache#get(Scriptable) * @see ClassCache#setCachingEnabled(boolean) */ + @Deprecated public static void setCachingEnabled(boolean cachingEnabled) { } diff --git a/src/org/mozilla/javascript/ContextFactory.java b/src/org/mozilla/javascript/ContextFactory.java index 9c6e556896..bfa351e655 100644 --- a/src/org/mozilla/javascript/ContextFactory.java +++ b/src/org/mozilla/javascript/ContextFactory.java @@ -584,6 +584,7 @@ public Context enterContext() * @deprecated use {@link #enterContext()} instead * @return a Context associated with the current thread */ + @Deprecated public final Context enter() { return enterContext(null); @@ -592,6 +593,7 @@ public final Context enter() /** * @deprecated Use {@link Context#exit()} instead. */ + @Deprecated public final void exit() { Context.exit(); diff --git a/src/org/mozilla/javascript/ContextListener.java b/src/org/mozilla/javascript/ContextListener.java index 5e17145c26..9b129f8260 100644 --- a/src/org/mozilla/javascript/ContextListener.java +++ b/src/org/mozilla/javascript/ContextListener.java @@ -45,16 +45,19 @@ * {@link Context} instances should implement * {@link ContextFactory.Listener}. */ +@Deprecated public interface ContextListener extends ContextFactory.Listener { /** * @deprecated Rhino runtime never calls the method. */ + @Deprecated public void contextEntered(Context cx); /** * @deprecated Rhino runtime never calls the method. */ + @Deprecated public void contextExited(Context cx); } diff --git a/src/org/mozilla/javascript/EcmaError.java b/src/org/mozilla/javascript/EcmaError.java index b8ac416e86..3b0e19f1e9 100644 --- a/src/org/mozilla/javascript/EcmaError.java +++ b/src/org/mozilla/javascript/EcmaError.java @@ -77,6 +77,7 @@ public class EcmaError extends RhinoException * @deprecated EcmaError error instances should not be constructed * explicitly since they are generated by the engine. */ + @Deprecated public EcmaError(Scriptable nativeError, String sourceName, int lineNumber, int columnNumber, String lineSource) { @@ -122,6 +123,7 @@ public String getErrorMessage() /** * @deprecated Use {@link RhinoException#sourceName()} from the super class. */ + @Deprecated public String getSourceName() { return sourceName(); @@ -130,6 +132,7 @@ public String getSourceName() /** * @deprecated Use {@link RhinoException#lineNumber()} from the super class. */ + @Deprecated public int getLineNumber() { return lineNumber(); @@ -139,6 +142,7 @@ public int getLineNumber() * @deprecated * Use {@link RhinoException#columnNumber()} from the super class. */ + @Deprecated public int getColumnNumber() { return columnNumber(); } @@ -146,6 +150,7 @@ public int getColumnNumber() { /** * @deprecated Use {@link RhinoException#lineSource()} from the super class. */ + @Deprecated public String getLineSource() { return lineSource(); } @@ -154,6 +159,7 @@ public String getLineSource() { * @deprecated * Always returns null. */ + @Deprecated public Scriptable getErrorObject() { return null; diff --git a/src/org/mozilla/javascript/EvaluatorException.java b/src/org/mozilla/javascript/EvaluatorException.java index 7b4e7cc477..84a67cc88e 100644 --- a/src/org/mozilla/javascript/EvaluatorException.java +++ b/src/org/mozilla/javascript/EvaluatorException.java @@ -91,6 +91,7 @@ public EvaluatorException(String detail, String sourceName, int lineNumber, /** * @deprecated Use {@link RhinoException#sourceName()} from the super class. */ + @Deprecated public String getSourceName() { return sourceName(); @@ -99,6 +100,7 @@ public String getSourceName() /** * @deprecated Use {@link RhinoException#lineNumber()} from the super class. */ + @Deprecated public int getLineNumber() { return lineNumber(); @@ -107,6 +109,7 @@ public int getLineNumber() /** * @deprecated Use {@link RhinoException#columnNumber()} from the super class. */ + @Deprecated public int getColumnNumber() { return columnNumber(); @@ -115,6 +118,7 @@ public int getColumnNumber() /** * @deprecated Use {@link RhinoException#lineSource()} from the super class. */ + @Deprecated public String getLineSource() { return lineSource(); diff --git a/src/org/mozilla/javascript/FunctionObject.java b/src/org/mozilla/javascript/FunctionObject.java index f5389c565c..834242958e 100644 --- a/src/org/mozilla/javascript/FunctionObject.java +++ b/src/org/mozilla/javascript/FunctionObject.java @@ -380,6 +380,7 @@ void initAsConstructor(Scriptable scope, Scriptable prototype) * and {@link #convertArg(Context, Scriptable, Object, int)} * for type conversion. */ + @Deprecated public static Object convertArg(Context cx, Scriptable scope, Object arg, Class desired) { diff --git a/src/org/mozilla/javascript/ImporterTopLevel.java b/src/org/mozilla/javascript/ImporterTopLevel.java index e28d05d6ce..7dec21199a 100644 --- a/src/org/mozilla/javascript/ImporterTopLevel.java +++ b/src/org/mozilla/javascript/ImporterTopLevel.java @@ -158,6 +158,7 @@ private Object getPackageProperty(String name, Scriptable start) { /** * @deprecated Kept only for compatibility. */ + @Deprecated public void importPackage(Context cx, Scriptable thisObj, Object[] args, Function funObj) { diff --git a/src/org/mozilla/javascript/JavaScriptException.java b/src/org/mozilla/javascript/JavaScriptException.java index 448df47389..16802af549 100644 --- a/src/org/mozilla/javascript/JavaScriptException.java +++ b/src/org/mozilla/javascript/JavaScriptException.java @@ -57,6 +57,7 @@ public class JavaScriptException extends RhinoException * Use {@link WrappedException#WrappedException(Throwable)} to report * exceptions in Java code. */ + @Deprecated public JavaScriptException(Object value) { this(value, "", 0); @@ -118,6 +119,7 @@ public Object getValue() /** * @deprecated Use {@link RhinoException#sourceName()} from the super class. */ + @Deprecated public String getSourceName() { return sourceName(); @@ -126,6 +128,7 @@ public String getSourceName() /** * @deprecated Use {@link RhinoException#lineNumber()} from the super class. */ + @Deprecated public int getLineNumber() { return lineNumber(); diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index c8733d85b7..3bea025422 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -736,6 +736,7 @@ public long getLength() { } /** @deprecated Use {@link #getLength()} instead. */ + @Deprecated public long jsGet_length() { return getLength(); } diff --git a/src/org/mozilla/javascript/NativeFunction.java b/src/org/mozilla/javascript/NativeFunction.java index 937412970a..716ea56478 100644 --- a/src/org/mozilla/javascript/NativeFunction.java +++ b/src/org/mozilla/javascript/NativeFunction.java @@ -103,6 +103,7 @@ public int getArity() * For backwards compatibility keep an old method name used by * Batik and possibly others. */ + @Deprecated public String jsGet_name() { return getFunctionName(); diff --git a/src/org/mozilla/javascript/NativeGlobal.java b/src/org/mozilla/javascript/NativeGlobal.java index c1d6976059..a3350b2bde 100644 --- a/src/org/mozilla/javascript/NativeGlobal.java +++ b/src/org/mozilla/javascript/NativeGlobal.java @@ -544,6 +544,7 @@ static boolean isEvalFunction(Object functionObj) * @deprecated Use {@link ScriptRuntime#constructError(String,String)} * instead. */ + @Deprecated public static EcmaError constructError(Context cx, String error, String message, @@ -557,6 +558,7 @@ public static EcmaError constructError(Context cx, * {@link ScriptRuntime#constructError(String,String,String,int,String,int)} * instead. */ + @Deprecated public static EcmaError constructError(Context cx, String error, String message, diff --git a/src/org/mozilla/javascript/NativeJavaObject.java b/src/org/mozilla/javascript/NativeJavaObject.java index 22f1a42e67..a217de6c2e 100644 --- a/src/org/mozilla/javascript/NativeJavaObject.java +++ b/src/org/mozilla/javascript/NativeJavaObject.java @@ -180,6 +180,7 @@ public Object[] getIds() { @deprecated Use {@link Context#getWrapFactory()} together with calling {@link WrapFactory#wrap(Context, Scriptable, Object, Class)} */ + @Deprecated public static Object wrap(Scriptable scope, Object obj, Class staticType) { Context cx = Context.getContext(); @@ -492,6 +493,7 @@ else if (value instanceof Class) { * @deprecated as of 1.5 Release 4 * @see org.mozilla.javascript.Context#jsToJava(Object, Class) */ + @Deprecated public static Object coerceType(Class type, Object value) { return coerceTypeImpl(type, value); diff --git a/src/org/mozilla/javascript/NativeJavaPackage.java b/src/org/mozilla/javascript/NativeJavaPackage.java index 24413aa775..52855fed88 100644 --- a/src/org/mozilla/javascript/NativeJavaPackage.java +++ b/src/org/mozilla/javascript/NativeJavaPackage.java @@ -73,6 +73,7 @@ public class NativeJavaPackage extends ScriptableObject * @deprecated NativeJavaPackage is an internal class, do not use * it directly. */ + @Deprecated public NativeJavaPackage(String packageName, ClassLoader classLoader) { this(false, packageName, classLoader); } @@ -81,6 +82,7 @@ public NativeJavaPackage(String packageName, ClassLoader classLoader) { * @deprecated NativeJavaPackage is an internal class, do not use * it directly. */ + @Deprecated public NativeJavaPackage(String packageName) { this(false, packageName, Context.getCurrentContext().getApplicationClassLoader()); diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index a2f2fa8e54..18113d125d 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -579,6 +579,7 @@ public Object setValue(Object value) { throw new UnsupportedOperationException(); } + @Override public boolean equals(Object other) { if (!(other instanceof Map.Entry)) { return false; @@ -588,11 +589,13 @@ public boolean equals(Object other) { && (value == null ? e.getValue() == null : value.equals(e.getValue())); } + @Override public int hashCode() { return (ekey == null ? 0 : ekey.hashCode()) ^ (value == null ? 0 : value.hashCode()); } + @Override public String toString() { return ekey + "=" + value; } diff --git a/src/org/mozilla/javascript/RhinoException.java b/src/org/mozilla/javascript/RhinoException.java index 4f39e3b24b..a7ddff0327 100644 --- a/src/org/mozilla/javascript/RhinoException.java +++ b/src/org/mozilla/javascript/RhinoException.java @@ -257,6 +257,7 @@ public String getScriptStackTrace() * @return a script stack dump * @since 1.6R6 */ + @Deprecated public String getScriptStackTrace(FilenameFilter filter) { return getScriptStackTrace(); diff --git a/src/org/mozilla/javascript/RhinoSecurityManager.java b/src/org/mozilla/javascript/RhinoSecurityManager.java index 1d39037470..bb8cfa3c7c 100644 --- a/src/org/mozilla/javascript/RhinoSecurityManager.java +++ b/src/org/mozilla/javascript/RhinoSecurityManager.java @@ -16,8 +16,8 @@ public class RhinoSecurityManager extends SecurityManager { * or null if no script is currently running */ protected Class getCurrentScriptClass() { - Class[] context = getClassContext(); - for (Class c : context) { + Class[] context = getClassContext(); + for (Class c : context) { if (c != InterpretedFunction.class && NativeFunction.class.isAssignableFrom(c) || PolicySecurityController.SecureCaller.class.isAssignableFrom(c)) { return c; diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index d1b2d457a6..d5fec2cde1 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -1000,6 +1000,7 @@ public static Scriptable toObjectOrNull(Context cx, Object obj, /** * @deprecated Use {@link #toObject(Scriptable, Object)} instead. */ + @Deprecated public static Scriptable toObject(Scriptable scope, Object val, Class staticClass) { @@ -1052,6 +1053,7 @@ public static Scriptable toObject(Context cx, Scriptable scope, Object val) /** * @deprecated Use {@link #toObject(Context, Scriptable, Object)} instead. */ + @Deprecated public static Scriptable toObject(Context cx, Scriptable scope, Object val, Class staticClass) { @@ -1076,6 +1078,7 @@ public static void checkObjectCoercible(Object val) { /** * @deprecated The method is only present for compatibility. */ + @Deprecated public static Object call(Context cx, Object fun, Object thisArg, Object[] args, Scriptable scope) { @@ -1739,6 +1742,7 @@ public static Ref specialRef(Object obj, String specialProperty, /** * @deprecated */ + @Deprecated public static Object delete(Object obj, Object id, Context cx) { return delete(obj, id, cx, false); @@ -2815,6 +2819,7 @@ private static CharSequence newConsString(CharSequence val1, CharSequence val2) /** * @deprecated The method is only present for compatibility. */ + @Deprecated public static Object nameIncrDecr(Scriptable scopeChain, String id, int incrDecrMask) { @@ -3768,6 +3773,7 @@ public static Scriptable newArrayLiteral(Object[] objects, * the version called from new code. * @deprecated This method only present for compatibility. */ + @Deprecated public static Scriptable newObjectLiteral(Object[] propertyIds, Object[] propertyValues, Context cx, Scriptable scope) diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index c5ad13c5ac..615add6b8e 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -638,6 +638,7 @@ public boolean isConst(String name) * @deprecated Use {@link #getAttributes(String name)}. The engine always * ignored the start argument. */ + @Deprecated public final int getAttributes(String name, Scriptable start) { return getAttributes(name); @@ -647,6 +648,7 @@ public final int getAttributes(String name, Scriptable start) * @deprecated Use {@link #getAttributes(int index)}. The engine always * ignored the start argument. */ + @Deprecated public final int getAttributes(int index, Scriptable start) { return getAttributes(index); @@ -656,6 +658,7 @@ public final int getAttributes(int index, Scriptable start) * @deprecated Use {@link #setAttributes(String name, int attributes)}. * The engine always ignored the start argument. */ + @Deprecated public final void setAttributes(String name, Scriptable start, int attributes) { @@ -666,6 +669,7 @@ public final void setAttributes(String name, Scriptable start, * @deprecated Use {@link #setAttributes(int index, int attributes)}. * The engine always ignored the start argument. */ + @Deprecated public void setAttributes(int index, Scriptable start, int attributes) { diff --git a/src/org/mozilla/javascript/SecurityController.java b/src/org/mozilla/javascript/SecurityController.java index 8d51a619c5..6e84e1e498 100644 --- a/src/org/mozilla/javascript/SecurityController.java +++ b/src/org/mozilla/javascript/SecurityController.java @@ -201,6 +201,7 @@ public Object exec(Context cx, Scriptable scope) * override * {@link #callWithDomain(Object securityDomain, Context cx, Callable callable, Scriptable scope, Scriptable thisObj, Object[] args)}. */ + @Deprecated public Object execWithDomain(Context cx, Scriptable scope, Script script, Object securityDomain) { diff --git a/src/org/mozilla/javascript/SecurityUtilities.java b/src/org/mozilla/javascript/SecurityUtilities.java index f676cfdef4..00f5b3a7ef 100644 --- a/src/org/mozilla/javascript/SecurityUtilities.java +++ b/src/org/mozilla/javascript/SecurityUtilities.java @@ -91,7 +91,7 @@ public static ProtectionDomain getScriptProtectionDomain() { return AccessController.doPrivileged( new PrivilegedAction() { public ProtectionDomain run() { - Class c = ((RhinoSecurityManager) securityManager) + Class c = ((RhinoSecurityManager) securityManager) .getCurrentScriptClass(); return c == null ? null : c.getProtectionDomain(); } diff --git a/src/org/mozilla/javascript/WrappedException.java b/src/org/mozilla/javascript/WrappedException.java index c749f748fd..958c851f41 100644 --- a/src/org/mozilla/javascript/WrappedException.java +++ b/src/org/mozilla/javascript/WrappedException.java @@ -84,6 +84,7 @@ public Throwable getWrappedException() /** * @deprecated Use {@link #getWrappedException()} instead. */ + @Deprecated public Object unwrap() { return getWrappedException(); diff --git a/src/org/mozilla/javascript/v8dtoa/DiyFp.java b/src/org/mozilla/javascript/v8dtoa/DiyFp.java index 1d809b5541..a53385a4a4 100644 --- a/src/org/mozilla/javascript/v8dtoa/DiyFp.java +++ b/src/org/mozilla/javascript/v8dtoa/DiyFp.java @@ -137,6 +137,7 @@ static DiyFp normalize(DiyFp a) { void setF(long new_value) { f = new_value; } void setE(int new_value) { e = new_value; } + @Override public DiyFp clone() { try { return (DiyFp) super.clone(); diff --git a/testsrc/org/mozilla/javascript/tests/Bug448816Test.java b/testsrc/org/mozilla/javascript/tests/Bug448816Test.java index f8db8d5a3a..5f0db01eed 100644 --- a/testsrc/org/mozilla/javascript/tests/Bug448816Test.java +++ b/testsrc/org/mozilla/javascript/tests/Bug448816Test.java @@ -84,7 +84,7 @@ public void testValueIterator() { compareIterators(map.values().iterator(), reference.values().iterator()); } - private void compareIterators(Iterator it1, Iterator it2) { + private void compareIterators(Iterator it1, Iterator it2) { assertTrue(map.size() == 4); while (it1.hasNext()) { assertEquals(it1.next(), it2.next()); diff --git a/testsrc/org/mozilla/javascript/tests/Bug466207Test.java b/testsrc/org/mozilla/javascript/tests/Bug466207Test.java index bb78c68391..e6aaa129ef 100644 --- a/testsrc/org/mozilla/javascript/tests/Bug466207Test.java +++ b/testsrc/org/mozilla/javascript/tests/Bug466207Test.java @@ -91,14 +91,14 @@ public void testIterator() { compareListIterators(list.listIterator(5), reference.listIterator(5)); } - private void compareIterators(Iterator it1, Iterator it2) { + private void compareIterators(Iterator it1, Iterator it2) { while (it1.hasNext()) { assertEquals(it1.next(), it2.next()); } assertFalse(it2.hasNext()); } - private void compareListIterators(ListIterator it1, ListIterator it2) { + private void compareListIterators(ListIterator it1, ListIterator it2) { while (it1.hasPrevious()) { assertEquals(it1.previous(), it2.previous()); } diff --git a/testsrc/org/mozilla/javascript/tests/commonjs/module/RequireJarTest.java b/testsrc/org/mozilla/javascript/tests/commonjs/module/RequireJarTest.java index b5bd5216c6..41e3df351f 100644 --- a/testsrc/org/mozilla/javascript/tests/commonjs/module/RequireJarTest.java +++ b/testsrc/org/mozilla/javascript/tests/commonjs/module/RequireJarTest.java @@ -19,6 +19,7 @@ */ public class RequireJarTest extends RequireTest { + @Override public void testSandboxed() throws Exception { final Context cx = createContext(); @@ -43,6 +44,7 @@ private Context createContext() return cx; } + @Override public void testNonSandboxed() throws Exception { final Context cx = createContext(); @@ -53,10 +55,12 @@ public void testNonSandboxed() throws Exception require.requireMain(cx, "testNonSandboxed"); } + @Override public void testVariousUsageErrors() throws Exception { testWithSandboxedRequire("testNoArgsRequire"); } + @Override public void testRelativeId() throws Exception { final Context cx = createContext(); final Scriptable scope = cx.initStandardObjects(); @@ -66,6 +70,7 @@ public void testRelativeId() throws Exception { "testRelativeId.js", 1, null); } + @Override public void testSetMainForAlreadyLoadedModule() throws Exception { final Context cx = createContext(); final Scriptable scope = cx.initStandardObjects(); diff --git a/toolsrc/org/mozilla/javascript/tools/debugger/Main.java b/toolsrc/org/mozilla/javascript/tools/debugger/Main.java index a7620b9f88..2656187900 100644 --- a/toolsrc/org/mozilla/javascript/tools/debugger/Main.java +++ b/toolsrc/org/mozilla/javascript/tools/debugger/Main.java @@ -335,6 +335,7 @@ private static Main mainEmbeddedImpl(ContextFactory factory, /** * @deprecated Use {@link #setSize(int, int)} instead. */ + @Deprecated public void setSize(java.awt.Dimension dimension) { debugGui.setSize(dimension.width, dimension.height); } @@ -343,6 +344,7 @@ public void setSize(java.awt.Dimension dimension) { * @deprecated * The method does nothing and is only present for compatibility. */ + @Deprecated public void setOptimizationLevel(int level) { } @@ -350,6 +352,7 @@ public void setOptimizationLevel(int level) { * @deprecated * The method is only present for compatibility and should not be called. */ + @Deprecated public void contextEntered(Context cx) { throw new IllegalStateException(); } @@ -358,6 +361,7 @@ public void contextEntered(Context cx) { * @deprecated * The method is only present for compatibility and should not be called. */ + @Deprecated public void contextExited(Context cx) { throw new IllegalStateException(); } @@ -366,6 +370,7 @@ public void contextExited(Context cx) { * @deprecated * The method is only present for compatibility and should not be called. */ + @Deprecated public void contextCreated(Context cx) { throw new IllegalStateException(); } @@ -374,6 +379,7 @@ public void contextCreated(Context cx) { * @deprecated * The method is only present for compatibility and should not be called. */ + @Deprecated public void contextReleased(Context cx) { throw new IllegalStateException(); diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java index 166e8d6fa4..850c5b1084 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java @@ -113,6 +113,7 @@ String uri() { } /** @deprecated */ + @Deprecated final XmlNode.QName toNodeQname() { return delegate; } diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java index 5ef38e77c7..1b0830c649 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java @@ -74,42 +74,52 @@ public static void init(Context cx, Scriptable scope, boolean sealed) { } } + @Override public void setIgnoreComments(boolean b) { options.setIgnoreComments(b); } + @Override public void setIgnoreWhitespace(boolean b) { options.setIgnoreWhitespace(b); } + @Override public void setIgnoreProcessingInstructions(boolean b) { options.setIgnoreProcessingInstructions(b); } + @Override public void setPrettyPrinting(boolean b) { options.setPrettyPrinting(b); } + @Override public void setPrettyIndent(int i) { options.setPrettyIndent(i); } + @Override public boolean isIgnoreComments() { return options.isIgnoreComments(); } + @Override public boolean isIgnoreProcessingInstructions() { return options.isIgnoreProcessingInstructions(); } + @Override public boolean isIgnoreWhitespace() { return options.isIgnoreWhitespace(); } + @Override public boolean isPrettyPrinting() { return options.isPrettyPrinting(); } + @Override public int getPrettyIndent() { return options.getPrettyIndent(); } @@ -129,11 +139,13 @@ private XMLLibImpl(Scriptable globalScope) { } /** @deprecated */ + @Deprecated QName qnamePrototype() { return qnamePrototype; } /** @deprecated */ + @Deprecated Scriptable globalScope() { return globalScope; } @@ -157,6 +169,7 @@ private void exportToScope(boolean sealed) { } /** @deprecated */ + @Deprecated XMLName toAttributeName(Context cx, Object nameValue) { if (nameValue instanceof XMLName) { // TODO Will this always be an XMLName of type attribute name? diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java index a5d2511636..97142a9d00 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java @@ -138,6 +138,7 @@ static XMLName formStar() { } /** @deprecated */ + @Deprecated static XMLName formProperty(XmlNode.Namespace namespace, String localName) { if (localName != null && localName.equals("*")) localName = null; XMLName rv = new XMLName(); @@ -181,6 +182,7 @@ static XMLName create(XmlNode.QName qname, boolean attribute, boolean descendant } /** @deprecated */ + @Deprecated static XMLName create(XmlNode.QName qname) { return create(qname, false, false); } @@ -468,6 +470,7 @@ boolean isDescendants() { // TODO Fix whether this is an descendant XMLName at construction? /** @deprecated */ + @Deprecated void setIsDescendants() { // if (isDescendants) throw new IllegalStateException(); isDescendants = true; diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java index c273572d0b..f5ef72ba7c 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java @@ -691,6 +691,7 @@ static QName create(Namespace namespace, String localName) { } /** @deprecated */ + @Deprecated static QName create(String uri, String localName, String prefix) { return create(Namespace.create(prefix, uri), localName); } From 338154a8425f2e1a8bc49fdc684b7432afe3934f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 045/111] Check throw-flag in [[Put]] to emit TypeError in strict-mode --- src/org/mozilla/javascript/Parser.java | 5 +++ .../mozilla/javascript/ScriptableObject.java | 32 +++++++++++++------ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index 6e46388a5f..26ab5d2ce5 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -546,6 +546,11 @@ private AstRoot parse() throws IOException boolean savedStrictMode = inUseStrictDirective; ts.setOctalCharacterEscape(false); + if (savedStrictMode) { + // in case parser was initialized in strict-mode + root.setInStrictMode(true); + } + try { for (;;) { int tt = peekToken(); diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 615add6b8e..fb8257dfcf 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -198,8 +198,11 @@ private void readObject(ObjectInputStream in) } } - boolean setValue(Object value, Scriptable owner, Scriptable start) { + boolean setValue(Object value, Scriptable owner, Scriptable start, + boolean checked) { if ((attributes & READONLY) != 0) { + // TODO: proper error message + if (checked) { throw ScriptRuntime.typeError("[[ReadOnly]]"); } return true; } if (owner == start) { @@ -245,7 +248,8 @@ private static final class GetterSlot extends Slot } @Override - boolean setValue(Object value, Scriptable owner, Scriptable start) { + boolean setValue(Object value, Scriptable owner, Scriptable start, + boolean checked) { if (setter == null) { if (getter != null) { if (Context.getContext().hasFeature(Context.FEATURE_STRICT_MODE)) { @@ -282,10 +286,15 @@ boolean setValue(Object value, Scriptable owner, Scriptable start) { Function f = (Function)setter; f.call(cx, f.getParentScope(), start, new Object[] { value }); + } else { + // ES5 setters are defaulted to 'Undefined.instance' instead + // of 'null', so test here for non-existent setter + // TODO: proper error message + if (checked) { throw ScriptRuntime.typeError("[[Setter]]"); } } return true; } - return super.setValue(value, owner, start); + return super.setValue(value, owner, start, checked); } @Override @@ -348,8 +357,9 @@ private static class RelinkedSlot extends Slot { } @Override - boolean setValue(Object value, Scriptable owner, Scriptable start) { - return slot.setValue(value, owner, start); + boolean setValue(Object value, Scriptable owner, Scriptable start, + boolean checked) { + return slot.setValue(value, owner, start, checked); } @Override @@ -516,7 +526,7 @@ public final void put(String name, Scriptable start, Object value) */ public void put(String name, Scriptable start, Object value, boolean checked) { - if (putImpl(name, 0, start, value)) + if (putImpl(name, 0, start, value, checked)) return; if (start == this) throw Kit.codeBug(); @@ -546,7 +556,7 @@ public final void put(int index, Scriptable start, Object value) */ public void put(int index, Scriptable start, Object value, boolean checked) { - if (putImpl(null, index, start, value)) + if (putImpl(null, index, start, value, checked)) return; if (start == this) throw Kit.codeBug(); @@ -2586,7 +2596,7 @@ public synchronized final Object associateValue(Object key, Object value) * or this != start and a READONLY slot was found. */ private boolean putImpl(String name, int index, Scriptable start, - Object value) + Object value, boolean checked) { // This method is very hot (basically called on each assignment) // so we inline the extensible/sealed checks below. @@ -2599,13 +2609,14 @@ private boolean putImpl(String name, int index, Scriptable start, } else if (!isExtensible) { slot = getSlot(name, index, SLOT_QUERY); if (slot == null) { + if (checked) { throw ScriptRuntime.typeError("[[Extensible]]"); } return true; } } else { if (count < 0) checkNotSealed(name, index); slot = getSlot(name, index, SLOT_MODIFY); } - return slot.setValue(value, this, start); + return slot.setValue(value, this, start, checked); } @@ -2650,7 +2661,8 @@ private boolean putConstImpl(String name, int index, Scriptable start, } return true; } - return slot.setValue(value, this, start); + // TODO: strict flag? + return slot.setValue(value, this, start, false); } private Slot findAttributeSlot(String name, int index, int accessType) From 181909efe3d56a983e2e57325c9dc78bf9e8b017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 046/111] initialize strict-mode properly for AstRoot and FunctionNode --- src/org/mozilla/javascript/Parser.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index 26ab5d2ce5..2d4c6ccc5f 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -544,13 +544,9 @@ private AstRoot parse() throws IOException boolean inDirectivePrologue = true; boolean savedStrictMode = inUseStrictDirective; + root.setInStrictMode(savedStrictMode); ts.setOctalCharacterEscape(false); - if (savedStrictMode) { - // in case parser was initialized in strict-mode - root.setInStrictMode(true); - } - try { for (;;) { int tt = peekToken(); @@ -640,6 +636,7 @@ private AstNode parseFunctionBody(FunctionNode fnNode) boolean savedStrictMode = inUseStrictDirective; ts.setOctalCharacterEscape(false); // Don't set 'inUseStrictDirective' to false: inherit strict mode. + fnNode.setInStrictMode(savedStrictMode); pn.setLineno(ts.lineno); try { From cf7cbc6ee42fbc9e53fcc9879cf8e5ad67943173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 047/111] Use the right object to check strict-mode settings --- src/org/mozilla/javascript/CodeGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/CodeGenerator.java b/src/org/mozilla/javascript/CodeGenerator.java index 30e978aa83..761f120617 100644 --- a/src/org/mozilla/javascript/CodeGenerator.java +++ b/src/org/mozilla/javascript/CodeGenerator.java @@ -114,7 +114,7 @@ public InterpreterData compile(CompilerEnvirons compilerEnv, itsData = new InterpreterData(compilerEnv.getLanguageVersion(), scriptOrFn.getSourceName(), encodedSource, - tree.isInStrictMode()); + scriptOrFn.isInStrictMode()); itsData.topLevel = true; if (returnFunction) { From f41e7e1d8240a0af1ceca7ca824b5e400b1b8d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 048/111] strict eval uses a new variable environment --- src/org/mozilla/javascript/ScriptRuntime.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index d5fec2cde1..37f5c17da5 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -2707,6 +2707,15 @@ public static Object evalSpecial(Context cx, Scriptable scope, Script script = cx.compileString(x.toString(), evaluator, reporter, sourceName, 1, null, strictMode); evaluator.setEvalScriptFlag(script); + if (script instanceof InterpretedFunction) { + // TODO: extend Script interface to make this less voodoo + strictMode |= ((InterpretedFunction) script).idata.isStrict; + } + if (strictMode) { + NativeObject newScope = new NativeObject(); + newScope.setParentScope(scope); + scope = newScope; + } Callable c = (Callable)script; return c.call(cx, scope, (Scriptable)thisArg, ScriptRuntime.emptyArgs); } From b4b6d70f45096ae3e2d77ce8b4e82b3d81b722ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 049/111] Change more 'thisObject' definitions from Scriptable to Object --- .../mozilla/javascript/ContextFactory.java | 2 +- .../javascript/InterpretedFunction.java | 10 +++++---- src/org/mozilla/javascript/Interpreter.java | 21 ++++++++++++------- .../javascript/PolicySecurityController.java | 4 ++-- src/org/mozilla/javascript/ScriptRuntime.java | 6 +++--- .../javascript/SecurityController.java | 2 +- .../mozilla/javascript/debug/DebugFrame.java | 2 +- .../mozilla/javascript/optimizer/Codegen.java | 2 +- .../tests/ObserveInstructionCountTest.java | 2 +- .../javascript/tools/debugger/Dim.java | 4 ++-- .../tools/shell/JavaPolicySecurity.java | 2 +- 11 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/org/mozilla/javascript/ContextFactory.java b/src/org/mozilla/javascript/ContextFactory.java index bfa351e655..af1462e7f5 100644 --- a/src/org/mozilla/javascript/ContextFactory.java +++ b/src/org/mozilla/javascript/ContextFactory.java @@ -421,7 +421,7 @@ public final void initApplicationClassLoader(ClassLoader loader) */ protected Object doTopCall(Callable callable, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { Object result = callable.call(cx, scope, thisObj, args); return result instanceof ConsString ? result.toString() : result; diff --git a/src/org/mozilla/javascript/InterpretedFunction.java b/src/org/mozilla/javascript/InterpretedFunction.java index 2a12c36535..c822556f9d 100644 --- a/src/org/mozilla/javascript/InterpretedFunction.java +++ b/src/org/mozilla/javascript/InterpretedFunction.java @@ -155,12 +155,14 @@ public String getFunctionName() * @return the result of the function call. */ @Override - public Object call(Context cx, Scriptable scope, Object thisObject, + public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { - Scriptable thisObj = ScriptRuntime.toObjectOrNull(cx, thisObject, scope); - if (thisObj == null) { - thisObj = ScriptRuntime.getTopCallScope(cx); + if (!idata.isStrict) { + thisObj = ScriptRuntime.toObjectOrNull(cx, thisObj, scope); + if (thisObj == null) { + thisObj = ScriptRuntime.getTopCallScope(cx); + } } if (!ScriptRuntime.hasTopCall(cx)) { return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args); diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index 9ac28f7ef2..6f15ec4a7d 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -104,7 +104,7 @@ private static class CallFrame implements Cloneable, Serializable boolean useActivation; boolean isContinuationsTopFrame; - Scriptable thisObj; + Object thisObj; Scriptable[] scriptRegExps; // The values that change during interpretation @@ -850,7 +850,7 @@ private static void initFunction(Context cx, Scriptable scope, static Object interpret(InterpretedFunction ifun, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (!ScriptRuntime.hasTopCall(cx)) Kit.codeBug(); @@ -2631,19 +2631,24 @@ private static CallFrame initFrameForApplyOrCall(Context cx, CallFrame frame, Scriptable calleeScope, IdFunctionObject ifun, InterpretedFunction iApplyCallable) { - Scriptable applyThis; + Object applyThis; if (indexReg != 0) { Object obj = stack[stackTop + 2]; if (obj == UniqueTag.DOUBLE_MARK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop + 2]); - applyThis = ScriptRuntime.toObjectOrNull(cx, obj); + applyThis = obj; } else { applyThis = null; } - if (applyThis == null) { - // This covers the case of args[0] == (null|undefined) as well. - applyThis = ScriptRuntime.getTopCallScope(cx); + if (!iApplyCallable.idata.isStrict) { + if (applyThis != null) { + applyThis = ScriptRuntime.toObjectOrNull(cx, applyThis); + } + if (applyThis == null) { + // This covers the case of args[0] == (null|undefined) as well. + applyThis = ScriptRuntime.getTopCallScope(cx); + } } if(op == Icode_TAIL_CALL) { exitFrame(cx, frame, null); @@ -2676,7 +2681,7 @@ private static CallFrame initFrameForApplyOrCall(Context cx, CallFrame frame, } private static void initFrame(Context cx, Scriptable callerScope, - Scriptable thisObj, + Object thisObj, Object[] args, double[] argsDbl, int argShift, int argCount, InterpretedFunction fnOrScript, diff --git a/src/org/mozilla/javascript/PolicySecurityController.java b/src/org/mozilla/javascript/PolicySecurityController.java index d23e30c57d..69361f0a40 100644 --- a/src/org/mozilla/javascript/PolicySecurityController.java +++ b/src/org/mozilla/javascript/PolicySecurityController.java @@ -123,7 +123,7 @@ public Object getDynamicSecurityDomain(Object securityDomain) @Override public Object callWithDomain(final Object securityDomain, final Context cx, - Callable callable, Scriptable scope, Scriptable thisObj, + Callable callable, Scriptable scope, Object thisObj, Object[] args) { // Run in doPrivileged as we might be checked for "getClassLoader" @@ -184,7 +184,7 @@ public Object run() throws Exception public abstract static class SecureCaller { public abstract Object call(Callable callable, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args); + Object thisObj, Object[] args); } diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 37f5c17da5..6fb67b93fc 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -2546,7 +2546,7 @@ public static Scriptable newObject(Object fun, Context cx, public static Object callSpecial(Context cx, Callable fun, Scriptable thisObj, Object[] args, Scriptable scope, - Scriptable callerThis, int callType, + Object callerThis, int callType, String filename, int lineNumber, boolean strictMode) { @@ -3361,7 +3361,7 @@ public static Scriptable getTopCallScope(Context cx) public static Object doTopCall(Callable callable, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { if (scope == null) throw new IllegalArgumentException(); @@ -3422,7 +3422,7 @@ public static void addInstructionCount(Context cx, int instructionsToAdd) } } - public static void initScript(NativeFunction funObj, Scriptable thisObj, + public static void initScript(NativeFunction funObj, Object thisObj, Context cx, Scriptable scope, boolean evalScript) { diff --git a/src/org/mozilla/javascript/SecurityController.java b/src/org/mozilla/javascript/SecurityController.java index 6e84e1e498..b84b08cacf 100644 --- a/src/org/mozilla/javascript/SecurityController.java +++ b/src/org/mozilla/javascript/SecurityController.java @@ -184,7 +184,7 @@ public Class getStaticSecurityDomainClassInternal() */ public Object callWithDomain(Object securityDomain, Context cx, final Callable callable, Scriptable scope, - final Scriptable thisObj, final Object[] args) + final Object thisObj, final Object[] args) { return execWithDomain(cx, scope, new Script() { diff --git a/src/org/mozilla/javascript/debug/DebugFrame.java b/src/org/mozilla/javascript/debug/DebugFrame.java index ef15710e27..ad9dbf986b 100644 --- a/src/org/mozilla/javascript/debug/DebugFrame.java +++ b/src/org/mozilla/javascript/debug/DebugFrame.java @@ -58,7 +58,7 @@ public interface DebugFrame { @param args the array of arguments */ public void onEnter(Context cx, Scriptable activation, - Scriptable thisObj, Object[] args); + Object thisObj, Object[] args); /** Called when executed code reaches new line in the source. @param cx current Context for this thread diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index 8bb8fac841..ba6b8a8a34 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -564,7 +564,7 @@ private void generateCallMethod(ClassFileWriter cfw) "(Lorg/mozilla/javascript/Callable;" +"Lorg/mozilla/javascript/Context;" +"Lorg/mozilla/javascript/Scriptable;" - +"Lorg/mozilla/javascript/Scriptable;" + +"Ljava/lang/Object;" +"[Ljava/lang/Object;" +")Ljava/lang/Object;"); cfw.add(ByteCode.ARETURN); diff --git a/testsrc/org/mozilla/javascript/tests/ObserveInstructionCountTest.java b/testsrc/org/mozilla/javascript/tests/ObserveInstructionCountTest.java index 05d3d5d490..6975fedf79 100644 --- a/testsrc/org/mozilla/javascript/tests/ObserveInstructionCountTest.java +++ b/testsrc/org/mozilla/javascript/tests/ObserveInstructionCountTest.java @@ -63,7 +63,7 @@ protected void observeInstructionCount(Context cx, int instructionCount) @Override protected Object doTopCall(Callable callable, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) + Object thisObj, Object[] args) { MyContext mcx = (MyContext)cx; mcx.quota = 2000; diff --git a/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java b/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java index 13e2c8dcf6..1fb673000a 100644 --- a/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java +++ b/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java @@ -1178,7 +1178,7 @@ public static class StackFrame implements DebugFrame { /** * The 'this' object. */ - private Scriptable thisObj; + private Object thisObj; /** * Information about the function. @@ -1210,7 +1210,7 @@ private StackFrame(Context cx, Dim dim, FunctionSource fsource) { * Called when the stack frame is entered. */ public void onEnter(Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) { + Object thisObj, Object[] args) { contextData.pushFrame(this); this.scope = scope; this.thisObj = thisObj; diff --git a/toolsrc/org/mozilla/javascript/tools/shell/JavaPolicySecurity.java b/toolsrc/org/mozilla/javascript/tools/shell/JavaPolicySecurity.java index a6c80919eb..c070af411a 100644 --- a/toolsrc/org/mozilla/javascript/tools/shell/JavaPolicySecurity.java +++ b/toolsrc/org/mozilla/javascript/tools/shell/JavaPolicySecurity.java @@ -219,7 +219,7 @@ public Object callWithDomain(Object securityDomain, final Context cx, final Callable callable, final Scriptable scope, - final Scriptable thisObj, + final Object thisObj, final Object[] args) { ProtectionDomain staticDomain = (ProtectionDomain)securityDomain; From 6da44f37a30a54ada11362e8122ccb636682ecab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 050/111] re-enable non-standard regexp flag for String.prototype.replace --- src/org/mozilla/javascript/regexp/RegExpImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/org/mozilla/javascript/regexp/RegExpImpl.java b/src/org/mozilla/javascript/regexp/RegExpImpl.java index b49f43dace..41e3c8029a 100644 --- a/src/org/mozilla/javascript/regexp/RegExpImpl.java +++ b/src/org/mozilla/javascript/regexp/RegExpImpl.java @@ -85,11 +85,12 @@ public Object action(Context cx, Scriptable scope, case RA_REPLACE: { - boolean useRE = args.length != 0 && (args[0] instanceof NativeRegExp); + boolean useRE = (args.length > 0 && args[0] instanceof NativeRegExp) + || args.length > 2; NativeRegExp re = null; String search = null; if (useRE) { - re = createRegExp(cx, scope, args, 2, false); + re = createRegExp(cx, scope, args, 2, true); } else { Object arg0 = args.length < 1 ? Undefined.instance : args[0]; search = ScriptRuntime.toString(arg0); From ee09dab80eccba6408f9b01802cb7fa80c50000a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 051/111] Update caller initScript() to adjust to changed argument types --- src/org/mozilla/javascript/optimizer/Codegen.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index ba6b8a8a34..61b7f4f514 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -1749,7 +1749,7 @@ private void generatePrologue() cfw.addPush(0); // false to indicate it is not eval script addScriptRuntimeInvoke("initScript", "(Lorg/mozilla/javascript/NativeFunction;" - +"Lorg/mozilla/javascript/Scriptable;" + +"Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" +"Lorg/mozilla/javascript/Scriptable;" +"Z" From 1b28d5d03abcf6c44c0239255d9163e65b20e8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 052/111] 'eval' and 'arguments' must not be LHS operand for assignment resp. must not operand for post/pre-inc/dec --- src/org/mozilla/javascript/Parser.java | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index 2d4c6ccc5f..00065fd3d4 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -851,7 +851,10 @@ private FunctionNode function(int type) : "msg.anon.no.return.value"; addStrictWarning(msg, name == null ? "" : name.getIdentifier()); } - if (inUseStrictDirective) { + + // must use function's strictness here, 'inUseStrictDirective' from + // environment may already be reverted, cf. parseFunctionBody() + if (fnNode.isInStrictMode()) { checkStrictFunction(fnNode); } } finally { @@ -926,6 +929,18 @@ private void checkStrictFunction(FunctionNode fnNode) { } } + private boolean checkStrictAssignment(AstNode node) { + if (node instanceof Name) { + String name = node.getString(); + // strict mode doesn't allow eval/arguments for lhs + if ("eval".equals(name) || "arguments".equals(name)) { + addError("msg.bad.id.strict", name); + return false; + } + } + return true; + } + // This function does not match the closing RC: the caller matches // the RC so it can provide a suitable error message if not matched. // This means it's up to the caller to set the length of the node to @@ -2125,6 +2140,9 @@ private AstNode assignExpr() tt = peekToken(); if (Token.FIRST_ASSIGN <= tt && tt <= Token.LAST_ASSIGN) { consumeToken(); + if (inUseStrictDirective) { + checkStrictAssignment(pn); + } // Pull out JSDoc info and reset it before recursing. String jsdoc = getAndResetJsDoc(); @@ -3485,6 +3503,9 @@ private void checkBadIncDec(UnaryExpression expr) { reportError(expr.getType() == Token.INC ? "msg.bad.incr" : "msg.bad.decr"); + if (inUseStrictDirective) { + checkStrictAssignment(op); + } } private ErrorNode makeErrorNode() { From b1d04219a343bfb9c5f0849c41a020df4d575bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 053/111] String.prototype.replace calls function with 'this' = 'undefined --- src/org/mozilla/javascript/regexp/RegExpImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/regexp/RegExpImpl.java b/src/org/mozilla/javascript/regexp/RegExpImpl.java index 41e3c8029a..100fdec699 100644 --- a/src/org/mozilla/javascript/regexp/RegExpImpl.java +++ b/src/org/mozilla/javascript/regexp/RegExpImpl.java @@ -367,7 +367,8 @@ private static void replace_glob(GlobData rdata, Context cx, ScriptRuntime.setRegExpProxy(cx, re2); try { Scriptable parent = ScriptableObject.getTopLevelScope(scope); - Object result = rdata.lambda.call(cx, parent, parent, args); + Object thisObj = Undefined.instance; + Object result = rdata.lambda.call(cx, parent, thisObj, args); lambdaStr = ScriptRuntime.toString(result); } finally { ScriptRuntime.setRegExpProxy(cx, reImpl); From 796fa2b9b3982b970d76f6e25707a3df6d1d021b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 054/111] Function.prototype.bind uses supplied thisArg as-is, no coercing is to be applied --- src/org/mozilla/javascript/BaseFunction.java | 12 ++++++------ src/org/mozilla/javascript/BoundFunction.java | 9 ++++----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/org/mozilla/javascript/BaseFunction.java b/src/org/mozilla/javascript/BaseFunction.java index 4c8efde51e..88448cc725 100644 --- a/src/org/mozilla/javascript/BaseFunction.java +++ b/src/org/mozilla/javascript/BaseFunction.java @@ -328,15 +328,15 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } Callable targetFunction = (Callable) thisObj; int argc = args.length; - final Scriptable boundThis; + final Object boundThis; final Object[] boundArgs; - if (argc > 0) { - boundThis = ScriptRuntime.toObjectOrNull(cx, args[0], scope); + if (argc <= 1) { + boundThis = argc < 1 ? Undefined.instance : args[0]; + boundArgs = ScriptRuntime.emptyArgs; + } else { + boundThis = args[0]; boundArgs = new Object[argc-1]; System.arraycopy(args, 1, boundArgs, 0, argc-1); - } else { - boundThis = null; - boundArgs = ScriptRuntime.emptyArgs; } return new BoundFunction(cx, scope, targetFunction, boundThis, boundArgs); } diff --git a/src/org/mozilla/javascript/BoundFunction.java b/src/org/mozilla/javascript/BoundFunction.java index 195f50ed51..d239c71a77 100644 --- a/src/org/mozilla/javascript/BoundFunction.java +++ b/src/org/mozilla/javascript/BoundFunction.java @@ -49,12 +49,12 @@ public class BoundFunction extends BaseFunction { static final long serialVersionUID = 2118137342826470729L; private final Callable targetFunction; - private final Scriptable boundThis; + private final Object boundThis; private final Object[] boundArgs; private final int length; - public BoundFunction(Context cx, Scriptable scope, Callable targetFunction, Scriptable boundThis, - Object[] boundArgs) + public BoundFunction(Context cx, Scriptable scope, Callable targetFunction, + Object boundThis, Object[] boundArgs) { this.targetFunction = targetFunction; this.boundThis = boundThis; @@ -77,8 +77,7 @@ public BoundFunction(Context cx, Scriptable scope, Callable targetFunction, Scri @Override public Object call(Context cx, Scriptable scope, Object thisObj, Object[] extraArgs) { - Scriptable callThis = boundThis != null ? boundThis : ScriptRuntime.getTopCallScope(cx); - return targetFunction.call(cx, scope, callThis, concat(boundArgs, extraArgs)); + return targetFunction.call(cx, scope, boundThis, concat(boundArgs, extraArgs)); } @Override From d9c1d497592c232689b33dccfb9a7e62e4629873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 055/111] Ensure FunctionObject#call() coerces thisObject to Scriptable --- src/org/mozilla/javascript/FunctionObject.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/FunctionObject.java b/src/org/mozilla/javascript/FunctionObject.java index 834242958e..b3bc136db5 100644 --- a/src/org/mozilla/javascript/FunctionObject.java +++ b/src/org/mozilla/javascript/FunctionObject.java @@ -402,9 +402,15 @@ public static Object convertArg(Context cx, Scriptable scope, * Context, Scriptable, Scriptable, Object[]) */ @Override - public Object call(Context cx, Scriptable scope, Object thisObj, + public Object call(Context cx, Scriptable scope, Object thisObject, Object[] args) { + Scriptable thisObj = ScriptRuntime.toObjectOrNull(cx, thisObject); + if (thisObj == null) { + // This covers the case of args[0] == (null|undefined) as well. + thisObj = ScriptRuntime.getTopCallScope(cx); + } + assert thisObj != null; Object result; boolean checkMethodResult = false; int argsLength = args.length; From d3679df86d29412a566a6ed30a3c7f6e8dbe8d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 056/111] Add method to query function's strictness to NativeFunction; Add strict mode flag to Arguments and perform the additional strict mode checks in Arguments --- src/org/mozilla/javascript/Arguments.java | 49 ++++++++++++++++++- .../javascript/InterpretedFunction.java | 6 +++ src/org/mozilla/javascript/NativeCall.java | 4 +- .../mozilla/javascript/NativeFunction.java | 5 ++ .../mozilla/javascript/optimizer/Codegen.java | 14 +++++- 5 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/org/mozilla/javascript/Arguments.java b/src/org/mozilla/javascript/Arguments.java index 4b76212e56..1c51480c5a 100644 --- a/src/org/mozilla/javascript/Arguments.java +++ b/src/org/mozilla/javascript/Arguments.java @@ -53,9 +53,10 @@ final class Arguments extends IdScriptableObject private static final String FTAG = "Arguments"; - public Arguments(NativeCall activation) + public Arguments(NativeCall activation, boolean strict) { this.activation = activation; + this.strict = strict; Scriptable parent = activation.getParentScope(); setParentScope(parent); @@ -129,6 +130,39 @@ private void removeArg(int index) { // end helpers + @Override + public boolean has(String name, Scriptable start) { + if (strict) { + // must place check here instead of getInstanceIdValue() + if ("callee".equals(name) || "caller".equals(name)) { + return true; + } + } + return super.has(name, start); + } + + @Override + public Object get(String name, Scriptable start) { + if (strict) { + // must place check here instead of getInstanceIdValue() + if ("callee".equals(name) || "caller".equals(name)) { + throw ScriptRuntime.typeError0("msg.op.not.allowed"); + } + } + return super.get(name, start); + } + + @Override + public void put(String name, Scriptable start, Object value, boolean checked) { + if (strict) { + // must place check here instead of setInstanceIdValue() + if ("callee".equals(name) || "caller".equals(name)) { + throw ScriptRuntime.typeError0("msg.op.not.allowed"); + } + } + super.put(name, start, value, checked); + } + @Override public boolean has(int index, Scriptable start) { @@ -155,6 +189,10 @@ public Object get(int index, Scriptable start) private boolean sharedWithActivation(int index) { + if (strict) { + // arguments are not shared with activation in strict mode + return false; + } NativeFunction f = activation.function; int definedCount = f.getParamCount(); if (index < definedCount) { @@ -416,6 +454,12 @@ protected boolean defineOwnProperty(String name, PropertyDescriptor desc, @Override protected PropertyDescriptor getOwnProperty(String name) { + if (strict) { + if ("callee".equals(name) || "caller".equals(name)) { + Function thrower = ScriptRuntime.typeErrorThrower(); + return new PropertyDescriptor(thrower, thrower, DONTENUM | PERMANENT); + } + } PropertyDescriptor desc = super.getOwnProperty(name); double d = ScriptRuntime.toNumber(name); int index = (int) d; @@ -452,7 +496,8 @@ protected PropertyDescriptor getOwnProperty(String name) { private int lengthAttr = DONTENUM; private int constructorAttr = DONTENUM; - private NativeCall activation; + private final NativeCall activation; + private final boolean strict; // Initially args holds activation.getOriginalArgs(), but any modification // of its elements triggers creation of a copy. If its element holds NOT_FOUND, diff --git a/src/org/mozilla/javascript/InterpretedFunction.java b/src/org/mozilla/javascript/InterpretedFunction.java index c822556f9d..1c96c7fe25 100644 --- a/src/org/mozilla/javascript/InterpretedFunction.java +++ b/src/org/mozilla/javascript/InterpretedFunction.java @@ -237,5 +237,11 @@ protected boolean getParamOrVarConst(int index) { return idata.argIsConst[index]; } + + @Override + protected boolean isStrict() + { + return idata.isStrict; + } } diff --git a/src/org/mozilla/javascript/NativeCall.java b/src/org/mozilla/javascript/NativeCall.java index 6d8882419e..bca58cbbca 100644 --- a/src/org/mozilla/javascript/NativeCall.java +++ b/src/org/mozilla/javascript/NativeCall.java @@ -70,7 +70,7 @@ static void init(Scriptable scope, boolean sealed) this.originalArgs = (args == null) ? ScriptRuntime.emptyArgs : args; // TODO: which strict-mode setting is required here? - boolean strict = false; + boolean strict = function.isStrict(); // initialize values of arguments int paramAndVarCount = function.getParamAndVarCount(); @@ -87,7 +87,7 @@ static void init(Scriptable scope, boolean sealed) // initialize "arguments" property but only if it was not overridden by // the parameter with the same name if (!super.has("arguments", this)) { - defineProperty("arguments", new Arguments(this), PERMANENT, strict); + defineProperty("arguments", new Arguments(this, strict), PERMANENT, strict); } if (paramAndVarCount != 0) { diff --git a/src/org/mozilla/javascript/NativeFunction.java b/src/org/mozilla/javascript/NativeFunction.java index 716ea56478..aa6c3537ec 100644 --- a/src/org/mozilla/javascript/NativeFunction.java +++ b/src/org/mozilla/javascript/NativeFunction.java @@ -171,5 +171,10 @@ protected boolean getParamOrVarConst(int index) // from earlier Rhino versions. See Bugzilla #396117. return false; } + + /** + * Returns the function's strictness + */ + protected abstract boolean isStrict(); } diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index 61b7f4f514..819a963c1d 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -827,7 +827,8 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, final int Do_getParamOrVarName = 3; final int Do_getEncodedSource = 4; final int Do_getParamOrVarConst = 5; - final int SWITCH_COUNT = 6; + final int Do_isStrict = 6; + final int SWITCH_COUNT = 7; for (int methodIndex = 0; methodIndex != SWITCH_COUNT; ++methodIndex) { if (methodIndex == Do_getEncodedSource && encodedSource == null) { @@ -872,6 +873,10 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, ClassFileWriter.ACC_PUBLIC); cfw.addPush(encodedSource); break; + case Do_isStrict: + methodLocals = 1; // Only this + cfw.startMethod("getParamOrVarConst", "()Z", + ClassFileWriter.ACC_PUBLIC); default: throw Kit.codeBug(); } @@ -903,7 +908,7 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, switchStackTop); } - // Impelemnet method-specific switch code + // Implement method-specific switch code switch (methodIndex) { case Do_getFunctionName: // Push function name @@ -1014,6 +1019,11 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, cfw.add(ByteCode.ARETURN); break; + case Do_isStrict: + cfw.add(n.isInStrictMode() ? ByteCode.ICONST_1 : ByteCode.ICONST_0); + cfw.add(ByteCode.IRETURN); + break; + default: throw Kit.codeBug(); } From 18523576e871db95acf72c2646445a0a8fe1279c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 057/111] API-Change: Scriptable#delete(String|int) -> Scriptable#delete(String|int, boolean> - add 'throw' flag to [[Delete]] per 8.12.7 [[Delete]] (P, Throw) - existing callers to [[Delete]] set 'throw' flag to 'false' unless otherwise specified by the spec - add ScriptRuntime#delete and ScriptRuntime#deleteObjectElem with additional 'strict' flag to mark delete operations on strict references and deprecate the existing methods in favour of the ones - redirect old delete() methods in ScriptableObject to the new methods and mark the old ones as final to ease the upgrade process for users - prepend "$" to the currently unused ES5 [[GetProperty]], [[Get]], [[CanPut]], [[Put]], [[HasProperty]], [[Delete]], [[DefaultValue]] to avoid name clash with the new delete(String,boolean) method and mark them as final as well so other parties don't try to override these methods - remove the ES5 [[Get]] and [[Delete]] implementations from Arguments (only added earlier for testing) --- .../javascript/xml/impl/xmlbeans/XML.java | 2 +- .../javascript/xml/impl/xmlbeans/XMLList.java | 2 +- .../xml/impl/xmlbeans/XMLObjectImpl.java | 6 +- examples/Matrix.java | 4 +- src/org/mozilla/javascript/Arguments.java | 34 +----- src/org/mozilla/javascript/Delegator.java | 8 +- .../javascript/IdScriptableObject.java | 6 +- .../mozilla/javascript/ImporterTopLevel.java | 2 +- src/org/mozilla/javascript/NativeArray.java | 20 ++-- src/org/mozilla/javascript/NativeError.java | 2 +- src/org/mozilla/javascript/NativeJSON.java | 6 +- .../mozilla/javascript/NativeJavaObject.java | 4 +- src/org/mozilla/javascript/NativeObject.java | 4 +- src/org/mozilla/javascript/NativeString.java | 4 +- src/org/mozilla/javascript/NativeWith.java | 8 +- src/org/mozilla/javascript/ScriptRuntime.java | 31 +++++- src/org/mozilla/javascript/Scriptable.java | 6 +- .../mozilla/javascript/ScriptableObject.java | 103 +++++++++++++++--- src/org/mozilla/javascript/SpecialRef.java | 3 +- .../javascript/tests/NativeArrayTest.java | 4 +- .../org/mozilla/javascript/xmlimpl/XML.java | 2 +- .../mozilla/javascript/xmlimpl/XMLList.java | 2 +- .../javascript/xmlimpl/XMLObjectImpl.java | 4 +- 23 files changed, 163 insertions(+), 104 deletions(-) diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java index 42a6eed9cd..3731e59edf 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java @@ -1375,7 +1375,7 @@ void deleteXMLProperty(XMLName name) * @param index */ @Override - public void delete(int index) + public void delete(int index, boolean checked) { if (index == 0) { diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java index 5e60969b76..d33caa6de4 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLList.java @@ -580,7 +580,7 @@ void deleteXMLProperty(XMLName name) * @param index */ @Override - public void delete(int index) + public void delete(int index, boolean checked) { if (index >= 0 && index < length()) { diff --git a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java index 190f91e417..0be6ae3224 100644 --- a/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java +++ b/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XMLObjectImpl.java @@ -151,7 +151,7 @@ protected abstract Object jsConstructor(Context cx, boolean inNewExpr, // @Override - public final Object getDefaultValue(Class hint) + public final Object getDefaultValue(Class hint) { return toString(); } @@ -259,7 +259,7 @@ public final boolean delete(Context cx, Object id) if (xmlName == null) { long index = ScriptRuntime.lastUint32Result(cx); // XXX Fix this - delete((int)index); + delete((int)index, false); return true; } deleteXMLProperty(xmlName); @@ -267,7 +267,7 @@ public final boolean delete(Context cx, Object id) } @Override - public void delete(String name) { + public void delete(String name, boolean checked) { Context cx = Context.getCurrentContext(); deleteXMLProperty(lib.toXMLNameFromString(cx, name)); } diff --git a/examples/Matrix.java b/examples/Matrix.java index 8d0fed0f56..fa524ee689 100644 --- a/examples/Matrix.java +++ b/examples/Matrix.java @@ -194,7 +194,7 @@ public void put(int index, Scriptable start, Object value, boolean checked) { * This method shouldn't even be called since we define all properties * as PERMANENT. */ - public void delete(String id) { + public void delete(String id, boolean checked) { } /** @@ -203,7 +203,7 @@ public void delete(String id) { * This method shouldn't even be called since we define all properties * as PERMANENT. */ - public void delete(int index) { + public void delete(int index, boolean checked) { } /** diff --git a/src/org/mozilla/javascript/Arguments.java b/src/org/mozilla/javascript/Arguments.java index 1c51480c5a..f58dbe8ea3 100644 --- a/src/org/mozilla/javascript/Arguments.java +++ b/src/org/mozilla/javascript/Arguments.java @@ -222,12 +222,12 @@ public void put(int index, Scriptable start, Object value, boolean checked) } @Override - public void delete(int index) + public void delete(int index, boolean checked) { if (0 <= index && index < args.length) { removeArg(index); } - super.delete(index); + super.delete(index, checked); } // #string_id_map# @@ -392,36 +392,6 @@ Object[] getIds(boolean getAll) return ids; } - @Override - protected Object get(String name) { - double d = ScriptRuntime.toNumber(name); - int index = (int) d; - if (d == index) { - Object value = arg(index); - if (value != NOT_FOUND) { - if (sharedWithActivation(index)) { - return getFromActivation(index); - } else { - return value; - } - } - } - return super.get(name); - } - - @Override - protected boolean delete(String name, boolean checked) { - double d = ScriptRuntime.toNumber(name); - int index = (int) d; - boolean result = super.delete(name, checked); - if (result && d == index) { - if (0 <= index && index < args.length) { - removeArg(index); - } - } - return result; - } - @Override protected boolean defineOwnProperty(String name, PropertyDescriptor desc, boolean checked) { diff --git a/src/org/mozilla/javascript/Delegator.java b/src/org/mozilla/javascript/Delegator.java index 459e4d3764..63d8515e1b 100644 --- a/src/org/mozilla/javascript/Delegator.java +++ b/src/org/mozilla/javascript/Delegator.java @@ -157,14 +157,14 @@ public void put(int index, Scriptable start, Object value, boolean checked) { /** * @see org.mozilla.javascript.Scriptable#delete(String) */ - public void delete(String name) { - obj.delete(name); + public void delete(String name, boolean checked) { + obj.delete(name,checked); } /** * @see org.mozilla.javascript.Scriptable#delete(int) */ - public void delete(int index) { - obj.delete(index); + public void delete(int index, boolean checked) { + obj.delete(index,checked); } /** * @see org.mozilla.javascript.Scriptable#getPrototype diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index abcdc8ea91..1e4b0634ac 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -419,7 +419,7 @@ public void put(String name, Scriptable start, Object value, boolean checked) } @Override - public void delete(String name) + public void delete(String name, boolean checked) { int info = findInstanceIdInfo(name); if (info != 0) { @@ -442,7 +442,7 @@ public void delete(String name) return; } } - super.delete(name); + super.delete(name, checked); } @Override @@ -782,7 +782,7 @@ protected void updateOwnProperty(String name, PropertyDescriptor desc, | (desc.getAttributes() & mask); if (!iddesc.proto) { if (desc.isAccessorDescriptor()) { - delete(id); // it will be replaced with a slot + delete(id, false); // it will be replaced with a slot } else { if (desc.hasValue()) { setInstanceIdValue(id, desc.getValue()); diff --git a/src/org/mozilla/javascript/ImporterTopLevel.java b/src/org/mozilla/javascript/ImporterTopLevel.java index 7dec21199a..acddcd9eba 100644 --- a/src/org/mozilla/javascript/ImporterTopLevel.java +++ b/src/org/mozilla/javascript/ImporterTopLevel.java @@ -116,7 +116,7 @@ public void initStandardObjects(Context cx, boolean sealed) // delete "constructor" defined by exportAsJSClass so "constructor" // name would refer to Object.constructor // and not to JavaImporter.prototype.constructor. - delete("constructor"); + delete("constructor", false); } @Override diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index 3bea025422..d1df22cc93 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -480,14 +480,14 @@ public void put(int index, Scriptable start, Object value, boolean checked) } @Override - public void delete(int index) + public void delete(int index, boolean checked) { if (dense != null && 0 <= index && index < dense.length && !isSealed() && (denseOnly || !isGetterOrSetter(null, index, true))) { dense[index] = NOT_FOUND; } else { - super.delete(index); + super.delete(index, checked); } } @@ -801,13 +801,13 @@ private long updateEntries(long oldLength, long newLength) { long id = ids[i]; int index = (int) id; if (id == index) { - delete(index); + delete(index, false); if (has(index, this)) { return index; } } else { String strId = Long.toString(index); - delete(strId); + delete(strId, false); if (has(strId, this)) { return index; } @@ -819,13 +819,13 @@ private long updateEntries(long oldLength, long newLength) { for (long i = oldLength - 1; i >= newLength; --i) { int index = (int) i; if (i == index) { - delete(index); + delete(index, false); if (has(index, this)) { return i; } } else { String strId = Long.toString(i); - delete(strId); + delete(strId, false); if (has(strId, this)) { return i; } @@ -882,11 +882,11 @@ private void setLength(Object val) { String strId = (String)id; long index = toArrayIndex(strId); if (index >= longVal) - delete(strId); + delete(strId, false); } else { int index = ((Integer)id).intValue(); if (index >= longVal) - delete(index); + delete(index, false); } } } else { @@ -931,8 +931,8 @@ private static Object setLengthProperty(Context cx, Scriptable target, */ private static void deleteElem(Scriptable target, long index) { int i = (int)index; - if (i == index) { target.delete(i); } - else { target.delete(Long.toString(index)); } + if (i == index) { target.delete(i, false); } + else { target.delete(Long.toString(index), false); } } private static Object getElem(Context cx, Scriptable target, long index) diff --git a/src/org/mozilla/javascript/NativeError.java b/src/org/mozilla/javascript/NativeError.java index bdf608e319..20c6a3209b 100644 --- a/src/org/mozilla/javascript/NativeError.java +++ b/src/org/mozilla/javascript/NativeError.java @@ -172,7 +172,7 @@ public Object getStack() { public void setStack(Object value) { if (stackProvider != null) { stackProvider = null; - delete("stack"); + delete("stack", false); } put("stack", this, value, false); } diff --git a/src/org/mozilla/javascript/NativeJSON.java b/src/org/mozilla/javascript/NativeJSON.java index cfa6e64816..b6d82e4c01 100644 --- a/src/org/mozilla/javascript/NativeJSON.java +++ b/src/org/mozilla/javascript/NativeJSON.java @@ -173,7 +173,7 @@ private static Object walk(Context cx, Scriptable scope, Callable reviver, for (int i = 0; i < len; i++) { Object newElement = walk(cx, scope, reviver, val, i); if (newElement == Undefined.instance) { - val.delete(i); + val.delete(i, false); } else { val.put(i, val, newElement, false); } @@ -184,9 +184,9 @@ private static Object walk(Context cx, Scriptable scope, Callable reviver, Object newElement = walk(cx, scope, reviver, val, p); if (newElement == Undefined.instance) { if (p instanceof Number) - val.delete(((Number) p).intValue()); + val.delete(((Number) p).intValue(), false); else - val.delete((String) p); + val.delete((String) p, false); } else { if (p instanceof Number) val.put(((Number) p).intValue(), val, newElement, false); diff --git a/src/org/mozilla/javascript/NativeJavaObject.java b/src/org/mozilla/javascript/NativeJavaObject.java index a217de6c2e..ede1acd4ac 100644 --- a/src/org/mozilla/javascript/NativeJavaObject.java +++ b/src/org/mozilla/javascript/NativeJavaObject.java @@ -136,10 +136,10 @@ public boolean hasInstance(Object value) { return false; } - public void delete(String name) { + public void delete(String name, boolean checked) { } - public void delete(int index) { + public void delete(int index, boolean checked) { } public Scriptable getPrototype() { diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index 18113d125d..e0890d565b 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -518,9 +518,9 @@ public boolean containsValue(Object value) { public Object remove(Object key) { Object value = get(key); if (key instanceof String) { - delete((String) key); + delete((String) key, false); } else if (key instanceof Number) { - delete(((Number) key).intValue()); + delete(((Number) key).intValue(), false); } return value; } diff --git a/src/org/mozilla/javascript/NativeString.java b/src/org/mozilla/javascript/NativeString.java index 094b67eedd..60c46def2e 100644 --- a/src/org/mozilla/javascript/NativeString.java +++ b/src/org/mozilla/javascript/NativeString.java @@ -521,11 +521,11 @@ public boolean has(int index, Scriptable start) { } @Override - public void delete(int index) { + public void delete(int index, boolean checked) { if (0 <= index && index < string.length()) { return; } - super.delete(index); + super.delete(index, checked); } @Override diff --git a/src/org/mozilla/javascript/NativeWith.java b/src/org/mozilla/javascript/NativeWith.java index 626e3fe6d4..ccd7a889b3 100644 --- a/src/org/mozilla/javascript/NativeWith.java +++ b/src/org/mozilla/javascript/NativeWith.java @@ -117,14 +117,14 @@ public void put(int index, Scriptable start, Object value, boolean checked) prototype.put(index, start, value, checked); } - public void delete(String id) + public void delete(String id, boolean checked) { - prototype.delete(id); + prototype.delete(id, checked); } - public void delete(int index) + public void delete(int index, boolean checked) { - prototype.delete(index); + prototype.delete(index, checked); } public Scriptable getPrototype() { diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 6fb67b93fc..8589ca87e4 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -1683,16 +1683,23 @@ public static Object setObjectIndex(Scriptable obj, int index, Object value, return value; } + @Deprecated public static boolean deleteObjectElem(Scriptable target, Object elem, Context cx) + { + return deleteObjectElem(target, elem, cx, false); + } + + public static boolean deleteObjectElem(Scriptable target, Object elem, + Context cx, boolean strict) { String s = toStringIdOrIndex(cx, elem); if (s == null) { int index = lastIndexResult(cx); - target.delete(index); + target.delete(index, strict); return !target.has(index, target); } else { - target.delete(s); + target.delete(s, strict); return !target.has(s, target); } } @@ -1759,7 +1766,19 @@ public static Object delete(Object obj, Object id, Context cx) * define a return value. Here we assume that the [[Delete]] * method doesn't return a value. */ + @Deprecated public static Object delete(Object obj, Object id, Context cx, boolean isName) + { + return delete(obj, id, cx, isName, false); + } + + /** + * The delete operator + * + * See ECMA 11.4.1 + */ + public static Boolean delete(Object obj, Object id, Context cx, + boolean isName, boolean strict) { Scriptable sobj = toObjectOrNull(cx, obj); if (sobj == null) { @@ -1769,7 +1788,7 @@ public static Object delete(Object obj, Object id, Context cx, boolean isName) String idStr = (id == null) ? "null" : id.toString(); throw typeError2("msg.undef.prop.delete", toString(obj), idStr); } - boolean result = deleteObjectElem(sobj, id, cx); + boolean result = deleteObjectElem(sobj, id, cx, strict); return wrapBoolean(result); } @@ -2338,8 +2357,8 @@ private IllegalStateException error() { public boolean has(int index, Scriptable start) { throw error(); } public void put(String name, Scriptable start, Object value, boolean checked) { throw error(); } public void put(int index, Scriptable start, Object value, boolean checked) { throw error(); } - public void delete(String name) { throw error(); } - public void delete(int index) { throw error(); } + public void delete(String name, boolean checked) { throw error(); } + public void delete(int index, boolean checked) { throw error(); } public Scriptable getPrototype() { throw error(); } public void setPrototype(Scriptable prototype) { throw error(); } public Scriptable getParentScope() { throw error(); } @@ -2717,7 +2736,7 @@ public static Object evalSpecial(Context cx, Scriptable scope, scope = newScope; } Callable c = (Callable)script; - return c.call(cx, scope, (Scriptable)thisArg, ScriptRuntime.emptyArgs); + return c.call(cx, scope, thisArg, ScriptRuntime.emptyArgs); } /** diff --git a/src/org/mozilla/javascript/Scriptable.java b/src/org/mozilla/javascript/Scriptable.java index 9440b10230..22fb2327aa 100644 --- a/src/org/mozilla/javascript/Scriptable.java +++ b/src/org/mozilla/javascript/Scriptable.java @@ -247,10 +247,11 @@ public interface Scriptable { * To delete properties defined in a prototype chain, * see deleteProperty in ScriptableObject. * @param name the identifier for the property + * @param checked controls error handling * @see org.mozilla.javascript.Scriptable#get(String, Scriptable) * @see org.mozilla.javascript.ScriptableObject#deleteProperty(Scriptable, String) */ - public void delete(String name); + public void delete(String name, boolean checked); /** * Removes a property from this object. @@ -265,10 +266,11 @@ public interface Scriptable { * an integral index is used to select the property. * * @param index the numeric index for the property + * @param checked controls error handling * @see org.mozilla.javascript.Scriptable#get(int, Scriptable) * @see org.mozilla.javascript.ScriptableObject#deleteProperty(Scriptable, int) */ - public void delete(int index); + public void delete(int index, boolean checked); /** * Get the prototype of the object. diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index fb8257dfcf..5dda8d18d9 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -571,7 +571,21 @@ public void put(int index, Scriptable start, Object value, boolean checked) * * @param name the name of the property */ - public void delete(String name) + @Deprecated + public final void delete(String name) + { + delete(name, false); + } + + /** + * Removes a named property from the object. + * + * If the property is not found, or it has the PERMANENT attribute, + * no action is taken. + * + * @param name the name of the property + */ + public void delete(String name, boolean checked) { checkNotSealed(name, 0); removeSlot(name, 0); @@ -585,7 +599,21 @@ public void delete(String name) * * @param index the numeric index for the property */ - public void delete(int index) + @Deprecated + public final void delete(int index) + { + delete(index, false); + } + + /** + * Removes the indexed property from the object. + * + * If the property is not found, or it has the PERMANENT attribute, + * no action is taken. + * + * @param index the numeric index for the property + */ + public void delete(int index, boolean checked) { checkNotSealed(null, index); removeSlot(null, index); @@ -2380,13 +2408,33 @@ public static void putProperty(Scriptable obj, int index, Object value, * @param name a property name * @return true if the property doesn't exist or was successfully removed * @since 1.5R2 + * @deprecated use {@link #deleteProperty(Scriptable, String, boolean)} instead */ + @Deprecated public static boolean deleteProperty(Scriptable obj, String name) + { + return deleteProperty(obj, name, false); + } + + /** + * Removes the property from an object or its prototype chain. + *

+ * Searches for a property with name in obj or + * its prototype chain. If it is found, the object's delete + * method is called. + * @param obj a JavaScript object + * @param name a property name + * @param checked controls whether to throw an error if the attribute is [[Permanent]] + * @return true if the property doesn't exist or was successfully removed + * @since 1.7R4 + */ + public static boolean deleteProperty(Scriptable obj, String name, + boolean checked) { Scriptable base = getBase(obj, name); if (base == null) return true; - base.delete(name); + base.delete(name, checked); return !base.has(name, obj); } @@ -2400,13 +2448,33 @@ public static boolean deleteProperty(Scriptable obj, String name) * @param index a property index * @return true if the property doesn't exist or was successfully removed * @since 1.5R2 + * @deprecated use {@link #deleteProperty(Scriptable, int, boolean)} instead */ + @Deprecated public static boolean deleteProperty(Scriptable obj, int index) + { + return deleteProperty(obj, index, false); + } + + /** + * Removes the property from an object or its prototype chain. + *

+ * Searches for a property with index in obj or + * its prototype chain. If it is found, the object's delete + * method is called. + * @param obj a JavaScript object + * @param index a property index + * @param checked controls whether to throw an error if the attribute is [[Permanent]] + * @return true if the property doesn't exist or was successfully removed + * @since 1.7R4 + */ + public static boolean deleteProperty(Scriptable obj, int index, + boolean checked) { Scriptable base = getBase(obj, index); if (base == null) return true; - base.delete(index); + base.delete(index, checked); return !base.has(index, obj); } @@ -3111,7 +3179,7 @@ protected PropertyDescriptor getOwnProperty(String name) { * 8.12.2 [[GetProperty]] (P) * */ - protected PropertyDescriptor getProperty(String name) { + protected final PropertyDescriptor $getProperty(String name) { PropertyDescriptor prop = getOwnProperty(name); if (prop != null) { return prop; @@ -3120,7 +3188,7 @@ protected PropertyDescriptor getProperty(String name) { if (proto == null || !(proto instanceof ScriptableObject)) { return null; } - return ((ScriptableObject) proto).getProperty(name); + return ((ScriptableObject) proto).$getProperty(name); } /** @@ -3129,8 +3197,8 @@ protected PropertyDescriptor getProperty(String name) { * 8.12.3 [[Get]] (P) * */ - protected Object get(String name) { - PropertyDescriptor desc = getProperty(name); + protected final Object $get(String name) { + PropertyDescriptor desc = $getProperty(name); if (desc == null) { return Undefined.instance; } else if (desc.isDataDescriptor()) { @@ -3150,7 +3218,7 @@ protected Object get(String name) { * 8.12.4 [[CanPut]] (P) * */ - protected boolean canPut(String name) { + protected final boolean $canPut(String name) { PropertyDescriptor desc = getOwnProperty(name); if (desc != null) { if (desc.isAccessorDescriptor()) { @@ -3163,8 +3231,7 @@ protected boolean canPut(String name) { if (proto == null || !(proto instanceof ScriptableObject)) { return isExtensible(); } - PropertyDescriptor inherited = ((ScriptableObject) proto) - .getProperty(name); + PropertyDescriptor inherited = ((ScriptableObject) proto).$getProperty(name); if (inherited == null) { return isExtensible(); } @@ -3181,8 +3248,8 @@ protected boolean canPut(String name) { * 8.12.5 [[Put]] (P, V, Throw) * */ - protected void put(String name, Object value, boolean checked) { - if (!canPut(name)) { + protected final void $put(String name, Object value, boolean checked) { + if (!$canPut(name)) { if (checked) { throw ScriptRuntime.typeError("cannot [[Put]]:" + name); } @@ -3194,7 +3261,7 @@ protected void put(String name, Object value, boolean checked) { defineOwnProperty(name, valueDesc, checked); return; } - PropertyDescriptor desc = getProperty(name); + PropertyDescriptor desc = $getProperty(name); if (desc != null && desc.isAccessorDescriptor()) { Object setter = desc.getSetter(); call((Callable) setter, this, new Object[] { value }); @@ -3210,8 +3277,8 @@ protected void put(String name, Object value, boolean checked) { * 8.12.6 [[HasProperty]] (P) * */ - protected boolean hasProperty(String name) { - return getProperty(name) != null; + protected final boolean $hasProperty(String name) { + return $getProperty(name) != null; } /** @@ -3220,7 +3287,7 @@ protected boolean hasProperty(String name) { * 8.12.7 [[Delete]] (P, Throw) * */ - protected boolean delete(String name, boolean checked) { + protected final boolean $delete(String name, boolean checked) { PropertyDescriptor desc = getOwnProperty(name); if (desc == null) { return true; @@ -3243,7 +3310,7 @@ protected boolean delete(String name, boolean checked) { * * 8.12.8 [[DefaultValue]] (hint) */ - protected Object defaultValue(String hint) { + protected final Object $defaultValue(String hint) { if (hint == null) { hint = "Number"; } diff --git a/src/org/mozilla/javascript/SpecialRef.java b/src/org/mozilla/javascript/SpecialRef.java index 93c471d277..f1d42692dd 100644 --- a/src/org/mozilla/javascript/SpecialRef.java +++ b/src/org/mozilla/javascript/SpecialRef.java @@ -152,7 +152,8 @@ public boolean has(Context cx) public boolean delete(Context cx) { if (type == SPECIAL_NONE) { - return ScriptRuntime.deleteObjectElem(target, name, cx); + // TODO: default for 'checked' set to 'false' for now + return ScriptRuntime.deleteObjectElem(target, name, cx, false); } return false; } diff --git a/testsrc/org/mozilla/javascript/tests/NativeArrayTest.java b/testsrc/org/mozilla/javascript/tests/NativeArrayTest.java index 748a7c6830..eeba8fc86b 100644 --- a/testsrc/org/mozilla/javascript/tests/NativeArrayTest.java +++ b/testsrc/org/mozilla/javascript/tests/NativeArrayTest.java @@ -27,14 +27,14 @@ public void getIdsShouldIncludeBothIndexAndNormalProperties() { @Test public void deleteShouldRemoveIndexProperties() { array.put(0, array, "a", false); - array.delete(0); + array.delete(0, false); assertThat(array.has(0, array), is(false)); } @Test public void deleteShouldRemoveNormalProperties() { array.put("p", array, "a", false); - array.delete("p"); + array.delete("p", false); assertThat(array.has("p", array), is(false)); } diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java index eeea3cce0e..d59ffc6c8d 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java @@ -127,7 +127,7 @@ public Object[] getIds() { // TODO This is how I found it but I am not sure it makes sense @Override - public void delete(int index) { + public void delete(int index, boolean checked) { if (index == 0) { this.remove(); } diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java index ebb44d35f8..ff2fa58dfc 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java @@ -331,7 +331,7 @@ void deleteXMLProperty(XMLName name) { } @Override - public void delete(int index) { + public void delete(int index, boolean checked) { if (index >= 0 && index < length()) { XML xml = getXmlFromAnnotation(index); diff --git a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java index 1cb0d36a08..1495f85528 100644 --- a/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java +++ b/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java @@ -312,7 +312,7 @@ public final boolean delete(Context cx, Object id) { if (xmlName == null) { long index = ScriptRuntime.lastUint32Result(cx); // XXX Fix this - delete((int)index); + delete((int)index, false); return true; } deleteXMLProperty(xmlName); @@ -321,7 +321,7 @@ public final boolean delete(Context cx, Object id) { @Override - public void delete(String name) { + public void delete(String name, boolean checked) { Context cx = Context.getCurrentContext(); deleteXMLProperty(lib.toXMLNameFromString(cx, name)); } From 6bede2d3fd02e1feb16812cf3fb6fff806b8b1a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 058/111] 11.4.1 The delete Operator: SyntaxError is thrown operand is name --- src/org/mozilla/javascript/Parser.java | 32 +++++++++++++++++--------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index 00065fd3d4..1ab37a9310 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -941,6 +941,15 @@ private boolean checkStrictAssignment(AstNode node) { return true; } + private boolean checkStrictDelete(UnaryExpression expr) { + AstNode op = removeParens(expr.getOperand()); + if (op instanceof Name) { + addError("msg.bad.id.strict", op.getString()); + return false; + } + return true; + } + // This function does not match the closing RC: the caller matches // the RC so it can provide a suitable error message if not matched. // This means it's up to the caller to set the length of the node to @@ -2376,7 +2385,7 @@ private AstNode mulExpr() private AstNode unaryExpr() throws IOException { - AstNode node; + UnaryExpression node; int tt = peekToken(); int line = ts.lineno; @@ -2407,16 +2416,18 @@ private AstNode unaryExpr() case Token.INC: case Token.DEC: consumeToken(); - UnaryExpression expr = new UnaryExpression(tt, ts.tokenBeg, - memberExpr(true)); - expr.setLineno(line); - checkBadIncDec(expr); - return expr; + node = new UnaryExpression(tt, ts.tokenBeg, memberExpr(true)); + node.setLineno(line); + checkBadIncDec(node); + return node; case Token.DELPROP: consumeToken(); node = new UnaryExpression(tt, ts.tokenBeg, unaryExpr()); node.setLineno(line); + if (inUseStrictDirective) { + checkStrictDelete(node); + } return node; case Token.ERROR: @@ -2439,11 +2450,10 @@ private AstNode unaryExpr() return pn; } consumeToken(); - UnaryExpression uexpr = - new UnaryExpression(tt, ts.tokenBeg, pn, true); - uexpr.setLineno(line); - checkBadIncDec(uexpr); - return uexpr; + node = new UnaryExpression(tt, ts.tokenBeg, pn, true); + node.setLineno(line); + checkBadIncDec(node); + return node; } } From c48a2a8d49f30c05fcf401677fa59d01c8469f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 059/111] Invalid lhs in assignment throws ReferenceError as early error per chapter 16 --- src/org/mozilla/javascript/DefaultErrorReporter.java | 9 +++++++++ src/org/mozilla/javascript/Parser.java | 1 + src/org/mozilla/javascript/resources/Messages.properties | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/DefaultErrorReporter.java b/src/org/mozilla/javascript/DefaultErrorReporter.java index f6a0d5c9b0..995ccf8da9 100644 --- a/src/org/mozilla/javascript/DefaultErrorReporter.java +++ b/src/org/mozilla/javascript/DefaultErrorReporter.java @@ -78,13 +78,22 @@ public void error(String message, String sourceURI, int line, // Assume error message strings that start with "TypeError: " // should become TypeError exceptions. A bit of a hack, but we // don't want to change the ErrorReporter interface. + // And the same for "ReferenceError"... + // FIXME: This doesn't work for the french localized version! String error = "SyntaxError"; final String TYPE_ERROR_NAME = "TypeError"; + final String REFERENCE_ERROR_NAME = "ReferenceError"; final String DELIMETER = ": "; final String prefix = TYPE_ERROR_NAME + DELIMETER; if (message.startsWith(prefix)) { error = TYPE_ERROR_NAME; message = message.substring(prefix.length()); + } else { + final String prefix2 = REFERENCE_ERROR_NAME + DELIMETER; + if (message.startsWith(prefix2)) { + error = REFERENCE_ERROR_NAME; + message = message.substring(prefix2.length()); + } } throw ScriptRuntime.constructError(error, message, sourceURI, line, lineText, lineOffset); diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index 1ab37a9310..9c1210c851 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -930,6 +930,7 @@ private void checkStrictFunction(FunctionNode fnNode) { } private boolean checkStrictAssignment(AstNode node) { + node = removeParens(node); if (node instanceof Name) { String name = node.getString(); // strict mode doesn't allow eval/arguments for lhs diff --git a/src/org/mozilla/javascript/resources/Messages.properties b/src/org/mozilla/javascript/resources/Messages.properties index 5921f1fb40..afe02e34cd 100644 --- a/src/org/mozilla/javascript/resources/Messages.properties +++ b/src/org/mozilla/javascript/resources/Messages.properties @@ -127,7 +127,7 @@ msg.cant.convert =\ Can''t convert to type "{0}". msg.bad.assign.left =\ - Invalid assignment left-hand side. + ReferenceError: Invalid assignment left-hand side. msg.bad.decr =\ Invalid decrement operand. From cf64db0ed4b67ce56e8322a442968b99e5da13bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 060/111] Bring extended precision back again for Number.prototype.{toFixed,toPrecision,toExponential} per extension clause in chapter 16 --- src/org/mozilla/javascript/NativeNumber.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/org/mozilla/javascript/NativeNumber.java b/src/org/mozilla/javascript/NativeNumber.java index 182c31addf..b6cfe92028 100644 --- a/src/org/mozilla/javascript/NativeNumber.java +++ b/src/org/mozilla/javascript/NativeNumber.java @@ -53,6 +53,8 @@ final class NativeNumber extends IdScriptableObject private static final Object NUMBER_TAG = "Number"; + private static final int MAX_PRECISION = 100; + static void init(Scriptable scope, boolean sealed) { NativeNumber obj = new NativeNumber(0.0); @@ -157,7 +159,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, case Id_toFixed: return num_to(value, args, DToA.DTOSTR_FIXED, - DToA.DTOSTR_FIXED, 0, 20, 0); + DToA.DTOSTR_FIXED, 0, 0); case Id_toExponential: { // Handle special values before range check @@ -174,7 +176,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } // General case return num_to(value, args, DToA.DTOSTR_STANDARD_EXPONENTIAL, - DToA.DTOSTR_EXPONENTIAL, 0, 20, 1); + DToA.DTOSTR_EXPONENTIAL, 0, 1); } case Id_toPrecision: { @@ -195,7 +197,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } } return num_to(value, args, DToA.DTOSTR_STANDARD, - DToA.DTOSTR_PRECISION, 1, 21, 0); + DToA.DTOSTR_PRECISION, 1, 0); } default: throw new IllegalArgumentException(String.valueOf(id)); @@ -210,16 +212,17 @@ public String toString() { private static String num_to(double val, Object[] args, int zeroArgMode, int oneArgMode, - int precisionMin, int precisionMax, - int precisionOffset) + int precisionMin, int precisionOffset) { int precision; if (args.length == 0) { precision = 0; oneArgMode = zeroArgMode; } else { + /* We allow a larger range of precision than ECMA requires; + this is permitted by ECMA. [ES5.1, ch. 16 Errors] */ double p = ScriptRuntime.toInteger(args[0]); - if (p < precisionMin || p > precisionMax) { + if (p < precisionMin || p > MAX_PRECISION) { String msg = ScriptRuntime.getMessage1( "msg.bad.precision", ScriptRuntime.toString(args[0])); throw ScriptRuntime.constructError("RangeError", msg); From 1d33469fbe605a2900373b3a6185bf131ee91dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 061/111] Add non-configurable checks to NativeString and ScriptableObject and IdScriptableObject --- .../javascript/IdScriptableObject.java | 18 ++++++++++++++--- src/org/mozilla/javascript/NativeString.java | 4 ++++ .../mozilla/javascript/ScriptableObject.java | 20 +++++++++++++------ 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index 1e4b0634ac..15ce534c8b 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -213,10 +213,13 @@ final void set(int id, Scriptable start, Object value, boolean checked) String name = (String)valueArray[nameSlot]; start.put(name, start, value, checked); } + } else if (checked) { + // TODO: error message + throw ScriptRuntime.typeError("[[ReadOnly]]"); } } - final void delete(int id) + final void delete(int id, boolean checked) { ensureId(id); int attr = attributeArray[id - 1]; @@ -226,6 +229,9 @@ final void delete(int id) valueArray[valueSlot] = NOT_FOUND; attributeArray[id - 1] = EMPTY; } + } else if (checked) { + // TODO: error message + throw ScriptRuntime.typeError("[[Permanent]]"); } } @@ -401,6 +407,9 @@ public void put(String name, Scriptable start, Object value, boolean checked) else { start.put(name, start, value, checked); } + } else if (checked) { + // TODO: error message + throw ScriptRuntime.typeError("[[ReadOnly]]"); } return; } @@ -429,6 +438,9 @@ public void delete(String name, boolean checked) if ((attr & PERMANENT) == 0) { int id = (info & 0xFFFF); setInstanceIdValue(id, NOT_FOUND); + } else if (checked) { + // TODO: error message + throw ScriptRuntime.typeError("[[Permanent]]"); } return; } @@ -437,7 +449,7 @@ public void delete(String name, boolean checked) int id = prototypeValues.findId(name); if (id != 0) { if (!isSealed()) { - prototypeValues.delete(id); + prototypeValues.delete(id, checked); } return; } @@ -794,7 +806,7 @@ protected void updateOwnProperty(String name, PropertyDescriptor desc, } } else { if (desc.isAccessorDescriptor()) { - prototypeValues.delete(id); // it will be replaced with a slot + prototypeValues.delete(id, false); // it will be replaced with a slot } else { if (desc.hasValue()) { // save to pass 'false' b/c read-only check already passed diff --git a/src/org/mozilla/javascript/NativeString.java b/src/org/mozilla/javascript/NativeString.java index 60c46def2e..bcff5ff6aa 100644 --- a/src/org/mozilla/javascript/NativeString.java +++ b/src/org/mozilla/javascript/NativeString.java @@ -523,6 +523,10 @@ public boolean has(int index, Scriptable start) { @Override public void delete(int index, boolean checked) { if (0 <= index && index < string.length()) { + if (checked) { + // TODO: error message + throw ScriptRuntime.typeError("[[Permanent]]"); + } return; } super.delete(index, checked); diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 5dda8d18d9..11e8682cfd 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -588,7 +588,7 @@ public final void delete(String name) public void delete(String name, boolean checked) { checkNotSealed(name, 0); - removeSlot(name, 0); + removeSlot(name, 0, checked); } /** @@ -616,7 +616,7 @@ public final void delete(int index) public void delete(int index, boolean checked) { checkNotSealed(null, index); - removeSlot(null, index); + removeSlot(null, index, checked); } /** @@ -2896,7 +2896,7 @@ private synchronized Slot createSlot(String name, int indexOrHash, int accessTyp return newSlot; } - private synchronized void removeSlot(String name, int index) { + private synchronized void removeSlot(String name, int index, boolean checked) { int indexOrHash = (name != null ? name.hashCode() : index); Slot[] slotsLocalRef = slots; @@ -2915,7 +2915,15 @@ private synchronized void removeSlot(String name, int index) { prev = slot; slot = slot.next; } - if (slot != null && (slot.getAttributes() & PERMANENT) == 0) { + if (slot == null) { + return; + } else if ((slot.getAttributes() & PERMANENT) != 0) { + if (checked) { + // TODO: proper error message + throw ScriptRuntime.typeError("[[Permanent]]"); + } + return; + } else { count--; // remove slot from hash table if (prev == slot) { @@ -3294,9 +3302,9 @@ protected PropertyDescriptor getOwnProperty(String name) { } else if (desc.isConfigurable()) { long index = ScriptRuntime.indexFromString(name); if (index >= 0) { - removeSlot(null, (int) index); + removeSlot(null, (int) index, checked); } else { - removeSlot(name, 0); + removeSlot(name, 0, checked); } return true; } else if (checked) { From 52054dae052c47cb4381ec4e7717022be09ffac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 062/111] Implement strict-mode restriction w.r.t to 'arguments' and 'caller' for user-functions and bound functions --- src/org/mozilla/javascript/BoundFunction.java | 60 +++++++++++++++++-- .../mozilla/javascript/NativeFunction.java | 58 ++++++++++++++++++ 2 files changed, 113 insertions(+), 5 deletions(-) diff --git a/src/org/mozilla/javascript/BoundFunction.java b/src/org/mozilla/javascript/BoundFunction.java index d239c71a77..3f3bd0a630 100644 --- a/src/org/mozilla/javascript/BoundFunction.java +++ b/src/org/mozilla/javascript/BoundFunction.java @@ -67,11 +67,61 @@ public BoundFunction(Context cx, Scriptable scope, Callable targetFunction, ScriptRuntime.setFunctionProtoAndParent(this, scope); - Function thrower = ScriptRuntime.typeErrorThrower(); - PropertyDescriptor throwing = new PropertyDescriptor(thrower, thrower, DONTENUM | PERMANENT); - this.defineOwnProperty("caller", throwing, false); - // this.defineOwnProperty("arguments", throwing, false); - this.updateOwnProperty("arguments", throwing, null); + Function thrower = ScriptRuntime.typeErrorThrower(cx); + PropertyDescriptor desc = new PropertyDescriptor(thrower, thrower, DONTENUM | PERMANENT); + defineOwnProperty("caller", desc, false); + // TODO: this doesn't work properly see BaseFunction+IdScriptableObject, + // just place the required checks in the overridden methods below + // defineOwnProperty("arguments", desc, false); + } + + @Override + public boolean has(String name, Scriptable start) { + // see comment in constructor + if ("arguments".equals(name)) { + return true; + } + return super.has(name, start); + } + + @Override + public Object get(String name, Scriptable start) { + // see comment in constructor + if ("arguments".equals(name)) { + throw ScriptRuntime.typeError0("msg.op.not.allowed"); + } + return super.get(name, start); + } + + @Override + public void put(String name, Scriptable start, Object value, boolean checked) { + // see comment in constructor + if ("arguments".equals(name)) { + throw ScriptRuntime.typeError0("msg.op.not.allowed"); + } + super.put(name, start, value, checked); + } + + @Override + public void delete(String name, boolean checked) { + // see comment in constructor + if ("arguments".equals(name)) { + if (checked) { + throw ScriptRuntime.typeError0("[[Permanent]]"); + } + return; + } + super.delete(name, checked); + } + + @Override + protected PropertyDescriptor getOwnProperty(String name) { + // see comment in constructor + if ("arguments".equals(name)) { + Function thrower = ScriptRuntime.typeErrorThrower(); + return new PropertyDescriptor(thrower, thrower, DONTENUM | PERMANENT); + } + return super.getOwnProperty(name); } @Override diff --git a/src/org/mozilla/javascript/NativeFunction.java b/src/org/mozilla/javascript/NativeFunction.java index aa6c3537ec..ff09348d96 100644 --- a/src/org/mozilla/javascript/NativeFunction.java +++ b/src/org/mozilla/javascript/NativeFunction.java @@ -57,6 +57,15 @@ public abstract class NativeFunction extends BaseFunction public final void initScriptFunction(Context cx, Scriptable scope) { ScriptRuntime.setFunctionProtoAndParent(this, scope); + if (isStrict()) { + Function thrower = ScriptRuntime.typeErrorThrower(cx); + PropertyDescriptor desc = new PropertyDescriptor(thrower, thrower, + DONTENUM | PERMANENT); + defineOwnProperty("caller", desc, false); + // TODO: this doesn't work properly see BaseFunction+IdScriptableObject, + // just place the required checks in the overridden methods below + // defineOwnProperty("arguments", desc, false); + } } /** @@ -77,6 +86,55 @@ final String decompile(int indent, int flags) } } + @Override + public boolean has(String name, Scriptable start) { + // see comment in constructor + if ("arguments".equals(name) && isStrict()) { + return true; + } + return super.has(name, start); + } + + @Override + public Object get(String name, Scriptable start) { + // see comment in constructor + if ("arguments".equals(name) && isStrict()) { + throw ScriptRuntime.typeError0("msg.op.not.allowed"); + } + return super.get(name, start); + } + + @Override + public void put(String name, Scriptable start, Object value, boolean checked) { + // see comment in constructor + if ("arguments".equals(name) && isStrict()) { + throw ScriptRuntime.typeError0("msg.op.not.allowed"); + } + super.put(name, start, value, checked); + } + + @Override + public void delete(String name, boolean checked) { + // see comment in constructor + if ("arguments".equals(name) && isStrict()) { + if (checked) { + throw ScriptRuntime.typeError("[[Permanent]]"); + } + return; + } + super.delete(name, checked); + } + + @Override + protected PropertyDescriptor getOwnProperty(String name) { + // see comment in constructor + if ("arguments".equals(name) && isStrict()) { + Function thrower = ScriptRuntime.typeErrorThrower(); + return new PropertyDescriptor(thrower, thrower, DONTENUM | PERMANENT); + } + return super.getOwnProperty(name); + } + @Override public int getLength() { From a76c103f765980dcbaa355743db3c527ab8747a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 063/111] Use correct 'thisObject' for Array.prototype.{search,every,filter,some,map,reduce,reduceRight} --- src/org/mozilla/javascript/NativeArray.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index d1df22cc93..1de5ef05b9 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -1192,9 +1192,11 @@ private static Scriptable js_sort(final Context cx, final Scriptable scope, Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); final Comparator comparator; if (args.length > 0 && Undefined.instance != args[0]) { - final Callable jsCompareFunction = ScriptRuntime - .getValueFunctionAndThis(args[0], cx); - final Scriptable funThis = ScriptRuntime.lastStoredScriptable(cx); + if (!(args[0] instanceof Callable)) { + throw ScriptRuntime.notFunctionError(args[0]); + } + final Callable jsCompareFunction = (Callable)args[0]; + final Object funThis = Undefined.instance; final Object[] cmpBuf = new Object[2]; // Buffer for cmp arguments comparator = new Comparator() { public int compare(final Object x, final Object y) { @@ -1814,13 +1816,7 @@ private static Object iterativeMethod(Context cx, int id, Scriptable scope, } Function f = (Function) callbackArg; Scriptable parent = ScriptableObject.getTopLevelScope(f); - Object thisArg; - if (args.length < 2 || args[1] == null || args[1] == Undefined.instance) - { - thisArg = parent; - } else { - thisArg = ScriptRuntime.toObject(cx, scope, args[1]); - } + Object thisArg = args.length < 2 ? Undefined.instance : args[1]; int resultLength = id == Id_map ? (int) length : 0; Scriptable array = id != Id_every ? cx.newArray(scope, resultLength) : null; long j=0; @@ -1882,6 +1878,7 @@ private static Object reduceMethod(Context cx, int id, Scriptable scope, } Function f = (Function) callbackArg; Scriptable parent = ScriptableObject.getTopLevelScope(f); + Object thisArg = Undefined.instance; // hack to serve both reduce and reduceRight with the same loop boolean movingLeft = id == Id_reduce; Object value = args.length > 1 ? args[1] : Scriptable.NOT_FOUND; @@ -1896,7 +1893,7 @@ private static Object reduceMethod(Context cx, int id, Scriptable scope, value = elem; } else { Object[] innerArgs = { value, elem, index, thisObj }; - value = f.call(cx, parent, parent, innerArgs); + value = f.call(cx, parent, thisArg, innerArgs); } } if (value == Scriptable.NOT_FOUND) { From 2cb53d62399bd0da4c0558f4a1231745e18f8415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 064/111] override construct() in IdFunctionObject to enable code like Number.call(null,42)===42 --- .../mozilla/javascript/IdFunctionObject.java | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/org/mozilla/javascript/IdFunctionObject.java b/src/org/mozilla/javascript/IdFunctionObject.java index fd7b28db97..29b6600556 100644 --- a/src/org/mozilla/javascript/IdFunctionObject.java +++ b/src/org/mozilla/javascript/IdFunctionObject.java @@ -126,20 +126,47 @@ public Scriptable getPrototype() public Object call(Context cx, Scriptable scope, Object thisObj, Object[] args) { + if (thisObj == null && useCallAsConstructor) { + // thisObj == null is a special marker for constructor functions + thisObj = Undefined.instance; + } return idcall.execIdCall(this, cx, scope, thisObj, args); } @Override - public Scriptable createObject(Context cx, Scriptable scope) + public Scriptable construct(Context cx, Scriptable scope, Object[] args) { - if (useCallAsConstructor) { - return null; + if (!useCallAsConstructor) { + // Throw error if not explicitly coded to be used as constructor, + // to satisfy ECMAScript standard (see bugzilla 202019). + throw ScriptRuntime.typeError1("msg.not.ctor", functionName); + } + Object val = idcall.execIdCall(this, cx, scope, null, args); + if (!(val instanceof Scriptable)) { + // It is program error not to return Scriptable from + // the call method of constructor functions. + throw new IllegalStateException( + "Bad implementaion of call as constructor, name=" + +getFunctionName()+" in "+getClass().getName()); + } + Scriptable result = (Scriptable)val; + if (result.getPrototype() == null) { + result.setPrototype(getClassPrototype()); + } + if (result.getParentScope() == null) { + Scriptable parent = getParentScope(); + if (result != parent) { + result.setParentScope(parent); + } } - // Throw error if not explicitly coded to be used as constructor, - // to satisfy ECMAScript standard (see bugzilla 202019). - // To follow current (2003-05-01) SpiderMonkey behavior, change it to: - // return super.createObject(cx, scope); - throw ScriptRuntime.typeError1("msg.not.ctor", functionName); + return result; + } + + @Override + public Scriptable createObject(Context cx, Scriptable scope) + { + // should no longer be called now that construct() is overridden as well + throw Kit.codeBug(); } @Override From 0fb8cd94794c377ed9e2c70b4ddbd5175d399fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 065/111] Set-up proto and parent-scope for TypeErrorThrower function; Remove artifical restriction in #getApplyArguments(); WIP for delete and direct eval --- src/org/mozilla/javascript/Context.java | 1 + src/org/mozilla/javascript/ScriptRuntime.java | 36 ++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/org/mozilla/javascript/Context.java b/src/org/mozilla/javascript/Context.java index 020c2f97b4..dafa975e6b 100644 --- a/src/org/mozilla/javascript/Context.java +++ b/src/org/mozilla/javascript/Context.java @@ -2610,6 +2610,7 @@ public void removeActivationName(String name) boolean isContinuationsTopCall; NativeCall currentActivationCall; XMLLib cachedXMLLib; + BaseFunction typeErrorThrower; // for Objects, Arrays to tag themselves as being printed out, // so they don't print themselves out recursively. diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 8589ca87e4..a46013bb79 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -74,13 +74,20 @@ public class ScriptRuntime { protected ScriptRuntime() { } - /** * Returns representation of the [[ThrowTypeError]] object. * See ECMA 5 spec, 13.2.3 */ public static BaseFunction typeErrorThrower() { - if (THROW_TYPE_ERROR == null) { + return typeErrorThrower(Context.getCurrentContext()); + } + + /** + * Returns representation of the [[ThrowTypeError]] object. + * See ECMA 5 spec, 13.2.3 + */ + public static BaseFunction typeErrorThrower(Context cx) { + if (cx.typeErrorThrower == null) { BaseFunction thrower = new BaseFunction() { static final long serialVersionUID = -5891740962154902286L; @@ -93,12 +100,12 @@ public int getLength() { return 0; } }; + ScriptRuntime.setFunctionProtoAndParent(thrower, cx.topCallScope); thrower.preventExtensions(); - THROW_TYPE_ERROR = thrower; + cx.typeErrorThrower = thrower; } - return THROW_TYPE_ERROR; + return cx.typeErrorThrower; } - private static BaseFunction THROW_TYPE_ERROR = null; static class NoSuchMethodShim implements Callable { String methodName; @@ -1780,6 +1787,10 @@ public static Object delete(Object obj, Object id, Context cx, boolean isName) public static Boolean delete(Object obj, Object id, Context cx, boolean isName, boolean strict) { + if (isName && strict) { + // TODO: error message + throw constructError("SyntaxError", ""); + } Scriptable sobj = toObjectOrNull(cx, obj); if (sobj == null) { if (isName) { @@ -2563,16 +2574,25 @@ public static Scriptable newObject(Object fun, Context cx, } public static Object callSpecial(Context cx, Callable fun, - Scriptable thisObj, + Object thisObj, Object[] args, Scriptable scope, Object callerThis, int callType, String filename, int lineNumber, boolean strictMode) { if (callType == Node.SPECIALCALL_EVAL) { - if (thisObj.getParentScope() == null && NativeGlobal.isEvalFunction(fun)) { + assert thisObj == Undefined.instance || thisObj instanceof Scriptable; + // FIXME: This really needs be to checked with spec! + if (thisObj == Undefined.instance) { return evalSpecial(cx, scope, callerThis, args, filename, lineNumber, strictMode); + } else { + if (! (thisObj instanceof Scriptable)) Kit.codeBug(); + if (((Scriptable) thisObj).getParentScope() == null + && NativeGlobal.isEvalFunction(fun)) { + return evalSpecial(cx, scope, callerThis, args, + filename, lineNumber, strictMode); + } } } else if (callType == Node.SPECIALCALL_WITH) { if (NativeWith.isWithFunction(fun)) { @@ -2640,7 +2660,7 @@ static Object[] getApplyArguments(Context cx, Object arg1) { if (arg1 == null || arg1 == Undefined.instance) { return ScriptRuntime.emptyArgs; - } else if (arg1 instanceof NativeArray || arg1 instanceof Arguments) { + } else if (arg1 instanceof Scriptable) { return cx.getElements((Scriptable) arg1); } else { throw ScriptRuntime.typeError0("msg.arg.isnt.array"); From 4a57ccc0e3f2c2639611c6c72e96668a9fff0268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 066/111] Array.prototype.toLocaleString changed to follow spec/spidermonkey more closely --- src/org/mozilla/javascript/NativeArray.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index 1de5ef05b9..34adf4ab02 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -1059,13 +1059,16 @@ private static String toStringHelper(Context cx, Scriptable scope, } else { if (toLocale) { - Callable fun; - Scriptable funThis; - fun = ScriptRuntime.getPropFunctionAndThis( - elem, "toLocaleString", cx); - funThis = ScriptRuntime.lastStoredScriptable(cx); - elem = fun.call(cx, scope, funThis, - ScriptRuntime.emptyArgs); + Scriptable funThis = ScriptRuntime.toObject(cx, + scope, elem); + Object fun = ScriptableObject.getProperty(funThis, + "toLocaleString"); + if (!(fun instanceof Callable)) { + throw ScriptRuntime.notFunctionError(funThis, + fun, "toLocaleString"); + } + elem = ((Callable)fun).call(cx, scope, funThis, + ScriptRuntime.emptyArgs); } result.append(ScriptRuntime.toString(elem)); } From 0509ae438ea082f2afd6fdba8e1e476fe989355b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 067/111] RefCallable also needs to handle non-Scriptable thisObject --- src/org/mozilla/javascript/RefCallable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/RefCallable.java b/src/org/mozilla/javascript/RefCallable.java index 6d4b61ce59..a7ff2d4dd2 100644 --- a/src/org/mozilla/javascript/RefCallable.java +++ b/src/org/mozilla/javascript/RefCallable.java @@ -54,6 +54,6 @@ public interface RefCallable extends Callable * @param thisObj the JavaScript this object * @param args the array of arguments */ - public Ref refCall(Context cx, Scriptable thisObj, Object[] args); + public Ref refCall(Context cx, Object thisObj, Object[] args); } From 77f97a3213d1a558aedafc1ff398b15114b884d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 068/111] Use 'null' as marker to use 'undefined' to thisObj; Set callThis to 'undefined' if not specified for call/apply; Add scope to callRef --- src/org/mozilla/javascript/ScriptRuntime.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index a46013bb79..8a75f2013c 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -1825,7 +1825,7 @@ private static Object nameOrFunction(Context cx, Scriptable scope, boolean asFunctionCall) { Object result; - Scriptable thisObj = scope; // It is used only if asFunctionCall==true. + Scriptable thisObj = null; // It is used only if asFunctionCall==true. XMLObject firstXMLObject = null; for (;;) { @@ -1858,8 +1858,7 @@ private static Object nameOrFunction(Context cx, Scriptable scope, if (asFunctionCall) { // ECMA 262 requires that this for nested funtions // should be top scope - thisObj = ScriptableObject. - getTopLevelScope(parentScope); + thisObj = null; } break; } @@ -1890,7 +1889,7 @@ private static Object nameOrFunction(Context cx, Scriptable scope, result = firstXMLObject.get(name, firstXMLObject); } // For top scope thisObj for functions is always scope itself. - thisObj = scope; + thisObj = null; break; } } @@ -2290,6 +2289,12 @@ public static Callable getNameFunctionAndThis(String name, Scriptable scope) { Object value = getNameObjectAndThis(name, cx, scope); + // restore old behaviour + scope = lastStoredScriptable(cx); + if (scope == null) { + scope = cx.topCallScope; + } + storeScriptable(cx, scope); return ensureCallable(cx, value); } @@ -2402,8 +2407,7 @@ public static Object getNameObjectAndThis(String name, } } // Top scope is not NativeWith or NativeCall => thisObj == scope - Scriptable thisObj = scope; - storeScriptable(cx, thisObj); + storeScriptable(cx, null); return result; } @@ -2541,8 +2545,8 @@ public static Object getValueObjectAndThis(Object value, Context cx) * can be GC-reachable after this method returns. If this is necessary, * store args.clone(), not args array itself. */ - public static Ref callRef(Callable function, Scriptable thisObj, - Object[] args, Context cx) + public static Ref callRef(Callable function, Scriptable scope, + Object thisObj, Object[] args, Context cx) { if (function instanceof RefCallable) { RefCallable rfunction = (RefCallable)function; @@ -2552,7 +2556,8 @@ public static Ref callRef(Callable function, Scriptable thisObj, } return ref; } - // No runtime support for now + // No runtime support for now, but need to call function nonetheless + function.call(cx, scope, thisObj, args); String msg = getMessage1("msg.no.ref.from.function", toString(function)); throw constructError("ReferenceError", msg); @@ -2637,7 +2642,7 @@ public static Object applyOrCall(boolean isApply, int L = args.length; Callable function = getCallable(thisObj); - Object callThis = (L != 0 ? args[0] : null); + Object callThis = (L != 0 ? args[0] : Undefined.instance); Object[] callArgs; if (isApply) { // Follow Ecma 15.3.4.3 From 6df69e0cf788453a79e084650b80d766abcc20c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 069/111] Test262 complete Interpreter, but still needs some tweaks --- src/org/mozilla/javascript/Interpreter.java | 47 +++++++++++++-------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index 6f15ec4a7d..8c7b487ef6 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -1403,7 +1403,9 @@ private static Object interpretLoop(Context cx, CallFrame frame, --stackTop; Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.delete(lhs, rhs, cx, op == Icode_DELNAME); + stack[stackTop] = ScriptRuntime.delete(lhs, rhs, cx, + op == Icode_DELNAME, + frame.idata.isStrict); continue Loop; } case Token.GETPROPNOWARN : { @@ -1527,14 +1529,16 @@ private static Object interpretLoop(Context cx, CallFrame frame, indexReg += frame.localShift; stack[indexReg] = null; continue Loop; - case Icode_NAME_AND_THIS : + case Icode_NAME_AND_THIS : { // stringReg: name ++stackTop; stack[stackTop] = ScriptRuntime.getNameObjectAndThis(stringReg, cx, frame.scope); ++stackTop; - stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); + Scriptable thisObj = ScriptRuntime.lastStoredScriptable(cx); + stack[stackTop] = (thisObj != null ? thisObj : Undefined.instance); continue Loop; + } case Icode_PROP_AND_THIS: { Object obj = stack[stackTop]; if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]); @@ -1559,7 +1563,9 @@ private static Object interpretLoop(Context cx, CallFrame frame, if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]); stack[stackTop] = ScriptRuntime.getValueObjectAndThis(value, cx); ++stackTop; - stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); + // ignore stored scriptable + ScriptRuntime.lastStoredScriptable(cx); + stack[stackTop] = Undefined.instance; continue Loop; } case Icode_CALLSPECIAL : { @@ -1586,9 +1592,10 @@ private static Object interpretLoop(Context cx, CallFrame frame, // stack change: function thisObj arg0 .. argN -> result stackTop -= 1 + indexReg; + // FIXME: here! // Call code generation ensure that stack here // is ... Callable Scriptable - Scriptable functionThis = (Scriptable)stack[stackTop + 1]; + Object functionThis = stack[stackTop + 1]; Callable function = ScriptRuntime.ensureCallable(stack[stackTop]); Object[] outArgs = getArgsArray( stack, sDbl, stackTop + 2, indexReg); @@ -1614,18 +1621,18 @@ private static Object interpretLoop(Context cx, CallFrame frame, // CALL generation ensures that fun and funThisObj // are already Scriptable and Callable objects respectively Callable fun = ScriptRuntime.ensureCallable(stack[stackTop]); - Scriptable funThisObj = (Scriptable)stack[stackTop + 1]; + Object funThisObj = stack[stackTop + 1]; + Scriptable calleeScope = frame.scope; + if (frame.useActivation) { + calleeScope = ScriptableObject.getTopLevelScope(frame.scope); + } if (op == Token.REF_CALL) { Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 2, indexReg); - stack[stackTop] = ScriptRuntime.callRef(fun, funThisObj, - outArgs, cx); + stack[stackTop] = ScriptRuntime.callRef(fun, calleeScope, + funThisObj, outArgs, cx); continue Loop; } - Scriptable calleeScope = frame.scope; - if (frame.useActivation) { - calleeScope = ScriptableObject.getTopLevelScope(frame.scope); - } if (fun instanceof InterpretedFunction) { InterpretedFunction ifun = (InterpretedFunction)fun; if (frame.fnOrScript.securityDomain == ifun.securityDomain) { @@ -1652,6 +1659,14 @@ private static Object interpretLoop(Context cx, CallFrame frame, // it is being done here. exitFrame(cx, frame, null); } + if (!ifun.idata.isStrict) { + // TODO: move into initFrame()? + funThisObj = ScriptRuntime.toObjectOrNull(cx, funThisObj); + if (funThisObj == null) { + // This covers the case of args[0] == (null|undefined) as well. + funThisObj = ScriptRuntime.getTopCallScope(cx); + } + } initFrame(cx, calleeScope, funThisObj, stack, sDbl, stackTop + 2, indexReg, ifun, callParentFrame, calleeFrame); @@ -2417,7 +2432,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, */ private static CallFrame initFrameForNoSuchMethod(Context cx, CallFrame frame, int indexReg, Object[] stack, double[] sDbl, - int stackTop, int op, Scriptable funThisObj, Scriptable calleeScope, + int stackTop, int op, Object funThisObj, Scriptable calleeScope, NoSuchMethodShim noSuchMethodShim, InterpretedFunction ifun) { // create an args array from the stack @@ -2639,12 +2654,10 @@ private static CallFrame initFrameForApplyOrCall(Context cx, CallFrame frame, applyThis = obj; } else { - applyThis = null; + applyThis = Undefined.instance; } if (!iApplyCallable.idata.isStrict) { - if (applyThis != null) { - applyThis = ScriptRuntime.toObjectOrNull(cx, applyThis); - } + applyThis = ScriptRuntime.toObjectOrNull(cx, applyThis); if (applyThis == null) { // This covers the case of args[0] == (null|undefined) as well. applyThis = ScriptRuntime.getTopCallScope(cx); From d8a0b0952e797fcd1c120f25d7f388c197f30fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 070/111] Test262 complete Codegen, but still needs some tweaks --- .../mozilla/javascript/optimizer/Codegen.java | 89 +++++++++++++------ .../javascript/optimizer/OptRuntime.java | 38 ++++---- 2 files changed, 86 insertions(+), 41 deletions(-) diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index 819a963c1d..dd1d09994e 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -513,14 +513,23 @@ private void generateCallMethod(ClassFileWriter cfw) | ClassFileWriter.ACC_FINAL)); // Generate code for: - // Scriptable thisObj = ScriptRuntime.toObjectOrNull(cx, thisObject, scope); - // if (thisObj == null) { - // thisObj = ScriptRuntime.getTopCallScope(cx); + // if (!isStrict()) { + // thisObj = ScriptRuntime.toObjectOrNull(cx, thisObj, scope); + // if (thisObj == null) { + // thisObj = ScriptRuntime.getTopCallScope(cx); + // } // } + int notStrict = cfw.acquireLabel(); int thisObjNull = cfw.acquireLabel(); - cfw.addALoad(1); - cfw.addALoad(3); - cfw.addALoad(2); + cfw.addALoad(0); // + cfw.addInvoke(ByteCode.INVOKEVIRTUAL, + cfw.getClassName(), + "isStrict", + "()Z"); + cfw.add(ByteCode.IFNE, notStrict); + cfw.addALoad(1); // context + cfw.addALoad(3); // thisObj + cfw.addALoad(2); // scope cfw.addInvoke(ByteCode.INVOKESTATIC, "org/mozilla/javascript/ScriptRuntime", "toObjectOrNull", @@ -528,17 +537,19 @@ private void generateCallMethod(ClassFileWriter cfw) +"Ljava/lang/Object;" +"Lorg/mozilla/javascript/Scriptable;" +")Lorg/mozilla/javascript/Scriptable;"); - cfw.addAStore(5); - cfw.addALoad(5); + cfw.add(ByteCode.DUP); + // stack: ... thisObj thisObj cfw.add(ByteCode.IFNONNULL, thisObjNull); - cfw.addALoad(1); + cfw.add(ByteCode.POP); + cfw.addALoad(1); // context cfw.addInvoke(ByteCode.INVOKESTATIC, "org/mozilla/javascript/ScriptRuntime", "getTopCallScope", "(Lorg/mozilla/javascript/Context;" +")Lorg/mozilla/javascript/Scriptable;"); - cfw.addAStore(5); cfw.markLabel(thisObjNull); + cfw.addAStore(3); // thisObj + cfw.markLabel(notStrict); // Generate code for: // if (!ScriptRuntime.hasTopCall(cx)) { @@ -556,7 +567,7 @@ private void generateCallMethod(ClassFileWriter cfw) cfw.addALoad(0); cfw.addALoad(1); cfw.addALoad(2); - cfw.addALoad(5); + cfw.addALoad(3); cfw.addALoad(4); cfw.addInvoke(ByteCode.INVOKESTATIC, "org/mozilla/javascript/ScriptRuntime", @@ -574,7 +585,7 @@ private void generateCallMethod(ClassFileWriter cfw) cfw.addALoad(0); cfw.addALoad(1); cfw.addALoad(2); - cfw.addALoad(5); + cfw.addALoad(3); cfw.addALoad(4); int end = scriptOrFnNodes.length; @@ -637,8 +648,8 @@ private void generateCallMethod(ClassFileWriter cfw) getBodyMethodSignature(n)); cfw.add(ByteCode.ARETURN); } - cfw.stopMethod((short)6); - // 5: this, cx, scope, js this, args[], js this' + cfw.stopMethod((short)5); + // 5: this, cx, scope, js this, args[] } private void generateMain(ClassFileWriter cfw) @@ -875,8 +886,9 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, break; case Do_isStrict: methodLocals = 1; // Only this - cfw.startMethod("getParamOrVarConst", "()Z", + cfw.startMethod("isStrict", "()Z", ClassFileWriter.ACC_PUBLIC); + break; default: throw Kit.codeBug(); } @@ -1020,7 +1032,7 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, break; case Do_isStrict: - cfw.add(n.isInStrictMode() ? ByteCode.ICONST_1 : ByteCode.ICONST_0); + cfw.addPush(n.isInStrictMode()); cfw.add(ByteCode.IRETURN); break; @@ -1301,7 +1313,7 @@ String getBodyMethodSignature(ScriptNode n) sb.append(mainClassSignature); sb.append("Lorg/mozilla/javascript/Context;" +"Lorg/mozilla/javascript/Scriptable;" - +"Lorg/mozilla/javascript/Scriptable;"); + +"Ljava/lang/Object;"); if (n.getType() == Token.FUNCTION) { OptFunctionNode ofn = OptFunctionNode.get(n); if (ofn.isTargetOfDirectCall()) { @@ -2333,6 +2345,7 @@ private void generateExpression(Node node, Node parent) child = child.getNext(); generateCallArgArray(node, child, false); cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); addOptRuntimeInvoke( "callRef", "(Ljava/lang/Object;" @@ -2757,11 +2770,12 @@ private void generateExpression(Node node, Node parent) generateExpression(child, node); cfw.addALoad(contextLocal); cfw.addPush(isName); + cfw.addPush(scriptOrFn.isInStrictMode()); addScriptRuntimeInvoke("delete", "(Ljava/lang/Object;" +"Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" - +"Z)Ljava/lang/Object;"); + +"ZZ)Ljava/lang/Boolean;"); break; case Token.BINDNAME: @@ -3288,6 +3302,7 @@ private void visitSpecialCall(Node node, int type, int specialType, +"Lorg/mozilla/javascript/Scriptable;" +"I" // call type +"Ljava/lang/String;I" // filename, linenumber + +"Z" // strict flag +")Ljava/lang/Object;"; cfw.addALoad(variableObjectLocal); cfw.addALoad(thisObjLocal); @@ -3295,6 +3310,7 @@ private void visitSpecialCall(Node node, int type, int specialType, String sourceName = scriptOrFn.getSourceName(); cfw.addPush(sourceName == null ? "" : sourceName); cfw.addPush(itsLineNumber); + cfw.addPush(scriptOrFn.isInStrictMode()); } addOptRuntimeInvoke(methodName, callSignature); @@ -3339,7 +3355,7 @@ private void visitStandardCall(Node node, Node child) generateObjectAndThisObj(child, node); methodName = "call0"; signature = "(Ljava/lang/Object;" - +"Lorg/mozilla/javascript/Scriptable;" + +"Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" +"Lorg/mozilla/javascript/Scriptable;" +")Ljava/lang/Object;"; @@ -3370,7 +3386,7 @@ private void visitStandardCall(Node node, Node child) generateExpression(firstArgChild, node); methodName = "call1"; signature = "(Ljava/lang/Object;" - +"Lorg/mozilla/javascript/Scriptable;" + +"Ljava/lang/Object;" +"Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" +"Lorg/mozilla/javascript/Scriptable;" @@ -3380,7 +3396,7 @@ private void visitStandardCall(Node node, Node child) generateExpression(firstArgChild.getNext(), node); methodName = "call2"; signature = "(Ljava/lang/Object;" - +"Lorg/mozilla/javascript/Scriptable;" + +"Ljava/lang/Object;" +"Ljava/lang/Object;" +"Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" @@ -3390,7 +3406,7 @@ private void visitStandardCall(Node node, Node child) generateCallArgArray(node, firstArgChild, false); methodName = "callN"; signature = "(Ljava/lang/Object;" - +"Lorg/mozilla/javascript/Scriptable;" + +"Ljava/lang/Object;" +"[Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" +"Lorg/mozilla/javascript/Scriptable;" @@ -3618,7 +3634,7 @@ private void generateObjectAndThisObj(Node node, Node parent) { // Place on stack (function object, function this) pair int type = node.getType(); - switch (node.getType()) { + switch (type) { case Token.GETPROPNOWARN: throw Kit.codeBug(); @@ -3685,6 +3701,29 @@ private void generateObjectAndThisObj(Node node, Node parent) "lastStoredScriptable", "(Lorg/mozilla/javascript/Context;" +")Lorg/mozilla/javascript/Scriptable;"); + switch (type) { + case Token.GETPROP: + case Token.GETELEM: + // no further changes needed + break; + case Token.NAME: { + // replace null with undefined + int undefCheckLabel = cfw.acquireLabel(); + // stack: ... thisObj + cfw.add(ByteCode.DUP); + cfw.add(ByteCode.IFNONNULL, undefCheckLabel); + cfw.add(ByteCode.POP); + Codegen.pushUndefined(cfw); + cfw.markLabel(undefCheckLabel); + // stack: ... [thisObj | undefined] + break; + } + default: // including GETVAR + // ignore stored scriptable + cfw.add(ByteCode.POP); + Codegen.pushUndefined(cfw); + break; + } } private void updateLineNumber(Node node) @@ -5109,7 +5148,7 @@ private void visitSetProp(int type, Node node, Node child) generateExpression(child, node); assert (type == Token.STRICT_SETPROP || type == Token.STRICT_SETPROP_OP) == scriptOrFn.isInStrictMode(); - cfw.add(scriptOrFn.isInStrictMode() ? ByteCode.ICONST_1 : ByteCode.ICONST_0); + cfw.addPush(scriptOrFn.isInStrictMode()); cfw.addALoad(contextLocal); addScriptRuntimeInvoke( "setObjectProp", @@ -5158,7 +5197,7 @@ private void visitSetElem(int type, Node node, Node child) generateExpression(child, node); assert (type == Token.STRICT_SETELEM || type == Token.STRICT_SETELEM_OP) == scriptOrFn.isInStrictMode(); - cfw.add(scriptOrFn.isInStrictMode() ? ByteCode.ICONST_1 : ByteCode.ICONST_0); + cfw.addPush(scriptOrFn.isInStrictMode()); cfw.addALoad(contextLocal); if (indexIsNumber) { addScriptRuntimeInvoke( diff --git a/src/org/mozilla/javascript/optimizer/OptRuntime.java b/src/org/mozilla/javascript/optimizer/OptRuntime.java index da7c44557d..952c531996 100644 --- a/src/org/mozilla/javascript/optimizer/OptRuntime.java +++ b/src/org/mozilla/javascript/optimizer/OptRuntime.java @@ -51,7 +51,7 @@ public final class OptRuntime extends ScriptRuntime /** * Implement ....() call shrinking optimizer code. */ - public static Object call0(Object fun, Scriptable thisObj, + public static Object call0(Object fun, Object thisObj, Context cx, Scriptable scope) { Callable c = ensureCallable(fun); @@ -61,7 +61,7 @@ public static Object call0(Object fun, Scriptable thisObj, /** * Implement ....(arg) call shrinking optimizer code. */ - public static Object call1(Object fun, Scriptable thisObj, Object arg0, + public static Object call1(Object fun, Object thisObj, Object arg0, Context cx, Scriptable scope) { Callable c = ensureCallable(fun); @@ -71,7 +71,7 @@ public static Object call1(Object fun, Scriptable thisObj, Object arg0, /** * Implement ....(arg0, arg1) call shrinking optimizer code. */ - public static Object call2(Object fun, Scriptable thisObj, + public static Object call2(Object fun, Object thisObj, Object arg0, Object arg1, Context cx, Scriptable scope) { @@ -82,7 +82,7 @@ public static Object call2(Object fun, Scriptable thisObj, /** * Implement ....(arg0, arg1, ...) call shrinking optimizer code. */ - public static Object callN(Object fun, Scriptable thisObj, + public static Object callN(Object fun, Object thisObj, Object[] args, Context cx, Scriptable scope) { @@ -96,9 +96,11 @@ public static Object callN(Object fun, Scriptable thisObj, public static Object callName(Object[] args, String name, Context cx, Scriptable scope) { - Callable f = getNameFunctionAndThis(name, cx, scope); - Scriptable thisObj = lastStoredScriptable(cx); - return f.call(cx, scope, thisObj, args); + Object f = getNameObjectAndThis(name, cx, scope); + Object thisObj = lastStoredScriptable(cx); + thisObj = (thisObj != null ? thisObj : Undefined.instance); + Callable c = ensureCallable(f); + return c.call(cx, scope, thisObj, args); } /** @@ -107,9 +109,11 @@ public static Object callName(Object[] args, String name, public static Object callName0(String name, Context cx, Scriptable scope) { - Callable f = getNameFunctionAndThis(name, cx, scope); - Scriptable thisObj = lastStoredScriptable(cx); - return f.call(cx, scope, thisObj, ScriptRuntime.emptyArgs); + Object f = getNameObjectAndThis(name, cx, scope); + Object thisObj = lastStoredScriptable(cx); + thisObj = (thisObj != null ? thisObj : Undefined.instance); + Callable c = ensureCallable(f); + return c.call(cx, scope, thisObj, ScriptRuntime.emptyArgs); } /** @@ -118,19 +122,20 @@ public static Object callName0(String name, public static Object callProp0(Object value, String property, Context cx, Scriptable scope) { - Callable f = getPropFunctionAndThis(value, property, cx, scope); + Object f = getPropObjectAndThis(value, property, cx, scope); Scriptable thisObj = lastStoredScriptable(cx); - return f.call(cx, scope, thisObj, ScriptRuntime.emptyArgs); + Callable c = ensureCallable(f); + return c.call(cx, scope, thisObj, ScriptRuntime.emptyArgs); } /** * Implement ....(arg0, arg1, ...) call-ref shrinking optimizer code. */ public static Ref callRef(Object fun, Scriptable thisObj, - Object[] args, Context cx) + Object[] args, Context cx, Scriptable scope) { Callable c = ensureCallable(fun); - return callRef(c, thisObj, args, cx); + return callRef(c, scope, thisObj, args, cx); } public static Object add(Object val1, double val2) @@ -176,12 +181,13 @@ public static Object callSpecial(Context cx, Object fun, Scriptable thisObj, Object[] args, Scriptable scope, Scriptable callerThis, int callType, - String fileName, int lineNumber) + String fileName, int lineNumber, + boolean strictMode) { Callable c = ensureCallable(fun); return ScriptRuntime.callSpecial(cx, c, thisObj, args, scope, callerThis, callType, - fileName, lineNumber, false); + fileName, lineNumber, strictMode); } public static Object newObjectSpecial(Context cx, Object fun, From 7026596ba22382b3004d09a7509ea47e540aa657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 071/111] - process inc/dec with proper strict-mode setting in Interpreter - add new helper methods for 8.7.1 GetValue() and 8.7.2 PutValue() to ScriptRuntime which perform the special-casing for primitives - update the callers in Intepreter and Codegen to use the new helper methods - update more instructions in Codegen to handle non-Scriptable thisObjects - guard some optimizations in Codegen with additional checks for strict-mode - update more methods in OptRuntime to handle non-Scriptable thisObjects --- src/org/mozilla/javascript/Interpreter.java | 42 ++- src/org/mozilla/javascript/ScriptRuntime.java | 324 ++++++++++++++++-- .../mozilla/javascript/optimizer/Codegen.java | 97 ++++-- .../javascript/optimizer/OptRuntime.java | 26 +- 4 files changed, 407 insertions(+), 82 deletions(-) diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index 8c7b487ef6..5b2fcea9cf 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -1417,7 +1417,9 @@ private static Object interpretLoop(Context cx, CallFrame frame, case Token.GETPROP : { Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.getObjectProp(lhs, stringReg, cx, frame.scope); + stack[stackTop] = ScriptRuntime.getObjectValue(lhs, stringReg, + frame.idata.isStrict, + cx, frame.scope); continue Loop; } case Token.STRICT_SETPROP : @@ -1428,15 +1430,18 @@ private static Object interpretLoop(Context cx, CallFrame frame, --stackTop; Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.setObjectProp(lhs, stringReg, rhs, - frame.idata.isStrict, cx); + ScriptRuntime.putObjectValue(lhs, stringReg, frame.idata.isStrict, + rhs, cx, frame.scope); + stack[stackTop] = rhs; continue Loop; } case Icode_PROP_INC_DEC : { Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); stack[stackTop] = ScriptRuntime.propIncrDecr(lhs, stringReg, - cx, iCode[frame.pc]); + frame.idata.isStrict, + cx, frame.scope, + iCode[frame.pc]); ++frame.pc; continue Loop; } @@ -1449,10 +1454,12 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object value; Object id = stack[stackTop + 1]; if (id != DBL_MRK) { - value = ScriptRuntime.getObjectElem(lhs, id, cx, frame.scope); + value = ScriptRuntime.getObjectValue(lhs, id, frame.idata.isStrict, + cx, frame.scope); } else { double d = sDbl[stackTop + 1]; - value = ScriptRuntime.getObjectIndex(lhs, d, cx); + value = ScriptRuntime.getObjectValue(lhs, d, frame.idata.isStrict, + cx, frame.scope); } stack[stackTop] = value; continue Loop; @@ -1469,17 +1476,16 @@ private static Object interpretLoop(Context cx, CallFrame frame, if (lhs == DBL_MRK) { lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); } - Object value; Object id = stack[stackTop + 1]; if (id != DBL_MRK) { - value = ScriptRuntime.setObjectElem(lhs, id, rhs, - frame.idata.isStrict, cx); + ScriptRuntime.putObjectValue(lhs, id, frame.idata.isStrict, + rhs, cx, frame.scope); } else { double d = sDbl[stackTop + 1]; - value = ScriptRuntime.setObjectIndex(lhs, d, rhs, - frame.idata.isStrict, cx); + ScriptRuntime.putObjectValue(lhs, d, frame.idata.isStrict, + rhs, cx, frame.scope); } - stack[stackTop] = value; + stack[stackTop] = rhs; continue Loop; } case Icode_ELEM_INC_DEC: { @@ -1488,10 +1494,9 @@ private static Object interpretLoop(Context cx, CallFrame frame, --stackTop; Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - // TODO: checked flag! - stack[stackTop] = ScriptRuntime.elemIncrDecr(lhs, rhs, cx, - iCode[frame.pc], - false); + stack[stackTop] = ScriptRuntime.elemIncrDecr(lhs, rhs, frame.idata.isStrict, + cx, frame.scope, + iCode[frame.pc]); ++frame.pc; continue Loop; } @@ -1825,6 +1830,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, continue Loop; case Icode_NAME_INC_DEC : stack[++stackTop] = ScriptRuntime.nameIncrDecr(frame.scope, stringReg, + frame.idata.isStrict, cx, iCode[frame.pc]); ++frame.pc; continue Loop; @@ -1868,8 +1874,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object val = stack[stackTop]; if (val == DBL_MRK) val = ScriptRuntime.wrapNumber(sDbl[stackTop]); stringReg = frame.idata.argNames[indexReg]; - // TODO: check -> 'false'? - frame.scope.put(stringReg, frame.scope, val, false); + frame.scope.put(stringReg, frame.scope, val, frame.idata.isStrict); } continue Loop; case Icode_GETVAR1: @@ -1906,6 +1911,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, } else { String varName = frame.idata.argNames[indexReg]; stack[stackTop] = ScriptRuntime.nameIncrDecr(frame.scope, varName, + frame.idata.isStrict, cx, incrDecrMask); } ++frame.pc; diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 8a75f2013c..b4d6201b72 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -1473,6 +1473,283 @@ static String toStringIdOrIndex(Context cx, Object id) } } + /** + * 8.7 The Reference Specification Type: HasPrimitiveBase(V)
+ * Returns true if the base value is a Boolean, String, or Number. + */ + private static Scriptable tryPrimitive(Context cx, Scriptable scope, Object val) { + if (val instanceof CharSequence) { + NativeString result = new NativeString((CharSequence)val); + setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.String); + return result; + } + if (val instanceof Number) { + NativeNumber result = new NativeNumber(((Number)val).doubleValue()); + setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Number); + return result; + } + if (val instanceof Boolean) { + NativeBoolean result = new NativeBoolean(((Boolean)val).booleanValue()); + setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Boolean); + return result; + } + return null; + } + + /** + * 8.7 Extension: Wrap as a LiveConnect object. + */ + private static Scriptable tryWrap(Context cx, Scriptable scope, Object val) { + Object wrapped = cx.getWrapFactory().wrap(cx, scope, val, null); + if (wrapped instanceof Scriptable) + return (Scriptable) wrapped; + return null; + } + + /** + * Special [[Get]] if reference base value is primitive, see ES5.1 [8.7.1] + */ + private static Object getPrimitiveValue(Object base, Scriptable obj, + String property, Context cx, + Scriptable scope) { + // TODO: could need some optimization + assert obj instanceof ScriptableObject; + ScriptableObject sobj = (ScriptableObject) obj; + PropertyDescriptor desc = sobj.$getProperty(property); + if (desc == null) { + return Undefined.instance; + } else if (desc.isDataDescriptor()) { + return desc.getValue(); + } + Object getter = desc.getGetter(); + if (getter == Undefined.instance) { + return Undefined.instance; + } else { + return ((Callable) getter).call(cx, scope, base, emptyArgs); + } + } + + /** + * Special [[Put]] if reference base value is primitive, see ES5.1 [8.7.2] + */ + private static void putPrimitiveValue(Object base, Scriptable obj, + String property, boolean checked, + Object value, Context cx, + Scriptable scope) { + // TODO: could need some optimization + assert obj instanceof ScriptableObject; + ScriptableObject sobj = (ScriptableObject) obj; + if (!sobj.$canPut(property)) { + if (checked) { + // TODO: error message + throw typeError("[[ReadOnly]]"); + } + return; + } + PropertyDescriptor ownDesc = sobj.getOwnProperty(property); + if (ownDesc != null && ownDesc.isDataDescriptor()) { + if (checked) { + // TODO: error message + throw typeError("[[ReadOnly]]"); + } + return; + } + PropertyDescriptor desc = sobj.$getProperty(property); + if (desc == null || !desc.isAccessorDescriptor()) { + if (checked) { + // TODO: error message + throw typeError("[[ReadOnly]]"); + } + return; + } + Object setter = desc.getSetter(); + ((Callable) setter).call(cx, scope, base, new Object[]{ value }); + } + + /** + * ES5.1 [8.7.1 GetValue (V)]
+ * + * Precondition: The reference V={obj, elem, strict} is either an + * unresolvable reference or a property reference. + * + * @param obj The base value component + * @param elem The referenced name component + * @param strict The strict reference component + * @param cx The current context + * @param scope The current scope + */ + public static Object getObjectValue(Object obj, Object elem, boolean strict, + Context cx, Scriptable scope) + { + Scriptable sobj; + if (obj == null || obj == Undefined.instance) { + throw undefReadError(obj, elem); + } else if (obj instanceof Scriptable) { + sobj = (Scriptable) obj; + } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { + return getPrimitiveValue(obj, sobj, toString(elem), cx, scope); + } else if ((sobj = tryWrap(cx, scope, obj)) == null) { + throw errorWithClassName("msg.invalid.type", obj); + } + return getObjectElem(sobj, elem, cx); + } + + /** + * ES5.1 [8.7.1 GetValue (V)] + * + * Precondition: The reference V={obj, property, strict} is either an + * unresolvable reference or a property reference. + * + * @param obj The base value component + * @param property The referenced name component + * @param strict The strict reference component + * @param cx The current context + * @param scope The current scope + */ + public static Object getObjectValue(Object obj, String property, boolean strict, + Context cx, Scriptable scope) + { + Scriptable sobj; + if (obj == null || obj == Undefined.instance) { + throw undefReadError(obj, property); + } else if (obj instanceof Scriptable) { + sobj = (Scriptable) obj; + } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { + return getPrimitiveValue(obj, sobj, property, cx, scope); + } else if ((sobj = tryWrap(cx, scope, obj)) == null) { + throw errorWithClassName("msg.invalid.type", obj); + } + return getObjectProp(sobj, property, cx); + } + + /** + * ES5.1 [8.7.1 GetValue (V)] + * + * Precondition: The reference V={obj, dblIndex, strict} is either an + * unresolvable reference or a property reference. + * + * @param obj The base value component + * @param dblIndex The referenced name component + * @param strict The strict reference component + * @param cx The current context + * @param scope The current scope + */ + public static Object getObjectValue(Object obj, double dblIndex, boolean strict, + Context cx, Scriptable scope) + { + Scriptable sobj; + if (obj == null || obj == Undefined.instance) { + throw undefReadError(obj, toString(dblIndex)); + } else if (obj instanceof Scriptable) { + sobj = (Scriptable) obj; + } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { + return getPrimitiveValue(obj, sobj, toString(dblIndex), cx, scope); + } else if ((sobj = tryWrap(cx, scope, obj)) == null) { + throw errorWithClassName("msg.invalid.type", obj); + } + int index = (int) dblIndex; + if (index == dblIndex) { + return getObjectIndex(sobj, index, cx); + } else { + return getObjectProp(sobj, toString(dblIndex), cx); + } + } + + /** + * ES5.1 [8.7.2 PutValue (V, W)] + * + * Precondition: The reference V={obj, elem, strict} is either an + * unresolvable reference or a property reference. + * + * @param obj The base value component + * @param elem The referenced name component + * @param strict The strict reference component + * @param value The value to set the reference to + * @param cx The current context + * @param scope The current scope + */ + public static void putObjectValue(Object obj, Object elem, boolean strict, + Object value, Context cx, Scriptable scope) + { + Scriptable sobj; + if (obj == null || obj == Undefined.instance) { + throw undefWriteError(obj, elem, value); + } else if (obj instanceof Scriptable) { + sobj = (Scriptable) obj; + } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { + putPrimitiveValue(obj, sobj, toString(elem), strict, value, cx, scope); + return; + } else if ((sobj = tryWrap(cx, scope, obj)) == null) { + throw errorWithClassName("msg.invalid.type", obj); + } + setObjectElem(sobj, elem, value, strict, cx); + } + + /** + * ES5.1 [8.7.2 PutValue (V, W)] + * + * Precondition: The reference V={obj, property, strict} is either an + * unresolvable reference or a property reference. + * + * @param obj The base value component + * @param property The referenced name component + * @param strict The strict reference component + * @param value The value to set the reference to + * @param cx The current context + * @param scope The current scope + */ + public static void putObjectValue(Object obj, String property, boolean strict, + Object value, Context cx, Scriptable scope) + { + Scriptable sobj; + if (obj == null || obj == Undefined.instance) { + throw undefWriteError(obj, property, value); + } else if (obj instanceof Scriptable) { + sobj = (Scriptable) obj; + } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { + putPrimitiveValue(obj, sobj, property, strict, value, cx, scope); + return; + } else if ((sobj = tryWrap(cx, scope, obj)) == null) { + throw errorWithClassName("msg.invalid.type", obj); + } + ScriptableObject.putProperty(sobj, property, value, strict); + } + + /** + * ES5.1 [8.7.2 PutValue (V, W)] + * + * Precondition: The reference V={obj, dblIndex, strict} is either an + * unresolvable reference or a property reference. + * + * @param obj The base value component + * @param dblIndex The referenced name component + * @param strict The strict reference component + * @param value The value to set the reference to + * @param cx The current context + * @param scope The current scope + */ + public static void putObjectValue(Object obj, double dblIndex, boolean strict, + Object value, Context cx, Scriptable scope) + { + Scriptable sobj; + if (obj == null || obj == Undefined.instance) { + throw undefWriteError(obj, toString(dblIndex), value); + } else if (obj instanceof Scriptable) { + sobj = (Scriptable) obj; + } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { + putPrimitiveValue(obj, sobj, toString(dblIndex), strict, value, cx, scope); + return; + } else if ((sobj = tryWrap(cx, scope, obj)) == null) { + throw errorWithClassName("msg.invalid.type", obj); + } + int index = (int) dblIndex; + if (index == dblIndex) { + ScriptableObject.putProperty(sobj, index, value, strict); + } else { + ScriptableObject.putProperty(sobj, toString(dblIndex), value, strict); + } + } + /** * Call obj.[[Get]](id) */ @@ -2876,15 +3153,16 @@ private static CharSequence newConsString(CharSequence val1, CharSequence val2) public static Object nameIncrDecr(Scriptable scopeChain, String id, int incrDecrMask) { - return nameIncrDecr(scopeChain, id, Context.getContext(), incrDecrMask); + return nameIncrDecr(scopeChain, id, false, Context.getContext(), incrDecrMask); } public static Object nameIncrDecr(Scriptable scopeChain, String id, - Context cx, int incrDecrMask) + boolean strict, Context cx, + int incrDecrMask) { Scriptable target; Object value; - search: { + search: { do { if (cx.useDynamicScope && scopeChain.getParentScope() == null) { scopeChain = checkDynamicScope(cx.topCallScope, scopeChain); @@ -2905,21 +3183,29 @@ public static Object nameIncrDecr(Scriptable scopeChain, String id, } while (scopeChain != null); throw notFoundError(scopeChain, id); } - return doScriptableIncrDecr(target, id, scopeChain, value, + return doScriptableIncrDecr(target, id, scopeChain, strict, value, incrDecrMask); } - public static Object propIncrDecr(Object obj, String id, - Context cx, int incrDecrMask) + public static Object propIncrDecr(Object obj, String id, boolean strict, + Context cx, Scriptable scope, + int incrDecrMask) { - Scriptable start = toObjectOrNull(cx, obj); - if (start == null) { + Scriptable start; + if (obj == null || obj == Undefined.instance) { throw undefReadError(obj, id); + } else if (obj instanceof Scriptable) { + start = (Scriptable) obj; + } else if ((start = tryPrimitive(cx, scope, obj)) != null) { + // use elemIncDecr() for primitives + return elemIncrDecr(obj, id, strict, cx, scope, incrDecrMask); + } else if ((start = tryWrap(cx, scope, obj)) == null) { + throw errorWithClassName("msg.invalid.type", obj); } Scriptable target = start; Object value; - search: { + search: { do { value = target.get(id, start); if (value != Scriptable.NOT_FOUND) { @@ -2927,17 +3213,17 @@ public static Object propIncrDecr(Object obj, String id, } target = target.getPrototype(); } while (target != null); - // TODO: strict mode flag? - start.put(id, start, NaNobj, false); + start.put(id, start, NaNobj, strict); return NaNobj; } - return doScriptableIncrDecr(target, id, start, value, + return doScriptableIncrDecr(target, id, start, strict, value, incrDecrMask); } private static Object doScriptableIncrDecr(Scriptable target, String id, Scriptable protoChainStart, + boolean strict, Object value, int incrDecrMask) { @@ -2958,8 +3244,7 @@ private static Object doScriptableIncrDecr(Scriptable target, --number; } Number result = wrapNumber(number); - // TODO: strict mode flag? - target.put(id, protoChainStart, result, false); + target.put(id, protoChainStart, result, strict); if (post) { return value; } else { @@ -2967,12 +3252,11 @@ private static Object doScriptableIncrDecr(Scriptable target, } } - // TODO: argument position for 'checked' - public static Object elemIncrDecr(Object obj, Object index, - Context cx, int incrDecrMask, - boolean checked) + public static Object elemIncrDecr(Object obj, Object index, boolean strict, + Context cx, Scriptable scope, + int incrDecrMask) { - Object value = getObjectElem(obj, index, cx); + Object value = getObjectValue(obj, index, strict, cx, scope); boolean post = ((incrDecrMask & Node.POST_FLAG) != 0); double number; if (value instanceof Number) { @@ -2990,7 +3274,7 @@ public static Object elemIncrDecr(Object obj, Object index, --number; } Number result = wrapNumber(number); - setObjectElem(obj, index, result, checked, cx); + putObjectValue(obj, index, strict, result, cx, scope); if (post) { return value; } else { diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index dd1d09994e..cffe5071a7 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -1497,7 +1497,7 @@ private void generateGenerator() addOptRuntimeInvoke("createNativeGenerator", "(Lorg/mozilla/javascript/NativeFunction;" +"Lorg/mozilla/javascript/Scriptable;" - +"Lorg/mozilla/javascript/Scriptable;II" + +"Ljava/lang/Object;II" +")Lorg/mozilla/javascript/Scriptable;"); cfw.add(ByteCode.ARETURN); @@ -2661,20 +2661,23 @@ private void generateExpression(Node node, Node parent) case Token.GETELEM: generateExpression(child, node); // object generateExpression(child.getNext(), node); // id + cfw.addPush(scriptOrFn.isInStrictMode()); cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) { addScriptRuntimeInvoke( - "getObjectIndex", - "(Ljava/lang/Object;D" + "getObjectValue", + "(Ljava/lang/Object;DZ" +"Lorg/mozilla/javascript/Context;" + +"Lorg/mozilla/javascript/Scriptable;" +")Ljava/lang/Object;"); } else { - cfw.addALoad(variableObjectLocal); addScriptRuntimeInvoke( - "getObjectElem", + "getObjectValue", "(Ljava/lang/Object;" +"Ljava/lang/Object;" + +"Z" +"Lorg/mozilla/javascript/Context;" +"Lorg/mozilla/javascript/Scriptable;" +")Ljava/lang/Object;"); @@ -3286,7 +3289,7 @@ private void visitSpecialCall(Node node, int type, int specialType, +"Ljava/lang/Object;" +"[Ljava/lang/Object;" +"Lorg/mozilla/javascript/Scriptable;" - +"Lorg/mozilla/javascript/Scriptable;" + +"Ljava/lang/Object;" +"I" // call type +")Ljava/lang/Object;"; cfw.addALoad(variableObjectLocal); @@ -3296,10 +3299,10 @@ private void visitSpecialCall(Node node, int type, int specialType, methodName = "callSpecial"; callSignature = "(Lorg/mozilla/javascript/Context;" +"Ljava/lang/Object;" - +"Lorg/mozilla/javascript/Scriptable;" + +"Ljava/lang/Object;" +"[Ljava/lang/Object;" +"Lorg/mozilla/javascript/Scriptable;" - +"Lorg/mozilla/javascript/Scriptable;" + +"Ljava/lang/Object;" +"I" // call type +"Ljava/lang/String;I" // filename, linenumber +"Z" // strict flag @@ -4451,11 +4454,13 @@ private void visitIncDec(Node node) case Token.NAME: cfw.addALoad(variableObjectLocal); cfw.addPush(child.getString()); // push name + cfw.addPush(scriptOrFn.isInStrictMode()); cfw.addALoad(contextLocal); cfw.addPush(incrDecrMask); addScriptRuntimeInvoke("nameIncrDecr", "(Lorg/mozilla/javascript/Scriptable;" +"Ljava/lang/String;" + +"Z" +"Lorg/mozilla/javascript/Context;" +"I)Ljava/lang/Object;"); break; @@ -4465,12 +4470,16 @@ private void visitIncDec(Node node) Node getPropChild = child.getFirstChild(); generateExpression(getPropChild, node); generateExpression(getPropChild.getNext(), node); + cfw.addPush(scriptOrFn.isInStrictMode()); cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); cfw.addPush(incrDecrMask); addScriptRuntimeInvoke("propIncrDecr", "(Ljava/lang/Object;" +"Ljava/lang/String;" + +"Z" +"Lorg/mozilla/javascript/Context;" + +"Lorg/mozilla/javascript/Scriptable;" +"I)Ljava/lang/Object;"); break; } @@ -4478,25 +4487,27 @@ private void visitIncDec(Node node) Node elemChild = child.getFirstChild(); generateExpression(elemChild, node); generateExpression(elemChild.getNext(), node); + cfw.addPush(scriptOrFn.isInStrictMode()); cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); cfw.addPush(incrDecrMask); - // TODO: checked flag! - cfw.add(ByteCode.ICONST_0); if (elemChild.getNext().getIntProp(Node.ISNUMBER_PROP, -1) != -1) { addOptRuntimeInvoke("elemIncrDecr", "(Ljava/lang/Object;" +"D" + +"Z" +"Lorg/mozilla/javascript/Context;" + +"Lorg/mozilla/javascript/Scriptable;" +"I" - +"Z" +")Ljava/lang/Object;"); } else { addScriptRuntimeInvoke("elemIncrDecr", "(Ljava/lang/Object;" +"Ljava/lang/Object;" + +"Z" +"Lorg/mozilla/javascript/Context;" + +"Lorg/mozilla/javascript/Scriptable;" +"I" - +"Z" +")Ljava/lang/Object;"); } break; @@ -5085,10 +5096,13 @@ private void visitGetProp(Node node, Node child) } /* for 'this.foo' we call getObjectProp(Scriptable...) which can - skip some casting overhead. + skip some casting overhead. (non-strict only) */ int childType = child.getType(); - if (childType == Token.THIS && nameChild.getType() == Token.STRING) { + if (!scriptOrFn.isInStrictMode() + && childType == Token.THIS + && nameChild.getType() == Token.STRING) + { cfw.addALoad(contextLocal); addScriptRuntimeInvoke( "getObjectProp", @@ -5097,12 +5111,14 @@ private void visitGetProp(Node node, Node child) +"Lorg/mozilla/javascript/Context;" +")Ljava/lang/Object;"); } else { + cfw.addPush(scriptOrFn.isInStrictMode()); cfw.addALoad(contextLocal); cfw.addALoad(variableObjectLocal); addScriptRuntimeInvoke( - "getObjectProp", + "getObjectValue", "(Ljava/lang/Object;" +"Ljava/lang/String;" + +"Z" +"Lorg/mozilla/javascript/Context;" +"Lorg/mozilla/javascript/Scriptable;" +")Ljava/lang/Object;"); @@ -5124,8 +5140,9 @@ private void visitSetProp(int type, Node node, Node child) // stack: ... object object name -> ... object name object name cfw.add(ByteCode.DUP_X1); //for 'this.foo += ...' we call thisGet which can skip some - //casting overhead. - if (objectChild.getType() == Token.THIS + //casting overhead. (non-strict only) + if (!scriptOrFn.isInStrictMode() + && objectChild.getType() == Token.THIS && nameChild.getType() == Token.STRING) { cfw.addALoad(contextLocal); @@ -5136,28 +5153,36 @@ private void visitSetProp(int type, Node node, Node child) +"Lorg/mozilla/javascript/Context;" +")Ljava/lang/Object;"); } else { + cfw.addPush(scriptOrFn.isInStrictMode()); cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); addScriptRuntimeInvoke( - "getObjectProp", + "getObjectValue", "(Ljava/lang/Object;" +"Ljava/lang/String;" + +"Z" +"Lorg/mozilla/javascript/Context;" + +"Lorg/mozilla/javascript/Scriptable;" +")Ljava/lang/Object;"); } } generateExpression(child, node); + cfw.add(ByteCode.DUP_X2); assert (type == Token.STRICT_SETPROP || type == Token.STRICT_SETPROP_OP) == scriptOrFn.isInStrictMode(); cfw.addPush(scriptOrFn.isInStrictMode()); + cfw.add(ByteCode.SWAP); cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); addScriptRuntimeInvoke( - "setObjectProp", + "putObjectValue", "(Ljava/lang/Object;" +"Ljava/lang/String;" - +"Ljava/lang/Object;" +"Z" + +"Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" - +")Ljava/lang/Object;"); + +"Lorg/mozilla/javascript/Scriptable;" + +")V"); } private void visitSetElem(int type, Node node, Node child) @@ -5175,48 +5200,60 @@ private void visitSetElem(int type, Node node, Node child) // stack: ... object object number // -> ... object number object number cfw.add(ByteCode.DUP2_X1); + cfw.addPush(scriptOrFn.isInStrictMode()); cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); addOptRuntimeInvoke( - "getObjectIndex", - "(Ljava/lang/Object;D" + "getObjectValue", + "(Ljava/lang/Object;DZ" +"Lorg/mozilla/javascript/Context;" + +"Lorg/mozilla/javascript/Scriptable;" +")Ljava/lang/Object;"); } else { // stack: ... object object indexObject // -> ... object indexObject object indexObject cfw.add(ByteCode.DUP_X1); + cfw.addPush(scriptOrFn.isInStrictMode()); cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); addScriptRuntimeInvoke( - "getObjectElem", + "getObjectValue", "(Ljava/lang/Object;" +"Ljava/lang/Object;" + +"Z" +"Lorg/mozilla/javascript/Context;" + +"Lorg/mozilla/javascript/Scriptable;" +")Ljava/lang/Object;"); } } generateExpression(child, node); + cfw.add(ByteCode.DUP_X2); assert (type == Token.STRICT_SETELEM || type == Token.STRICT_SETELEM_OP) == scriptOrFn.isInStrictMode(); cfw.addPush(scriptOrFn.isInStrictMode()); + cfw.add(ByteCode.SWAP); cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); if (indexIsNumber) { addScriptRuntimeInvoke( - "setObjectIndex", + "putObjectValue", "(Ljava/lang/Object;" +"D" - +"Ljava/lang/Object;" +"Z" + +"Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" - +")Ljava/lang/Object;"); + +"Lorg/mozilla/javascript/Scriptable;" + +")V"); } else { addScriptRuntimeInvoke( - "setObjectElem", + "putObjectValue", "(Ljava/lang/Object;" +"Ljava/lang/Object;" - +"Ljava/lang/Object;" +"Z" + +"Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" - +")Ljava/lang/Object;"); + +"Lorg/mozilla/javascript/Scriptable;" + +")V"); } } diff --git a/src/org/mozilla/javascript/optimizer/OptRuntime.java b/src/org/mozilla/javascript/optimizer/OptRuntime.java index 952c531996..a8e354692a 100644 --- a/src/org/mozilla/javascript/optimizer/OptRuntime.java +++ b/src/org/mozilla/javascript/optimizer/OptRuntime.java @@ -156,13 +156,12 @@ public static Object add(double val1, Object val2) return new ConsString(toString(val1), (CharSequence)val2); } - // TODO: argument position for 'checked' - public static Object elemIncrDecr(Object obj, double index, - Context cx, int incrDecrMask, - boolean checked) + public static Object elemIncrDecr(Object obj, double index, boolean strict, + Context cx, Scriptable scope, + int incrDecrMask) { - return ScriptRuntime.elemIncrDecr(obj, new Double(index), cx, - incrDecrMask, checked); + return ScriptRuntime.elemIncrDecr(obj, new Double(index), strict, + cx, scope, incrDecrMask); } public static Object[] padStart(Object[] currentArgs, int count) { @@ -178,9 +177,9 @@ public static void initFunction(NativeFunction fn, int functionType, } public static Object callSpecial(Context cx, Object fun, - Scriptable thisObj, Object[] args, + Object thisObj, Object[] args, Scriptable scope, - Scriptable callerThis, int callType, + Object callerThis, int callType, String fileName, int lineNumber, boolean strictMode) { @@ -192,7 +191,7 @@ public static Object callSpecial(Context cx, Object fun, public static Object newObjectSpecial(Context cx, Object fun, Object[] args, Scriptable scope, - Scriptable callerThis, int callType) + Object callerThis, int callType) { return ScriptRuntime.newSpecial(cx, fun, args, scope, callType); } @@ -285,7 +284,7 @@ public static void throwStopIteration(Object obj) { public static Scriptable createNativeGenerator(NativeFunction funObj, Scriptable scope, - Scriptable thisObj, + Object thisObj, int maxLocals, int maxStack) { @@ -315,17 +314,16 @@ public static class GeneratorState { static final String resumptionPoint_NAME = "resumptionPoint"; static final String resumptionPoint_TYPE = "I"; - public Scriptable thisObj; + public Object thisObj; static final String thisObj_NAME = "thisObj"; - static final String thisObj_TYPE = - "Lorg/mozilla/javascript/Scriptable;"; + static final String thisObj_TYPE = "Ljava/lang/Object;"; Object[] stackState; Object[] localsState; int maxLocals; int maxStack; - GeneratorState(Scriptable thisObj, int maxLocals, int maxStack) { + GeneratorState(Object thisObj, int maxLocals, int maxStack) { this.thisObj = thisObj; this.maxLocals = maxLocals; this.maxStack = maxStack; From f5329f629e6bc61a75d40b45b6a09440d801d0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 072/111] - support special GetValue and PutValue on primitives in call expressions - handle primitive thisObject in NativeString, NativeBoolean and NativeNumber functions --- src/org/mozilla/javascript/Context.java | 2 +- src/org/mozilla/javascript/Interpreter.java | 17 +- src/org/mozilla/javascript/NativeBoolean.java | 9 +- src/org/mozilla/javascript/NativeNumber.java | 9 +- src/org/mozilla/javascript/NativeString.java | 15 +- src/org/mozilla/javascript/ScriptRuntime.java | 160 ++++++++++-------- .../mozilla/javascript/optimizer/Codegen.java | 13 +- .../javascript/optimizer/OptRuntime.java | 8 +- 8 files changed, 135 insertions(+), 98 deletions(-) diff --git a/src/org/mozilla/javascript/Context.java b/src/org/mozilla/javascript/Context.java index dafa975e6b..49813c5216 100644 --- a/src/org/mozilla/javascript/Context.java +++ b/src/org/mozilla/javascript/Context.java @@ -2665,7 +2665,7 @@ public void removeActivationName(String name) long scratchUint32; // It can be used to return the second Scriptable result from function - Scriptable scratchScriptable; + Object scratchThis; // Generate an observer count on compiled code public boolean generateObserverCount = false; diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index 5b2fcea9cf..579f3ec2b8 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -1540,7 +1540,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, stack[stackTop] = ScriptRuntime.getNameObjectAndThis(stringReg, cx, frame.scope); ++stackTop; - Scriptable thisObj = ScriptRuntime.lastStoredScriptable(cx); + Object thisObj = ScriptRuntime.lastStoredThis(cx); stack[stackTop] = (thisObj != null ? thisObj : Undefined.instance); continue Loop; } @@ -1551,7 +1551,9 @@ private static Object interpretLoop(Context cx, CallFrame frame, stack[stackTop] = ScriptRuntime.getPropObjectAndThis(obj, stringReg, cx, frame.scope); ++stackTop; - stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); + // ignore stored this + ScriptRuntime.lastStoredThis(cx); + stack[stackTop] = obj; continue Loop; } case Icode_ELEM_AND_THIS: { @@ -1559,8 +1561,11 @@ private static Object interpretLoop(Context cx, CallFrame frame, if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop - 1]); Object id = stack[stackTop]; if (id == DBL_MRK) id = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop - 1] = ScriptRuntime.getElemObjectAndThis(obj, id, cx); - stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); + stack[stackTop - 1] = ScriptRuntime.getElemObjectAndThis(obj, id, cx, + frame.scope); + // ignore stored this + ScriptRuntime.lastStoredThis(cx); + stack[stackTop] = obj; continue Loop; } case Icode_VALUE_AND_THIS : { @@ -1568,8 +1573,8 @@ private static Object interpretLoop(Context cx, CallFrame frame, if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]); stack[stackTop] = ScriptRuntime.getValueObjectAndThis(value, cx); ++stackTop; - // ignore stored scriptable - ScriptRuntime.lastStoredScriptable(cx); + // ignore stored this + ScriptRuntime.lastStoredThis(cx); stack[stackTop] = Undefined.instance; continue Loop; } diff --git a/src/org/mozilla/javascript/NativeBoolean.java b/src/org/mozilla/javascript/NativeBoolean.java index f59fb88c49..7a6f4ea9c2 100644 --- a/src/org/mozilla/javascript/NativeBoolean.java +++ b/src/org/mozilla/javascript/NativeBoolean.java @@ -121,9 +121,14 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, // The rest of Boolean.prototype methods require thisObj to be Boolean - if (!(thisObj instanceof NativeBoolean)) + boolean value; + if (thisObj instanceof Boolean) { + value = ((Boolean)thisObj).booleanValue(); + } else if (thisObj instanceof NativeBoolean) { + value = ((NativeBoolean)thisObj).booleanValue; + } else { throw incompatibleCallError(f); - boolean value = ((NativeBoolean)thisObj).booleanValue; + } switch (id) { diff --git a/src/org/mozilla/javascript/NativeNumber.java b/src/org/mozilla/javascript/NativeNumber.java index b6cfe92028..53ec4c03d4 100644 --- a/src/org/mozilla/javascript/NativeNumber.java +++ b/src/org/mozilla/javascript/NativeNumber.java @@ -136,9 +136,14 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, // The rest of Number.prototype methods require thisObj to be Number - if (!(thisObj instanceof NativeNumber)) + double value; + if (thisObj instanceof Number) { + value = ((Number)thisObj).doubleValue(); + } else if (thisObj instanceof NativeNumber) { + value = ((NativeNumber)thisObj).doubleValue; + } else { throw incompatibleCallError(f); - double value = ((NativeNumber)thisObj).doubleValue; + } switch (id) { diff --git a/src/org/mozilla/javascript/NativeString.java b/src/org/mozilla/javascript/NativeString.java index bcff5ff6aa..c160edc0e4 100644 --- a/src/org/mozilla/javascript/NativeString.java +++ b/src/org/mozilla/javascript/NativeString.java @@ -272,11 +272,11 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, case Id_toString: case Id_valueOf: // ECMA 15.5.4.2: 'the toString function is not generic. - CharSequence cs = realThis(thisObj, f).string; + CharSequence cs = realThis(thisObj, f); return cs instanceof String ? cs : cs.toString(); case Id_toSource: { - CharSequence s = realThis(thisObj, f).string; + CharSequence s = realThis(thisObj, f); return "(new String(\""+ScriptRuntime.escapeString(s.toString())+"\"))"; } @@ -452,11 +452,14 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } } - private static NativeString realThis(Object thisObj, IdFunctionObject f) + private static CharSequence realThis(Object thisObj, IdFunctionObject f) { - if (!(thisObj instanceof NativeString)) - throw incompatibleCallError(f); - return (NativeString)thisObj; + if (thisObj instanceof CharSequence) { + return (CharSequence)thisObj; + } else if (thisObj instanceof NativeString) { + return ((NativeString)thisObj).string; + } + throw incompatibleCallError(f); } /* diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index b4d6201b72..fa92647582 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -1513,7 +1513,9 @@ private static Object getPrimitiveValue(Object base, Scriptable obj, String property, Context cx, Scriptable scope) { // TODO: could need some optimization - assert obj instanceof ScriptableObject; + assert obj instanceof NativeString + || obj instanceof NativeNumber + || obj instanceof NativeBoolean; ScriptableObject sobj = (ScriptableObject) obj; PropertyDescriptor desc = sobj.$getProperty(property); if (desc == null) { @@ -1537,7 +1539,9 @@ private static void putPrimitiveValue(Object base, Scriptable obj, Object value, Context cx, Scriptable scope) { // TODO: could need some optimization - assert obj instanceof ScriptableObject; + assert obj instanceof NativeString + || obj instanceof NativeNumber + || obj instanceof NativeBoolean; ScriptableObject sobj = (ScriptableObject) obj; if (!sobj.$canPut(property)) { if (checked) { @@ -1563,6 +1567,7 @@ private static void putPrimitiveValue(Object base, Scriptable obj, return; } Object setter = desc.getSetter(); + assert setter instanceof Callable; ((Callable) setter).call(cx, scope, base, new Object[]{ value }); } @@ -2154,7 +2159,7 @@ private static Object nameOrFunction(Context cx, Scriptable scope, result = topScopeName(cx, scope, name); if (result == Scriptable.NOT_FOUND) { if (asFunctionCall) { - storeScriptable(cx, BAD_SCRIPTABLE); + storeThis(cx, BAD_SCRIPTABLE); return createNotFoundError(scope, name); } else if (firstXMLObject == null) { throw notFoundError(scope, name); @@ -2173,10 +2178,10 @@ private static Object nameOrFunction(Context cx, Scriptable scope, if (asFunctionCall) { if (!(result instanceof Callable)) { - storeScriptable(cx, BAD_SCRIPTABLE); + storeThis(cx, BAD_SCRIPTABLE); return notFunctionError(result, name); } - storeScriptable(cx, thisObj); + storeThis(cx, thisObj); } return result; @@ -2547,8 +2552,8 @@ public static Callable ensureCallable(Object value) throws RuntimeException { */ private static Callable ensureCallable(Context cx, Object value) { if (!(value instanceof Callable)) { - // clear stored scriptable before throwing - lastStoredScriptable(cx); + // clear stored this before throwing + lastStoredThis(cx); throw (RuntimeException)value; } return (Callable)value; @@ -2567,11 +2572,11 @@ public static Callable getNameFunctionAndThis(String name, { Object value = getNameObjectAndThis(name, cx, scope); // restore old behaviour - scope = lastStoredScriptable(cx); - if (scope == null) { - scope = cx.topCallScope; + Object thisObj = lastStoredThis(cx); + if (thisObj == null) { + thisObj = cx.topCallScope; } - storeScriptable(cx, scope); + storeThis(cx, thisObj); return ensureCallable(cx, value); } @@ -2586,7 +2591,7 @@ public static Callable getElemFunctionAndThis(Object obj, Object elem, Context cx) { - Object value = getElemObjectAndThis(obj, elem, cx); + Object value = getElemObjectAndThis(obj, elem, cx, getTopCallScope(cx)); return ensureCallable(cx, value); } @@ -2603,7 +2608,7 @@ public static Callable getPropFunctionAndThis(Object obj, String property, Context cx) { - Object value = getPropObjectAndThis(obj, property, cx); + Object value = getPropObjectAndThis(obj, property, cx, getTopCallScope(cx)); return ensureCallable(cx, value); } @@ -2616,7 +2621,7 @@ public static Callable getPropFunctionAndThis(Object obj, */ public static Callable getPropFunctionAndThis(Object obj, String property, - Context cx, final Scriptable scope) + Context cx, Scriptable scope) { Object value = getPropObjectAndThis(obj, property, cx, scope); return ensureCallable(cx, value); @@ -2676,7 +2681,7 @@ public static Object getNameObjectAndThis(String name, if (parent == null) { Object result = topScopeName(cx, scope, name); if (!(result instanceof Callable)) { - storeScriptable(cx, BAD_SCRIPTABLE); + storeThis(cx, BAD_SCRIPTABLE); if (result == Scriptable.NOT_FOUND) { return createNotFoundError(scope, name); } else { @@ -2684,7 +2689,7 @@ public static Object getNameObjectAndThis(String name, } } // Top scope is not NativeWith or NativeCall => thisObj == scope - storeScriptable(cx, null); + storeThis(cx, null); return result; } @@ -2701,44 +2706,15 @@ public static Object getNameObjectAndThis(String name, */ public static Object getElemObjectAndThis(Object obj, Object elem, - Context cx) + Context cx, Scriptable scope) { String str = toStringIdOrIndex(cx, elem); if (str != null) { - return getPropObjectAndThis(obj, str, cx); - } - int index = lastIndexResult(cx); - - Scriptable thisObj = toObjectOrNull(cx, obj); - if (thisObj == null) { - throw undefCallError(obj, String.valueOf(index)); - } - - Object value = ScriptableObject.getProperty(thisObj, index); - if (!(value instanceof Callable)) { - storeScriptable(cx, BAD_SCRIPTABLE); - return notFunctionError(value, elem); + return getObjectAndThisHelper(obj, str, -1, cx, scope); + } else { + int index = lastIndexResult(cx); + return getObjectAndThisHelper(obj, null, index, cx, scope); } - - storeScriptable(cx, thisObj); - return value; - } - - /** - * Prepare for calling obj.property(...): return function corresponding to - * obj.property and make obj properly converted to Scriptable available - * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. - * The caller must call ScriptRuntime.lastStoredScriptable() immediately - * after calling this method. - * Warning: this doesn't allow to resolve primitive prototype properly when - * many top scopes are involved. - */ - public static Object getPropObjectAndThis(Object obj, - String property, - Context cx) - { - Scriptable thisObj = toObjectOrNull(cx, obj); - return getPropFunctionAndThisHelper(obj, property, cx, thisObj); } /** @@ -2750,32 +2726,60 @@ public static Object getPropObjectAndThis(Object obj, */ public static Object getPropObjectAndThis(Object obj, String property, - Context cx, final Scriptable scope) + Context cx, Scriptable scope) { - Scriptable thisObj = toObjectOrNull(cx, obj, scope); - return getPropFunctionAndThisHelper(obj, property, cx, thisObj); + assert property != null; + return getObjectAndThisHelper(obj, property, -1, cx, scope); } - private static Object getPropFunctionAndThisHelper(Object obj, - String property, Context cx, Scriptable thisObj) - { - if (thisObj == null) { + private static Object getObjectAndThisHelper(Object obj, String property, + int index, Context cx, + Scriptable scope) { + Scriptable sobj; + Object value; + // spidermonkey also tries noSuchMethod for index properties, rhino bug? + boolean tryNoSuchMethod = property != null; + if (obj == null || obj == Undefined.instance) { throw undefCallError(obj, property); + } else if (obj instanceof Scriptable) { + sobj = (Scriptable) obj; + if (property == null) { + value = ScriptableObject.getProperty(sobj, index); + } else { + value = ScriptableObject.getProperty(sobj, property); + } + } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { + // follow spidermonkey (possible bug?) + tryNoSuchMethod = false; + if (property == null) { property = toString(index); } + value = getPrimitiveValue(obj, sobj, property, cx, scope); + } else if ((sobj = tryWrap(cx, scope, obj)) != null) { + if (property == null) { + value = ScriptableObject.getProperty(sobj, index); + } else { + value = ScriptableObject.getProperty(sobj, property); + } + } else { + throw errorWithClassName("msg.invalid.type", obj); } - Object value = ScriptableObject.getProperty(thisObj, property); - if (!(value instanceof Callable)) { - Object noSuchMethod = ScriptableObject.getProperty(thisObj, "__noSuchMethod__"); - if (noSuchMethod instanceof Callable) - value = new NoSuchMethodShim((Callable)noSuchMethod, property); + boolean valueCallable = value instanceof Callable; + if (tryNoSuchMethod && !valueCallable) { + Object noSuchMethod = ScriptableObject.getProperty(sobj, "__noSuchMethod__"); + if (noSuchMethod instanceof Callable) { + if (property == null) { property = toString(index); } + valueCallable = true; + value = new NoSuchMethodShim((Callable) noSuchMethod, property); + } } - if (!(value instanceof Callable)) { - storeScriptable(cx, BAD_SCRIPTABLE); - return notFunctionError(thisObj, value, property); + if (!valueCallable) { + if (property == null) { property = toString(index); } + storeThis(cx, BAD_SCRIPTABLE); + return notFunctionError(sobj, value, property); } - storeScriptable(cx, thisObj); + storeThis(cx, sobj); return value; } @@ -2789,7 +2793,7 @@ private static Object getPropFunctionAndThisHelper(Object obj, public static Object getValueObjectAndThis(Object value, Context cx) { if (!(value instanceof Callable)) { - storeScriptable(cx, BAD_SCRIPTABLE); + storeThis(cx, BAD_SCRIPTABLE); return notFunctionError(value); } Scriptable thisObj = null; @@ -2809,7 +2813,7 @@ public static Object getValueObjectAndThis(Object value, Context cx) thisObj = ScriptableObject.getTopLevelScope(thisObj); } } - storeScriptable(cx, thisObj); + storeThis(cx, thisObj); return value; } @@ -4555,21 +4559,27 @@ public static long lastUint32Result(Context cx) return value; } - private static void storeScriptable(Context cx, Scriptable value) + private static void storeThis(Context cx, Object value) { - // The previously stored scratchScriptable should be consumed - if (cx.scratchScriptable != null) + // The previously stored scratchThis should be consumed + if (cx.scratchThis != null) throw new IllegalStateException(); - cx.scratchScriptable = value; + cx.scratchThis = value; } - public static Scriptable lastStoredScriptable(Context cx) + public static Object lastStoredThis(Context cx) { - Scriptable result = cx.scratchScriptable; - cx.scratchScriptable = null; + Object result = cx.scratchThis; + cx.scratchThis = null; return result; } + @Deprecated + public static Scriptable lastStoredScriptable(Context cx) + { + return toObjectOrNull(cx, lastStoredThis(cx)); + } + static String makeUrlForGeneratedScript (boolean isEval, String masterScriptUrl, int masterScriptLine) { diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index cffe5071a7..9cbc1033ba 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -3645,6 +3645,7 @@ private void generateObjectAndThisObj(Node node, Node parent) case Token.GETELEM: { Node target = node.getFirstChild(); generateExpression(target, node); + cfw.add(ByteCode.DUP); // dup b/c we ignore lastStoredThis Node id = target.getNext(); if (type == Token.GETPROP) { String property = id.getString(); @@ -3664,11 +3665,13 @@ private void generateObjectAndThisObj(Node node, Node parent) throw Codegen.badTree(); generateExpression(id, node); // id cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); addScriptRuntimeInvoke( "getElemObjectAndThis", "(Ljava/lang/Object;" +"Ljava/lang/Object;" +"Lorg/mozilla/javascript/Context;" + +"Lorg/mozilla/javascript/Scriptable;" +")Ljava/lang/Object;"); } break; @@ -3701,13 +3704,17 @@ private void generateObjectAndThisObj(Node node, Node parent) // Get thisObj prepared by get(Name|Prop|Elem|Value)ObjectAndThis cfw.addALoad(contextLocal); addScriptRuntimeInvoke( - "lastStoredScriptable", + "lastStoredThis", "(Lorg/mozilla/javascript/Context;" - +")Lorg/mozilla/javascript/Scriptable;"); + +")Ljava/lang/Object;"); switch (type) { case Token.GETPROP: case Token.GETELEM: - // no further changes needed + // ignore stored scriptable, obj is still on stack (see DUP above) + // stack: ... obj value stored -> ... obj value + cfw.add(ByteCode.POP); + // stack: ... obj value -> ... value obj + cfw.add(ByteCode.SWAP); break; case Token.NAME: { // replace null with undefined diff --git a/src/org/mozilla/javascript/optimizer/OptRuntime.java b/src/org/mozilla/javascript/optimizer/OptRuntime.java index a8e354692a..6098fb12fa 100644 --- a/src/org/mozilla/javascript/optimizer/OptRuntime.java +++ b/src/org/mozilla/javascript/optimizer/OptRuntime.java @@ -97,7 +97,7 @@ public static Object callName(Object[] args, String name, Context cx, Scriptable scope) { Object f = getNameObjectAndThis(name, cx, scope); - Object thisObj = lastStoredScriptable(cx); + Object thisObj = lastStoredThis(cx); thisObj = (thisObj != null ? thisObj : Undefined.instance); Callable c = ensureCallable(f); return c.call(cx, scope, thisObj, args); @@ -110,7 +110,7 @@ public static Object callName0(String name, Context cx, Scriptable scope) { Object f = getNameObjectAndThis(name, cx, scope); - Object thisObj = lastStoredScriptable(cx); + Object thisObj = lastStoredThis(cx); thisObj = (thisObj != null ? thisObj : Undefined.instance); Callable c = ensureCallable(f); return c.call(cx, scope, thisObj, ScriptRuntime.emptyArgs); @@ -123,7 +123,9 @@ public static Object callProp0(Object value, String property, Context cx, Scriptable scope) { Object f = getPropObjectAndThis(value, property, cx, scope); - Scriptable thisObj = lastStoredScriptable(cx); + // ignore stored this + Object thisObj = lastStoredThis(cx); + thisObj = value; Callable c = ensureCallable(f); return c.call(cx, scope, thisObj, ScriptRuntime.emptyArgs); } From 33ab68cf6fed74a75d1189b2f1c551f1bfbc7d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 073/111] prepare optimization for GetValue/PutValue on primitives --- src/org/mozilla/javascript/ScriptRuntime.java | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index fa92647582..d2717987ca 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -1510,12 +1510,13 @@ private static Scriptable tryWrap(Context cx, Scriptable scope, Object val) { * Special [[Get]] if reference base value is primitive, see ES5.1 [8.7.1] */ private static Object getPrimitiveValue(Object base, Scriptable obj, - String property, Context cx, - Scriptable scope) { + String property, int index, + Context cx, Scriptable scope) { // TODO: could need some optimization assert obj instanceof NativeString || obj instanceof NativeNumber || obj instanceof NativeBoolean; + if (property == null) { property = toString(index); } ScriptableObject sobj = (ScriptableObject) obj; PropertyDescriptor desc = sobj.$getProperty(property); if (desc == null) { @@ -1592,7 +1593,13 @@ public static Object getObjectValue(Object obj, Object elem, boolean strict, } else if (obj instanceof Scriptable) { sobj = (Scriptable) obj; } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { - return getPrimitiveValue(obj, sobj, toString(elem), cx, scope); + String s = toStringIdOrIndex(cx, elem); + if (s != null) { + return getPrimitiveValue(obj, sobj, s, -1, cx, scope); + } else { + int index = lastIndexResult(cx); + return getPrimitiveValue(obj, sobj, null, index, cx, scope); + } } else if ((sobj = tryWrap(cx, scope, obj)) == null) { throw errorWithClassName("msg.invalid.type", obj); } @@ -1620,7 +1627,8 @@ public static Object getObjectValue(Object obj, String property, boolean strict, } else if (obj instanceof Scriptable) { sobj = (Scriptable) obj; } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { - return getPrimitiveValue(obj, sobj, property, cx, scope); + assert property != null; + return getPrimitiveValue(obj, sobj, property, -1, cx, scope); } else if ((sobj = tryWrap(cx, scope, obj)) == null) { throw errorWithClassName("msg.invalid.type", obj); } @@ -1648,7 +1656,13 @@ public static Object getObjectValue(Object obj, double dblIndex, boolean strict, } else if (obj instanceof Scriptable) { sobj = (Scriptable) obj; } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { - return getPrimitiveValue(obj, sobj, toString(dblIndex), cx, scope); + int index = (int) dblIndex; + if (index == dblIndex) { + return getPrimitiveValue(obj, sobj, null, index, cx, scope); + } else { + String s = toString(dblIndex); + return getPrimitiveValue(obj, sobj, s, -1, cx, scope); + } } else if ((sobj = tryWrap(cx, scope, obj)) == null) { throw errorWithClassName("msg.invalid.type", obj); } @@ -2751,8 +2765,7 @@ private static Object getObjectAndThisHelper(Object obj, String property, } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { // follow spidermonkey (possible bug?) tryNoSuchMethod = false; - if (property == null) { property = toString(index); } - value = getPrimitiveValue(obj, sobj, property, cx, scope); + value = getPrimitiveValue(obj, sobj, property, index, cx, scope); } else if ((sobj = tryWrap(cx, scope, obj)) != null) { if (property == null) { value = ScriptableObject.getProperty(sobj, index); From e51dbbec7037d0b771e27ef4199aade0b5b16a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 074/111] add PropertyDescriptor#toString() --- .../javascript/PropertyDescriptor.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/org/mozilla/javascript/PropertyDescriptor.java b/src/org/mozilla/javascript/PropertyDescriptor.java index 2b7d0f98f8..819152e587 100755 --- a/src/org/mozilla/javascript/PropertyDescriptor.java +++ b/src/org/mozilla/javascript/PropertyDescriptor.java @@ -329,4 +329,37 @@ public void setConfigurable(boolean configurable) { attributes = (configurable ? attributes & ~PERMANENT : attributes | PERMANENT); } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('{'); + if (hasValue()) { + String value = ScriptRuntime.toString(getValue()).trim(); + sb.append("[[Value]]: ").append(value).append(", "); + } + if (hasGetter()) { + String getter = ScriptRuntime.toString(getGetter()).trim(); + sb.append("[[Get]]: ").append(getter).append(", "); + } + if (hasSetter()) { + String setter = ScriptRuntime.toString(getSetter()).trim(); + sb.append("[[Set]]: ").append(setter).append(", "); + } + if (hasWritable()) { + sb.append("[[Writable]]: ").append(isWritable()).append(", "); + } + if (hasEnumerable()) { + sb.append("[[Enumerable]]: ").append(isEnumerable()).append(", "); + } + if (hasConfigurable()) { + sb.append("[[Configurable]]: ").append(isConfigurable()).append(", "); + } + if (sb.length() != 1) { + // if not only initial '{', remove trailing ", " + sb.setLength(sb.length() - 2); + } + sb.append('}'); + return sb.toString(); + } } From 3f38ce0a25a6a66be1933efa5dd9fd098e816c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 075/111] Add cache for parameter size; Increase initial size for the various caches to decrease number of rehashes necessary over time --- .../mozilla/classfile/ClassFileWriter.java | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/org/mozilla/classfile/ClassFileWriter.java b/src/org/mozilla/classfile/ClassFileWriter.java index 5852711ded..38e2d1476c 100644 --- a/src/org/mozilla/classfile/ClassFileWriter.java +++ b/src/org/mozilla/classfile/ClassFileWriter.java @@ -2872,6 +2872,16 @@ private static void badStack(int value) throw new IllegalStateException(s); } + private int sizeOfParameters(String pString) + { + int size = paramSizeCache.get(pString, -1); + if (size == -1) { + size = sizeOfParameters0(pString); + paramSizeCache.put(pString, size); + } + return size; + } + /* Really weird. Returns an int with # parameters in hi 16 bits, and stack difference removal of parameters from stack and pushing the @@ -2880,7 +2890,7 @@ private static void badStack(int value) If Java really supported references we wouldn't have to be this perverted. */ - private static int sizeOfParameters(String pString) + private static int sizeOfParameters0(String pString) { int length = pString.length(); int rightParenthesis = pString.lastIndexOf(')'); @@ -4297,6 +4307,8 @@ private void finalizeSuperBlockStarts() { private ObjArray itsVarDescriptors; private char[] tmpCharBuffer = new char[64]; + + private ObjToIntMap paramSizeCache = new ObjToIntMap(100); } final class ExceptionTableEntry @@ -4755,16 +4767,18 @@ void ensure(int howMuch) private static final int MAX_UTF_ENCODING_SIZE = 65535; - private UintMap itsStringConstHash = new UintMap(); - private ObjToIntMap itsUtf8Hash = new ObjToIntMap(); - private ObjToIntMap itsFieldRefHash = new ObjToIntMap(); - private ObjToIntMap itsMethodRefHash = new ObjToIntMap(); - private ObjToIntMap itsClassHash = new ObjToIntMap(); + // TODO: initialize maps with reasonable default size + private UintMap itsStringConstHash = new UintMap(100); + private ObjToIntMap itsUtf8Hash = new ObjToIntMap(200); + private ObjToIntMap itsFieldRefHash = new ObjToIntMap(20); + private ObjToIntMap itsMethodRefHash = new ObjToIntMap(100); + private ObjToIntMap itsClassHash = new ObjToIntMap(20); private int itsTop; private int itsTopIndex; - private UintMap itsConstantData = new UintMap(); - private UintMap itsPoolTypes = new UintMap(); + // TODO: initialize maps with reasonable default size + private UintMap itsConstantData = new UintMap(100); + private UintMap itsPoolTypes = new UintMap(100); private byte itsPool[]; } From 88571d314b77e16b8b5a231301c6c6805bdcca45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 076/111] Use Java1.4 Throwable API to obtain stacktrace information instead of printing the stacktrace and then performing string matching --- src/org/mozilla/javascript/Context.java | 41 +++++-------------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/src/org/mozilla/javascript/Context.java b/src/org/mozilla/javascript/Context.java index 49813c5216..2ace772e31 100644 --- a/src/org/mozilla/javascript/Context.java +++ b/src/org/mozilla/javascript/Context.java @@ -42,7 +42,6 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.CharArrayWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.Reader; @@ -2489,39 +2488,15 @@ static String getSourcePositionFromStack(int[] linep) * A bit of a hack, but the only way to get filename and line * number from an enclosing frame. */ - CharArrayWriter writer = new CharArrayWriter(); - RuntimeException re = new RuntimeException(); - re.printStackTrace(new PrintWriter(writer)); - String s = writer.toString(); - int open = -1; - int close = -1; - int colon = -1; - for (int i=0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == ':') - colon = i; - else if (c == '(') - open = i; - else if (c == ')') - close = i; - else if (c == '\n' && open != -1 && close != -1 && colon != -1 && - open < colon && colon < close) - { - String fileStr = s.substring(open + 1, colon); - if (!fileStr.endsWith(".java")) { - String lineStr = s.substring(colon + 1, close); - try { - linep[0] = Integer.parseInt(lineStr); - if (linep[0] < 0) { - linep[0] = 0; - } - return fileStr; - } - catch (NumberFormatException e) { - // fall through - } + StackTraceElement[] stackTrace = new Throwable().getStackTrace(); + for (StackTraceElement st : stackTrace) { + String file = st.getFileName(); + if (!(file == null || file.endsWith(".java"))) { + int line = st.getLineNumber(); + if (line >= 0) { + linep[0] = line; + return file; } - open = close = colon = -1; } } From 1e7d7ddaef79cf71aaf1c3bdd675b5dc352958e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 077/111] Add helper methods for faster element access on primitives --- .../javascript/IdScriptableObject.java | 24 +++++++++++++++++++ .../mozilla/javascript/ScriptableObject.java | 24 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index 15ce534c8b..205cdcf052 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -820,6 +820,30 @@ protected void updateOwnProperty(String name, PropertyDescriptor desc, super.updateOwnProperty(name, desc, current); } + /** + * Internal helper method, should only be called on prototype objects + * + * @see NativeString#getPrimitiveValue(CharSequence, String, int, Context, Scriptable) + * @see NativeBoolean#getPrimitiveValue(Boolean, String, int, Context, Scriptable) + * @see NativeString#getPrimitiveValue(CharSequence, String, int, Context, Scriptable) + */ + final Object getSlotOrProtoValue(String name, int index, Object base, + Context cx, Scriptable scope) { + // search slot properties first, cf. get() + Object value = getSlotValue(name, index, base, cx, scope); + if (value == NOT_FOUND && name != null) { + // no slot property found, proceed with prototype properties + PrototypeValues pv = prototypeValues; + assert pv != null; + int id = pv.findId(name); + if (id != 0) { + // found prototype property (but may be deleted!) + value = pv.get(id); + } + } + return value; + } + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 11e8682cfd..550a6aabd2 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -3154,6 +3154,30 @@ private static class SlotPropertyDescriptor extends PropertyDescriptor { } } + /** + * Internal helper method + * + * @see IdScriptableObject#getSlotOrProtoValue(String, int, Object, Context, Scriptable) + */ + final Object getSlotValue(String name, int index, Object base, + Context cx, Scriptable scope) { + Slot slot = getSlot(name, index, SLOT_QUERY); + if (slot == null) { + return NOT_FOUND; + } + slot = unwrapSlot(slot); + if (slot instanceof GetterSlot) { + Object getter = ((GetterSlot) slot).getter; + if (getter == null || getter == Undefined.instance) { + return Undefined.instance; + } + assert getter instanceof Callable; + return ((Callable) getter).call(cx, scope, base, ScriptRuntime.emptyArgs); + } else { + return slot.value; + } + } + /** * ECMAScript 5 * From 6f4f27dd9e6128be5dcb3c833a8f4d84e36fd801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 078/111] Improved [[Get]] operations for primitives --- src/org/mozilla/javascript/NativeBoolean.java | 29 +++++++++++++++ src/org/mozilla/javascript/NativeNumber.java | 29 +++++++++++++++ src/org/mozilla/javascript/NativeString.java | 37 +++++++++++++++++++ 3 files changed, 95 insertions(+) diff --git a/src/org/mozilla/javascript/NativeBoolean.java b/src/org/mozilla/javascript/NativeBoolean.java index 7a6f4ea9c2..0a3df6763a 100644 --- a/src/org/mozilla/javascript/NativeBoolean.java +++ b/src/org/mozilla/javascript/NativeBoolean.java @@ -40,6 +40,9 @@ package org.mozilla.javascript; +import static org.mozilla.javascript.TopLevel.getBuiltinPrototype; +import org.mozilla.javascript.TopLevel.Builtins; + /** * This class implements the Boolean native object. * See ECMA 15.6. @@ -144,6 +147,32 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, throw new IllegalArgumentException(String.valueOf(id)); } + /** + * Special [[Get]] if reference base value is primitive, see ES5.1 [8.7.1]: + * - Support for the Boolean type + * @see ScriptRuntime#getPrimitiveValue(Object, Scriptable, String, int, Context, Scriptable) + */ + static Object getPrimitiveValue(Boolean base, String property, int index, + Context cx, Scriptable scope) { + Object value = NOT_FOUND; + // search in boolean prototype + NativeBoolean booleanProto = getBooleanPrototype(scope); + value = booleanProto.getSlotOrProtoValue(property, index, base, cx, scope); + if (value != NOT_FOUND) return value; + // search in object prototype + NativeObject objectProto = (NativeObject)booleanProto.getPrototype(); + value = objectProto.getSlotOrProtoValue(property, index, base, cx, scope); + // object prototype has no other prototype + assert objectProto.getPrototype() == null; + return value; + } + + private static NativeBoolean getBooleanPrototype(Scriptable scope) { + Scriptable proto = getBuiltinPrototype(getTopLevelScope(scope), Builtins.Boolean); + assert proto instanceof NativeBoolean; + return (NativeBoolean) proto; + } + // #string_id_map# @Override diff --git a/src/org/mozilla/javascript/NativeNumber.java b/src/org/mozilla/javascript/NativeNumber.java index 53ec4c03d4..aeac43c224 100644 --- a/src/org/mozilla/javascript/NativeNumber.java +++ b/src/org/mozilla/javascript/NativeNumber.java @@ -40,6 +40,9 @@ package org.mozilla.javascript; +import static org.mozilla.javascript.TopLevel.getBuiltinPrototype; +import org.mozilla.javascript.TopLevel.Builtins; + /** * This class implements the Number native object. * @@ -239,6 +242,32 @@ private static String num_to(double val, return sb.toString(); } + /** + * Special [[Get]] if reference base value is primitive, see ES5.1 [8.7.1]: + * - Support for the Number type + * @see ScriptRuntime#getPrimitiveValue(Object, Scriptable, String, int, Context, Scriptable) + */ + static Object getPrimitiveValue(Number base, String property, int index, + Context cx, Scriptable scope) { + Object value = NOT_FOUND; + // search in number prototype + NativeNumber numberProto = getNumberPrototype(scope); + value = numberProto.getSlotOrProtoValue(property, index, base, cx, scope); + if (value != NOT_FOUND) return value; + // search in object prototype + NativeObject objectProto = (NativeObject)numberProto.getPrototype(); + value = objectProto.getSlotOrProtoValue(property, index, base, cx, scope); + // object prototype has no other prototype + assert objectProto.getPrototype() == null; + return value; + } + + private static NativeNumber getNumberPrototype(Scriptable scope) { + Scriptable proto = getBuiltinPrototype(getTopLevelScope(scope), Builtins.Number); + assert proto instanceof NativeNumber; + return (NativeNumber) proto; + } + // #string_id_map# @Override diff --git a/src/org/mozilla/javascript/NativeString.java b/src/org/mozilla/javascript/NativeString.java index c160edc0e4..0050f974b8 100644 --- a/src/org/mozilla/javascript/NativeString.java +++ b/src/org/mozilla/javascript/NativeString.java @@ -41,6 +41,9 @@ package org.mozilla.javascript; +import static org.mozilla.javascript.TopLevel.getBuiltinPrototype; +import static org.mozilla.javascript.TopLevel.Builtins; + import java.text.Collator; import java.util.Arrays; import java.util.LinkedHashSet; @@ -770,6 +773,40 @@ private static CharSequence js_slice(CharSequence target, Object[] args) { return target.subSequence((int) begin, (int) end); } + /** + * Special [[Get]] if reference base value is primitive, see ES5.1 [8.7.1]: + * - Support for the String type + * @see ScriptRuntime#getPrimitiveValue(Object, Scriptable, String, int, Context, Scriptable) + */ + static Object getPrimitiveValue(CharSequence base, String property, int index, + Context cx, Scriptable scope) { + // search in instance properties + if (property == null) { + if (0 <= index && index < base.length()) { + return String.valueOf(base.charAt(index)); + } + } else if ("length".equals(property)) { + return base.length(); + } + Object value = NOT_FOUND; + // search in string prototype + NativeString stringProto = getStringPrototype(scope); + value = stringProto.getSlotOrProtoValue(property, index, base, cx, scope); + if (value != NOT_FOUND) return value; + // search in object prototype + NativeObject objectProto = (NativeObject)stringProto.getPrototype(); + value = objectProto.getSlotOrProtoValue(property, index, base, cx, scope); + // object prototype has no other prototype + assert objectProto.getPrototype() == null; + return value; + } + + private static NativeString getStringPrototype(Scriptable scope) { + Scriptable proto = getBuiltinPrototype(getTopLevelScope(scope), Builtins.String); + assert proto instanceof NativeString; + return (NativeString) proto; + } + // #string_id_map# @Override From 4f5727e3cbd465a844146d018fcb7ab16df18d2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 079/111] Final change to use optimized [[Get]] for primitives --- src/org/mozilla/javascript/ScriptRuntime.java | 70 +++++++++++-------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index d2717987ca..2ca27b458f 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -1473,6 +1473,16 @@ static String toStringIdOrIndex(Context cx, Object id) } } + /** + * 8.7 The Reference Specification Type: HasPrimitiveBase(V)
+ * Returns true if the base value is a Boolean, String, or Number. + */ + private static boolean hasPrimitiveBase(Object val) { + return val instanceof CharSequence + || val instanceof Number + || val instanceof Boolean; + } + /** * 8.7 The Reference Specification Type: HasPrimitiveBase(V)
* Returns true if the base value is a Boolean, String, or Number. @@ -1509,27 +1519,29 @@ private static Scriptable tryWrap(Context cx, Scriptable scope, Object val) { /** * Special [[Get]] if reference base value is primitive, see ES5.1 [8.7.1] */ - private static Object getPrimitiveValue(Object base, Scriptable obj, + private static Object getPrimitiveValue(Object base, String property, int index, Context cx, Scriptable scope) { - // TODO: could need some optimization - assert obj instanceof NativeString - || obj instanceof NativeNumber - || obj instanceof NativeBoolean; - if (property == null) { property = toString(index); } - ScriptableObject sobj = (ScriptableObject) obj; - PropertyDescriptor desc = sobj.$getProperty(property); - if (desc == null) { - return Undefined.instance; - } else if (desc.isDataDescriptor()) { - return desc.getValue(); + assert base instanceof CharSequence + || base instanceof Number + || base instanceof Boolean; + Object value; + if (base instanceof CharSequence) { + value = NativeString.getPrimitiveValue((CharSequence)base, + property, index, cx, scope); + } else if (base instanceof Number) { + value = NativeNumber.getPrimitiveValue((Number)base, + property, index, cx, scope); + } else if (base instanceof Boolean) { + value = NativeBoolean.getPrimitiveValue((Boolean)base, + property, index, cx, scope); + } else { + throw Kit.codeBug(); } - Object getter = desc.getGetter(); - if (getter == Undefined.instance) { + if (value == ScriptableObject.NOT_FOUND) { return Undefined.instance; - } else { - return ((Callable) getter).call(cx, scope, base, emptyArgs); } + return value; } /** @@ -1592,13 +1604,13 @@ public static Object getObjectValue(Object obj, Object elem, boolean strict, throw undefReadError(obj, elem); } else if (obj instanceof Scriptable) { sobj = (Scriptable) obj; - } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { + } else if (hasPrimitiveBase(obj)) { String s = toStringIdOrIndex(cx, elem); if (s != null) { - return getPrimitiveValue(obj, sobj, s, -1, cx, scope); + return getPrimitiveValue(obj, s, -1, cx, scope); } else { int index = lastIndexResult(cx); - return getPrimitiveValue(obj, sobj, null, index, cx, scope); + return getPrimitiveValue(obj, null, index, cx, scope); } } else if ((sobj = tryWrap(cx, scope, obj)) == null) { throw errorWithClassName("msg.invalid.type", obj); @@ -1626,9 +1638,9 @@ public static Object getObjectValue(Object obj, String property, boolean strict, throw undefReadError(obj, property); } else if (obj instanceof Scriptable) { sobj = (Scriptable) obj; - } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { + } else if (hasPrimitiveBase(obj)) { assert property != null; - return getPrimitiveValue(obj, sobj, property, -1, cx, scope); + return getPrimitiveValue(obj, property, -1, cx, scope); } else if ((sobj = tryWrap(cx, scope, obj)) == null) { throw errorWithClassName("msg.invalid.type", obj); } @@ -1655,13 +1667,13 @@ public static Object getObjectValue(Object obj, double dblIndex, boolean strict, throw undefReadError(obj, toString(dblIndex)); } else if (obj instanceof Scriptable) { sobj = (Scriptable) obj; - } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { + } else if (hasPrimitiveBase(obj)) { int index = (int) dblIndex; if (index == dblIndex) { - return getPrimitiveValue(obj, sobj, null, index, cx, scope); + return getPrimitiveValue(obj, null, index, cx, scope); } else { String s = toString(dblIndex); - return getPrimitiveValue(obj, sobj, s, -1, cx, scope); + return getPrimitiveValue(obj, s, -1, cx, scope); } } else if ((sobj = tryWrap(cx, scope, obj)) == null) { throw errorWithClassName("msg.invalid.type", obj); @@ -2762,10 +2774,11 @@ private static Object getObjectAndThisHelper(Object obj, String property, } else { value = ScriptableObject.getProperty(sobj, property); } - } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) { + } else if (hasPrimitiveBase(obj)) { // follow spidermonkey (possible bug?) tryNoSuchMethod = false; - value = getPrimitiveValue(obj, sobj, property, index, cx, scope); + sobj = null; + value = getPrimitiveValue(obj, property, index, cx, scope); } else if ((sobj = tryWrap(cx, scope, obj)) != null) { if (property == null) { value = ScriptableObject.getProperty(sobj, index); @@ -2778,6 +2791,7 @@ private static Object getObjectAndThisHelper(Object obj, String property, boolean valueCallable = value instanceof Callable; if (tryNoSuchMethod && !valueCallable) { + assert sobj != null; Object noSuchMethod = ScriptableObject.getProperty(sobj, "__noSuchMethod__"); if (noSuchMethod instanceof Callable) { if (property == null) { property = toString(index); } @@ -2789,7 +2803,7 @@ private static Object getObjectAndThisHelper(Object obj, String property, if (!valueCallable) { if (property == null) { property = toString(index); } storeThis(cx, BAD_SCRIPTABLE); - return notFunctionError(sobj, value, property); + return notFunctionError(obj, value, property); } storeThis(cx, sobj); @@ -3213,7 +3227,7 @@ public static Object propIncrDecr(Object obj, String id, boolean strict, throw undefReadError(obj, id); } else if (obj instanceof Scriptable) { start = (Scriptable) obj; - } else if ((start = tryPrimitive(cx, scope, obj)) != null) { + } else if (hasPrimitiveBase(obj)) { // use elemIncDecr() for primitives return elemIncrDecr(obj, id, strict, cx, scope, incrDecrMask); } else if ((start = tryWrap(cx, scope, obj)) == null) { From 37fdb86e9e224f87bf3acfdd75ff9b2a3392dfd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 080/111] Add missing error messages; use reportError() instead of addError() for strict-mode errors; Check for duplicate parameters in destructuring --- src/org/mozilla/javascript/Parser.java | 74 +++++++++++++------ .../javascript/resources/Messages.properties | 6 ++ 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index 9c1210c851..45ef077ea9 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -57,6 +57,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -719,13 +720,13 @@ private void setStrictMode(ScriptNode script) { // "use strict"; // retroactively makes "\145" a syntax error // } if (ts.hasOctalCharacterEscape()) { - addError("msg.no.octal.strict"); + reportError("msg.no.octal.strict"); } inUseStrictDirective = true; script.setInStrictMode(true); } - private void parseFunctionParams(FunctionNode fnNode) + private void parseFunctionParams(FunctionNode fnNode) throws IOException { if (matchToken(Token.RP)) { @@ -789,8 +790,7 @@ private FunctionNode function(int type) // We forbid function statements in strict mode code. if (type == FunctionNode.FUNCTION_EXPRESSION_STATEMENT && inUseStrictDirective) { - // TODO: "in strict mode code, functions may be declared only at top level or immediately within another function" - // addError(); + reportError("msg.strict.function.stmt"); } if (matchToken(Token.NAME)) { @@ -806,8 +806,7 @@ private FunctionNode function(int type) } else if (matchToken(Token.LP)) { // Anonymous function: leave name as null if (type != FunctionNode.FUNCTION_EXPRESSION) { - // TODO: Unnamed function expressions are forbidden in statement context. - // addError("function statement requires a name"); + reportError("msg.unnamed.function.stmt"); } } else { if (compilerEnv.isAllowMemberExprAsFunctionName()) { @@ -904,7 +903,7 @@ private void checkStrictFunction(FunctionNode fnNode) { "arguments".equals(id) || TokenStream.isKeyword(id)) { - addError("msg.bad.id.strict", id); + reportError("msg.bad.id.strict", id); } } @@ -912,30 +911,59 @@ private void checkStrictFunction(FunctionNode fnNode) { Set paramNames = new HashSet(); for (AstNode param : fnNode.getParams()) { if (param instanceof Name) { - String paramName = param.getString(); - if ("eval".equals(paramName) || - "arguments".equals(paramName) || - TokenStream.isKeyword(paramName)) - { - addError("msg.bad.id.strict", paramName); - } - if (paramNames.contains(paramName)) { - addError("msg.dup.param.strict", paramName); - } - paramNames.add(paramName); + checkStrictFunctionParam(paramNames, param); } else if (hasDestruct && param instanceof DestructuringForm) { - // TODO: check duplicate arguments in destructuring params + List workQueue = new LinkedList(); + workQueue.add(param); + while (!workQueue.isEmpty()) { + AstNode node = workQueue.remove(0); + assert ((DestructuringForm)node).isDestructuring(); + if (node instanceof ObjectLiteral) { + for (ObjectProperty p : ((ObjectLiteral)node).getElements()) { + AstNode right = p.getRight(); + if (right instanceof Name) { + checkStrictFunctionParam(paramNames, right); + } else if (right instanceof DestructuringForm) { + workQueue.add(right); + } + } + } else { + assert (node instanceof ArrayLiteral); + for (AstNode p : ((ArrayLiteral)node).getElements()) { + if (p instanceof Name) { + checkStrictFunctionParam(paramNames, p); + } else if (p instanceof DestructuringForm) { + workQueue.add(p); + } + } + } + } } } } + private void checkStrictFunctionParam(Set paramNames, AstNode param) { + assert (param instanceof Name); + String paramName = param.getString(); + if ("eval".equals(paramName) || + "arguments".equals(paramName) || + TokenStream.isKeyword(paramName)) + { + reportError("msg.bad.id.strict", paramName); + } + if (paramNames.contains(paramName)) { + reportError("msg.dup.param.strict", paramName); + } + paramNames.add(paramName); + } + private boolean checkStrictAssignment(AstNode node) { node = removeParens(node); if (node instanceof Name) { String name = node.getString(); // strict mode doesn't allow eval/arguments for lhs if ("eval".equals(name) || "arguments".equals(name)) { - addError("msg.bad.id.strict", name); + reportError("msg.bad.id.strict", name); return false; } } @@ -945,7 +973,7 @@ private boolean checkStrictAssignment(AstNode node) { private boolean checkStrictDelete(UnaryExpression expr) { AstNode op = removeParens(expr.getOperand()); if (op instanceof Name) { - addError("msg.bad.id.strict", op.getString()); + reportError("msg.bad.id.strict", op.getString()); return false; } return true; @@ -1969,7 +1997,7 @@ private VariableDeclaration variables(int declType, int pos, boolean isStatement "arguments".equals(ts.getString()) || TokenStream.isKeyword(id)) { - addError("msg.bad.id.strict", id); + reportError("msg.bad.id.strict", id); } } defineSymbol(declType, ts.getString(), inForInit); @@ -3339,7 +3367,7 @@ private ObjectLiteral objectLiteral() if ((old & propertyType) != 0 && (old != VALUE || propertyType != VALUE || this.inUseStrictDirective)) { - addError("msg.dup.obj.lit.prop.strict", propertyName); + reportError("msg.dup.obj.lit.prop.strict", propertyName); } propertyNames.put(propertyName, propertyType | old); } else { diff --git a/src/org/mozilla/javascript/resources/Messages.properties b/src/org/mozilla/javascript/resources/Messages.properties index afe02e34cd..82abe8257c 100644 --- a/src/org/mozilla/javascript/resources/Messages.properties +++ b/src/org/mozilla/javascript/resources/Messages.properties @@ -543,6 +543,12 @@ msg.dup.param.strict =\ msg.bad.id.strict =\ "{0}" is not a valid identifier for this use in strict mode. +msg.strict.function.stmt =\ + "SyntaxError: in strict mode code, functions may be declared only at top level or immediately within another function" + +msg.unnamed.function.stmt =\ + "SyntaxError: function statement requires a name" + # ScriptRuntime # is there a better message for this? From fac37c41f79363e8c0bb287dac6672e33fb16735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 081/111] Fix bugs in octal escape sequences for regular expressions and make try to follow 'web reality' resp. Spidermonkey more closely --- .../javascript/regexp/NativeRegExp.java | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/org/mozilla/javascript/regexp/NativeRegExp.java b/src/org/mozilla/javascript/regexp/NativeRegExp.java index ef84842bf0..df3d0a5dc3 100644 --- a/src/org/mozilla/javascript/regexp/NativeRegExp.java +++ b/src/org/mozilla/javascript/regexp/NativeRegExp.java @@ -739,9 +739,9 @@ private static void doFlat(CompilerState state, char c) break; } if (!overflow) { - int digit = c - '0'; - if (value < (maxValue - digit) / 10) { - value = value * 10 + digit; + int v = value * 10 + (c - '0'); + if (v < maxValue) { + value = v; } else { overflow = true; value = maxValue; @@ -792,23 +792,19 @@ private static void doFlat(CompilerState state, char c) /* Decimal escape */ case '0': /* - * Under 'strict' ECMA 3, we interpret \0 as NUL and don't accept octal. - * However, (XXX and since Rhino doesn't have a 'strict' mode) we'll just - * behave the old way for compatibility reasons. - * (see http://bugzilla.mozilla.org/show_bug.cgi?id=141078) - * + * We're deliberately violating the ECMA 5.1 specification and allow octal + * escapes to follow spidermonkey and general 'web reality': + * http://wiki.ecmascript.org/doku.php?id=harmony:regexp_match_web_reality + * http://wiki.ecmascript.org/doku.php?id=strawman:match_web_reality_spec */ reportWarning(state.cx, "msg.bad.backref", ""); /* octal escape */ num = 0; - while (state.cp < state.cpend) { + for (int i = 0; i < 2 && state.cp < state.cpend; ++i) { c = src[state.cp]; if ((c >= '0') && (c <= '7')) { state.cp++; - tmp = 8 * num + (c - '0'); - if (tmp > 0377) - break; - num = tmp; + num = 8 * num + (c - '0'); } else break; @@ -831,19 +827,27 @@ private static void doFlat(CompilerState state, char c) if (num > state.parenCount) reportWarning(state.cx, "msg.bad.backref", ""); /* - * n > 9 or > count of parentheses, - * then treat as octal instead. + * n > count of parentheses, then treat as octal instead. + * Also see note above concerning 'web reality' */ - if ((num > 9) && (num > state.parenCount)) { + if (num > state.parenCount) { state.cp = termStart; - num = 0; - while (state.cp < state.cpend) { + if (c > '7') { + // invalid octal escape, follow spidermonkey and + // treat as \\8 resp. \\9 + c = '\\'; + doFlat(state, c); + break; + } + state.cp++; + num = c - '0'; + for (int i = 0; i < 2 && state.cp < state.cpend; ++i) { c = src[state.cp]; if ((c >= '0') && (c <= '7')) { - state.cp++; tmp = 8 * num + (c - '0'); if (tmp > 0377) break; + state.cp++; num = tmp; } else From 381a22c2ee51a984c6ac4888c0db73210b7c2dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 082/111] Add missing check for invalid 'arguments' as lhs in assignment (strict-mode) --- src/org/mozilla/javascript/Parser.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index 45ef077ea9..b720f066dd 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -3919,15 +3919,16 @@ protected Scope createScopeNode(int token, int lineno) { protected Node simpleAssignment(Node left, Node right) { int nodeType = left.getType(); switch (nodeType) { - case Token.NAME: + case Token.NAME: { + String name = left.getString(); if (inUseStrictDirective && - "eval".equals(((Name) left).getIdentifier())) + ("eval".equals(name) || "arguments".equals(name))) { - reportError("msg.bad.id.strict", - ((Name) left).getIdentifier()); + reportError("msg.bad.id.strict", name); } left.setType(Token.BINDNAME); return new Node(Token.SETNAME, left, right); + } case Token.GETPROP: case Token.GETELEM: { From f4417264e8cc7310f515a7b0f56783d91f2b3840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 083/111] Fix bugs in octal escape sequences for regular expressions and make try to follow 'web reality' resp. Spidermonkey more closely (step 2) --- .../javascript/regexp/NativeRegExp.java | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/org/mozilla/javascript/regexp/NativeRegExp.java b/src/org/mozilla/javascript/regexp/NativeRegExp.java index df3d0a5dc3..90cda35d71 100644 --- a/src/org/mozilla/javascript/regexp/NativeRegExp.java +++ b/src/org/mozilla/javascript/regexp/NativeRegExp.java @@ -316,10 +316,19 @@ static Object compileRE(Context cx, String str, String global, boolean flat) state.result.length = length; state.result.flatIndex = 0; state.progLength += 5; - } - else + } else { if (!parseDisjunction(state)) return null; + // Need to reparse if pattern contains invalid backreferences: + // "Note: if the number of left parentheses is less than the number + // specified in \#, the \# is taken as an octal escape" + if (state.maxBackReference > state.parenCount) { + state = new CompilerState(cx, regexp.source, length, flags); + state.backReferenceLimit = state.parenCount; + if (!parseDisjunction(state)) + return null; + } + } regexp.program = new byte[state.progLength + 1]; if (state.classCount != 0) { @@ -800,7 +809,9 @@ private static void doFlat(CompilerState state, char c) reportWarning(state.cx, "msg.bad.backref", ""); /* octal escape */ num = 0; - for (int i = 0; i < 2 && state.cp < state.cpend; ++i) { + // follow spidermonkey and allow multiple leading zeros, + // e.g. let /\0000/ match the string "\0" + while (num < 040 && state.cp < state.cpend) { c = src[state.cp]; if ((c >= '0') && (c <= '7')) { state.cp++; @@ -824,15 +835,15 @@ private static void doFlat(CompilerState state, char c) termStart = state.cp - 1; num = getDecimalValue(c, state, 0xFFFF, "msg.overlarge.backref"); - if (num > state.parenCount) + if (num > state.backReferenceLimit) reportWarning(state.cx, "msg.bad.backref", ""); /* * n > count of parentheses, then treat as octal instead. * Also see note above concerning 'web reality' */ - if (num > state.parenCount) { + if (num > state.backReferenceLimit) { state.cp = termStart; - if (c > '7') { + if (c >= '8') { // invalid octal escape, follow spidermonkey and // treat as \\8 resp. \\9 c = '\\'; @@ -841,14 +852,11 @@ private static void doFlat(CompilerState state, char c) } state.cp++; num = c - '0'; - for (int i = 0; i < 2 && state.cp < state.cpend; ++i) { + while (num < 040 && state.cp < state.cpend) { c = src[state.cp]; if ((c >= '0') && (c <= '7')) { - tmp = 8 * num + (c - '0'); - if (tmp > 0377) - break; state.cp++; - num = tmp; + num = 8 * num + (c - '0'); } else break; @@ -861,6 +869,9 @@ private static void doFlat(CompilerState state, char c) state.result = new RENode(REOP_BACKREF); state.result.parenIndex = num - 1; state.progLength += 3; + if (state.maxBackReference < num) { + state.maxBackReference = num; + } break; /* Control escape */ case 'f': @@ -2689,6 +2700,8 @@ class CompilerState { this.cp = 0; this.cpend = length; this.flags = flags; + this.backReferenceLimit = Integer.MAX_VALUE; + this.maxBackReference = 0; this.parenCount = 0; this.classCount = 0; this.progLength = 0; @@ -2699,6 +2712,8 @@ class CompilerState { int cpend; int cp; int flags; + int backReferenceLimit; + int maxBackReference; int parenCount; int parenNesting; int classCount; /* number of [] encountered */ From 86a2751c2db7a1d34c968d9941a39c0b07d04ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 084/111] Make direct eval detection spec-compliant --- src/org/mozilla/javascript/Interpreter.java | 1 - src/org/mozilla/javascript/ScriptRuntime.java | 28 +++++++++++-------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index 579f3ec2b8..6b0ef71128 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -1602,7 +1602,6 @@ private static Object interpretLoop(Context cx, CallFrame frame, // stack change: function thisObj arg0 .. argN -> result stackTop -= 1 + indexReg; - // FIXME: here! // Call code generation ensure that stack here // is ... Callable Scriptable Object functionThis = stack[stackTop + 1]; diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 2ca27b458f..106002fdfd 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -2894,18 +2894,22 @@ public static Object callSpecial(Context cx, Callable fun, boolean strictMode) { if (callType == Node.SPECIALCALL_EVAL) { - assert thisObj == Undefined.instance || thisObj instanceof Scriptable; - // FIXME: This really needs be to checked with spec! - if (thisObj == Undefined.instance) { + // Direct Eval [ES5.1 - 15.1.2.1.1] needs to meet two conditions: + // 1) reference base value is an environment record and reference + // name is "eval" + // 2) reference value is the built-in eval function + // Code generation takes care of the first condition, so we only + // need to test for the second one + assert thisObj == Undefined.instance + || (thisObj instanceof Scriptable + && ScriptableObject.hasProperty((Scriptable)thisObj, + "eval")); + if (NativeGlobal.isEvalFunction(fun)) { + // Entering (Direct) Eval Code [ES5.1 - 10.4.2 step 2]: + // Direct eval uses the ThisBinding, LexicalEnvironment and + // VariableEnvironment of the calling execution context return evalSpecial(cx, scope, callerThis, args, filename, lineNumber, strictMode); - } else { - if (! (thisObj instanceof Scriptable)) Kit.codeBug(); - if (((Scriptable) thisObj).getParentScope() == null - && NativeGlobal.isEvalFunction(fun)) { - return evalSpecial(cx, scope, callerThis, args, - filename, lineNumber, strictMode); - } } } else if (callType == Node.SPECIALCALL_WITH) { if (NativeWith.isWithFunction(fun)) { @@ -3061,9 +3065,11 @@ public static Object evalSpecial(Context cx, Scriptable scope, evaluator.setEvalScriptFlag(script); if (script instanceof InterpretedFunction) { // TODO: extend Script interface to make this less voodoo - strictMode |= ((InterpretedFunction) script).idata.isStrict; + strictMode |= ((InterpretedFunction) script).isStrict(); } if (strictMode) { + // Entering Eval Code [ES5.1 - 10.4.2 step 3]: + // strict eval code uses a new Lexical- and VariableEnvironment NativeObject newScope = new NativeObject(); newScope.setParentScope(scope); scope = newScope; From bfb33d533a174162c3d5216e6fadcafc8ce3a38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 085/111] Functions created through the built-in Function constructor do not inherit strict mode from the environment --- src/org/mozilla/javascript/BaseFunction.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/org/mozilla/javascript/BaseFunction.java b/src/org/mozilla/javascript/BaseFunction.java index 88448cc725..cebe06fcd4 100644 --- a/src/org/mozilla/javascript/BaseFunction.java +++ b/src/org/mozilla/javascript/BaseFunction.java @@ -591,15 +591,16 @@ private static Object jsConstructor(Context cx, Scriptable scope, ErrorReporter reporter; reporter = DefaultErrorReporter.forEval(cx.getErrorReporter()); + // Compile with explicit interpreter instance to force interpreter + // mode. Evaluator evaluator = Context.createInterpreter(); if (evaluator == null) { throw new JavaScriptException("Interpreter not present", filename, linep[0]); } - // Compile with explicit interpreter instance to force interpreter - // mode. - // TODO: inherit strictMode from environment + // Functions created through the built-in Function constructor do not + // inherit strict mode from environment [ES5.1, 10.1.1 Strict Mode Code] boolean strictMode = false; return cx.compileFunction(global, source, evaluator, reporter, sourceURI, 1, null, strictMode); From 363a0fa3a726882bd1adc023b419972ef936e79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 086/111] 'new Object' also performs ToObject conversion if called with a non-null/non-undefined value, see 15.2.2.1 --- src/org/mozilla/javascript/NativeObject.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index e0890d565b..e65f597917 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -148,13 +148,13 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, int id = f.methodId(); switch (id) { case Id_constructor: { - if (thisObj != null) { - // BaseFunction.construct will set up parent, proto - return f.construct(cx, scope, args); - } if (args.length == 0 || args[0] == null || args[0] == Undefined.instance) { + if (thisObj != null) { + // BaseFunction.construct will set up parent, proto + return f.construct(cx, scope, args); + } return new NativeObject(); } return ScriptRuntime.toObject(cx, scope, args[0]); From 78f3296779d593ad04ee6f963396bbccd7318bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 087/111] Don't create array object for Array.prototype.{every,some,forEach} --- src/org/mozilla/javascript/NativeArray.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index 34adf4ab02..4a863df353 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -1820,8 +1820,12 @@ private static Object iterativeMethod(Context cx, int id, Scriptable scope, Function f = (Function) callbackArg; Scriptable parent = ScriptableObject.getTopLevelScope(f); Object thisArg = args.length < 2 ? Undefined.instance : args[1]; - int resultLength = id == Id_map ? (int) length : 0; - Scriptable array = id != Id_every ? cx.newArray(scope, resultLength) : null; + Scriptable array; + if (id == Id_filter || id == Id_map) { + array = cx.newArray(scope, id == Id_map ? (int) length : 0); + } else { + array = null; + } long j=0; for (long i=0; i < length; i++) { Object[] innerArgs = new Object[3]; From 3a56086538615143a7a6015f2b9a0973a415a4ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 088/111] Make ScriptRuntime.init{Script,Function}() match 10.5 [Declaration Binding Instantiation] more closely --- src/org/mozilla/javascript/Interpreter.java | 10 ++- src/org/mozilla/javascript/ScriptRuntime.java | 70 ++++++++++++++----- .../mozilla/javascript/optimizer/Codegen.java | 17 +++-- .../javascript/optimizer/OptRuntime.java | 5 +- 4 files changed, 74 insertions(+), 28 deletions(-) diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index 6b0ef71128..b162fb870b 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -845,7 +845,8 @@ private static void initFunction(Context cx, Scriptable scope, InterpretedFunction fn; fn = InterpretedFunction.createFunction(cx, scope, parent, index); ScriptRuntime.initFunction(cx, scope, fn, fn.idata.itsFunctionType, - parent.idata.evalScriptFlag); + parent.idata.evalScriptFlag, + parent.isStrict()); } static Object interpret(InterpretedFunction ifun, @@ -2741,8 +2742,6 @@ private static void initFrame(Context cx, Scriptable callerScope, } } else { scope = callerScope; - ScriptRuntime.initScript(fnOrScript, thisObj, cx, scope, - fnOrScript.idata.evalScriptFlag); } if (idata.itsNestedFunctions != null) { @@ -2756,6 +2755,11 @@ private static void initFrame(Context cx, Scriptable callerScope, } } + if (idata.itsFunctionType == 0) { + ScriptRuntime.initScript(fnOrScript, thisObj, cx, scope, + fnOrScript.idata.evalScriptFlag); + } + Scriptable[] scriptRegExps = null; if (idata.itsRegExpLiterals != null) { // Wrapped regexps for functions are stored in diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 106002fdfd..7cbd40aef7 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -3787,6 +3787,12 @@ public static void addInstructionCount(Context cx, int instructionsToAdd) } } + /** + * ES5.1: 10.5 Declaration Binding Instantiation, step 8
+ * + * Create bindings for all variable declarations in {@code funObj} and + * initializes them to {@link Undefined.instance}. + */ public static void initScript(NativeFunction funObj, Object thisObj, Context cx, Scriptable scope, boolean evalScript) @@ -3804,8 +3810,11 @@ public static void initScript(NativeFunction funObj, Object thisObj, varScope = varScope.getParentScope(); } - // TODO: strict mode flag? - boolean strict = false; + // strict mode flag is always true, cf. + // 10.5 Declaration Binding Instantiation, step 8 + // -> 10.2.1.1.2 CreateMutableBinding (N, D) + // -> 10.2.1.2.2 CreateMutableBinding (N, D) + boolean strict = true; for (int i = varCount; i-- != 0;) { String name = funObj.getParamOrVarName(i); boolean isConst = funObj.getParamOrVarConst(i); @@ -4058,37 +4067,64 @@ public static void setBuiltinProtoAndParent(ScriptableObject object, object.setPrototype(TopLevel.getBuiltinPrototype(scope, type)); } + @Deprecated + public static void initFunction(Context cx, Scriptable scope, + NativeFunction function, int type, + boolean fromEvalCode) + { + initFunction(cx, scope, function, type, fromEvalCode, false); + } + /** + * ES5.1: 10.5 Declaration Binding Instantiation, step 5
+ * + * Create bindings for all function declarations in {@code funObj} and + * initializes them accordingly. + */ public static void initFunction(Context cx, Scriptable scope, NativeFunction function, int type, - boolean fromEvalCode) + boolean fromEvalCode, boolean strict) { - // TODO: strict mode flag? - boolean strict = false; if (type == FunctionNode.FUNCTION_STATEMENT) { String name = function.getFunctionName(); - if (name != null && name.length() != 0) { + assert name != null && name.length() > 0; + if (!ScriptableObject.hasProperty(scope, name)) { + // 10.5, step 5d: CreateMutableBinding if (!fromEvalCode) { // ECMA specifies that functions defined in global and // function scope outside eval should have DONTDELETE set. - ScriptableObject.defineProperty(scope, name, function, + ScriptableObject.defineProperty(scope, name, Undefined.instance, ScriptableObject.PERMANENT, - strict); + true); } else { - scope.put(name, scope, function, strict); + scope.put(name, scope, Undefined.instance, true); + } + } else if (scope.getParentScope() == null) { + // 10.5, step 5e: env is the environment record component of the global environment + ScriptableObject global = ScriptableObject.ensureScriptableObject(scope); + PropertyDescriptor desc = global.$getProperty(name); + if (desc.isConfigurable()) { + int attrs = fromEvalCode ? ScriptableObject.EMPTY : ScriptableObject.PERMANENT; + desc = new PropertyDescriptor(Undefined.instance, attrs); + global.defineOwnProperty(name, desc, true); + } else if (desc.isAccessorDescriptor() || !desc.isEnumerable() + || !desc.isWritable()) { + // TODO: error message + throw typeError("[[Bad Function Binding]]"); } } + // 10.5, step 5f: SetMutableBinding + scope.put(name, scope, function, strict); } else if (type == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { String name = function.getFunctionName(); - if (name != null && name.length() != 0) { - // Always put function expression statements into initial - // activation object ignoring the with statement to follow - // SpiderMonkey - while (scope instanceof NativeWith) { - scope = scope.getParentScope(); - } - scope.put(name, scope, function, strict); + assert name != null && name.length() > 0; + // Always put function expression statements into initial + // activation object ignoring the with statement to follow + // SpiderMonkey + while (scope instanceof NativeWith) { + scope = scope.getParentScope(); } + scope.put(name, scope, function, strict); } else { throw Kit.codeBug(); } diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index 9cbc1033ba..f600bfe48b 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -1764,6 +1764,15 @@ private void generatePrologue() +")V"); } else { debugVariableName = "global"; + } + + enterAreaStartLabel = cfw.acquireLabel(); + epilogueLabel = cfw.acquireLabel(); + cfw.markLabel(enterAreaStartLabel); + + generateNestedFunctionInits(); + + if (fnCurrent == null) { cfw.addALoad(funObjLocal); cfw.addALoad(thisObjLocal); cfw.addALoad(contextLocal); @@ -1778,12 +1787,6 @@ private void generatePrologue() +")V"); } - enterAreaStartLabel = cfw.acquireLabel(); - epilogueLabel = cfw.acquireLabel(); - cfw.markLabel(enterAreaStartLabel); - - generateNestedFunctionInits(); - // default is to generate debug info if (compilerEnv.isGenerateDebugInfo()) { cfw.addVariableDescriptor(debugVariableName, @@ -3098,11 +3101,13 @@ private void visitFunction(OptFunctionNode ofn, int functionType) cfw.addPush(functionType); cfw.addALoad(variableObjectLocal); cfw.addALoad(contextLocal); // load 'cx' + cfw.addPush(scriptOrFn.isInStrictMode()); addOptRuntimeInvoke("initFunction", "(Lorg/mozilla/javascript/NativeFunction;" +"I" +"Lorg/mozilla/javascript/Scriptable;" +"Lorg/mozilla/javascript/Context;" + +"Z" +")V"); } diff --git a/src/org/mozilla/javascript/optimizer/OptRuntime.java b/src/org/mozilla/javascript/optimizer/OptRuntime.java index 6098fb12fa..ac6cb17619 100644 --- a/src/org/mozilla/javascript/optimizer/OptRuntime.java +++ b/src/org/mozilla/javascript/optimizer/OptRuntime.java @@ -173,9 +173,10 @@ public static Object[] padStart(Object[] currentArgs, int count) { } public static void initFunction(NativeFunction fn, int functionType, - Scriptable scope, Context cx) + Scriptable scope, Context cx, + boolean strict) { - ScriptRuntime.initFunction(cx, scope, fn, functionType, false); + ScriptRuntime.initFunction(cx, scope, fn, functionType, false, strict); } public static Object callSpecial(Context cx, Object fun, From aa33ccf0e333e53f287fa3d13eae05c3bba9d7d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 089/111] Add internal method to circumvent [[Extensible]] check when setting lazily instantiated prototype for built-in functions --- src/org/mozilla/javascript/IdFunctionObject.java | 2 +- src/org/mozilla/javascript/ScriptableObject.java | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/org/mozilla/javascript/IdFunctionObject.java b/src/org/mozilla/javascript/IdFunctionObject.java index 29b6600556..f85dd9cc2a 100644 --- a/src/org/mozilla/javascript/IdFunctionObject.java +++ b/src/org/mozilla/javascript/IdFunctionObject.java @@ -117,7 +117,7 @@ public Scriptable getPrototype() Scriptable proto = super.getPrototype(); if (proto == null) { proto = getFunctionPrototype(getParentScope()); - setPrototype(proto); + setPrototype(proto, false); } return proto; } diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 550a6aabd2..1a24fbbf34 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -911,7 +911,21 @@ public Scriptable getPrototype() */ public void setPrototype(Scriptable m) { - if (!isExtensible()) throw ScriptRuntime.typeError0("msg.not.extensible"); + setPrototype(m, true); + } + + /** + * package-private interface to set the prototype property without + * performing the [[Extensible]] check + * + * @see IdFunctionObject#getPrototype() + */ + final void setPrototype(Scriptable m, boolean check) + { + assert m != this; + if (check && !isExtensible()) { + throw ScriptRuntime.typeError0("msg.not.extensible"); + } prototypeObject = m; } From 1cbab8d75613461e603289a23ccce1a3c9ebd84c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 090/111] Make sure storeThis() is always the last operation before 'return' to avoid possible IllegalStateExceptions --- src/org/mozilla/javascript/ScriptRuntime.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 7cbd40aef7..fcef9650b1 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -2707,12 +2707,13 @@ public static Object getNameObjectAndThis(String name, if (parent == null) { Object result = topScopeName(cx, scope, name); if (!(result instanceof Callable)) { - storeThis(cx, BAD_SCRIPTABLE); if (result == Scriptable.NOT_FOUND) { - return createNotFoundError(scope, name); + result = createNotFoundError(scope, name); } else { - return notFunctionError(result, name); + result = notFunctionError(result, name); } + storeThis(cx, BAD_SCRIPTABLE); + return result; } // Top scope is not NativeWith or NativeCall => thisObj == scope storeThis(cx, null); @@ -2802,8 +2803,9 @@ private static Object getObjectAndThisHelper(Object obj, String property, if (!valueCallable) { if (property == null) { property = toString(index); } + Object result = notFunctionError(obj, value, property); storeThis(cx, BAD_SCRIPTABLE); - return notFunctionError(obj, value, property); + return result; } storeThis(cx, sobj); @@ -2820,8 +2822,9 @@ private static Object getObjectAndThisHelper(Object obj, String property, public static Object getValueObjectAndThis(Object value, Context cx) { if (!(value instanceof Callable)) { + Object result = notFunctionError(value); storeThis(cx, BAD_SCRIPTABLE); - return notFunctionError(value); + return result; } Scriptable thisObj = null; if (value instanceof Scriptable) { From bfd4bb9d30098d53d78eb4ede7a8311d11d7e900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 091/111] Avoid adding new own 'name' property for error objects if not necessary (SES-conformance) --- src/org/mozilla/javascript/ScriptRuntime.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index fcef9650b1..3bcc2e3d4d 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -3951,7 +3951,12 @@ public static Scriptable newCatchScope(Throwable t, } Scriptable errorObject = cx.newObject(scope, errorName, args); - ScriptableObject.putProperty(errorObject, "name", errorName, false); + // set "name" unless already present with the same value + Object oldName = ScriptableObject.getProperty(errorObject, "name"); + if (oldName == ScriptableObject.NOT_FOUND + || !errorName.equals(toString(oldName))) { + ScriptableObject.putProperty(errorObject, "name", errorName, false); + } // set exception in Error objects to enable non-ECMA "stack" property if (errorObject instanceof NativeError) { ((NativeError) errorObject).setStackProvider(re); From 7870a8d2a44d4680d3fcec83ee554226f4a32af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 092/111] Refine redefineProperty() rules to match Spidermonkey more closely [S8.1_A3, S8.5_A4, S8.5_A10] --- src/org/mozilla/javascript/ScriptableObject.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 1a24fbbf34..287642167c 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -2272,8 +2272,11 @@ public static void redefineProperty(Scriptable obj, String name, if (base instanceof ConstProperties) { ConstProperties cp = (ConstProperties)base; - if (cp.isConst(name)) - throw Context.reportRuntimeError1("msg.const.redecl", name); + if (cp.isConst(name)) { + if (isConst) + throw Context.reportRuntimeError1("msg.const.redecl", name); + return; + } } if (isConst) throw Context.reportRuntimeError1("msg.var.redecl", name); From e36cd25f96c44f55febd313f6f74f90cd416fede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 093/111] Just a note to self --- src/org/mozilla/javascript/Arguments.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/org/mozilla/javascript/Arguments.java b/src/org/mozilla/javascript/Arguments.java index f58dbe8ea3..88591904ae 100644 --- a/src/org/mozilla/javascript/Arguments.java +++ b/src/org/mozilla/javascript/Arguments.java @@ -402,6 +402,7 @@ protected boolean defineOwnProperty(String name, PropertyDescriptor desc, boolean allowed = super.defineOwnProperty(name, desc, false); if (!allowed) { if (checked) { + // TODO: error message throw ScriptRuntime.typeError("[[DefineOwnProperty]]"); } return false; From 8db4656c69025a10b86c847cd8bef80e8a6cc920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 094/111] Remove __parent__ special property (https://bugzilla.mozilla.org/show_bug.cgi?id=552560) and align __proto__ implementation to match spidermonkey more closely --- src/org/mozilla/javascript/Context.java | 15 +++++---- src/org/mozilla/javascript/ScriptRuntime.java | 2 +- src/org/mozilla/javascript/SpecialRef.java | 33 ++++++++----------- .../object.getownpropertydescriptor.doctest | 2 -- 4 files changed, 22 insertions(+), 30 deletions(-) diff --git a/src/org/mozilla/javascript/Context.java b/src/org/mozilla/javascript/Context.java index 2ace772e31..9bac9de8d3 100644 --- a/src/org/mozilla/javascript/Context.java +++ b/src/org/mozilla/javascript/Context.java @@ -193,19 +193,20 @@ public class Context public static final int FEATURE_TO_STRING_AS_SOURCE = 4; /** - * Control if properties __proto__ and __parent__ - * are treated specially. + * Control if property __proto__ is treated specially. * If hasFeature(FEATURE_PARENT_PROTO_PROPERTIES) returns true, - * treat __parent__ and __proto__ as special properties. + * treat __proto__ as a special property. *

- * The properties allow to query and set scope and prototype chains for the - * objects. The special meaning of the properties is available - * only when they are used as the right hand side of the dot operator. + * The property allows to query and set prototype chains for the + * objects. The special meaning of the property is available + * only when it is used as the right hand side of the dot operator. * For example, while x.__proto__ = y changes the prototype * chain of the object x to point to y, * x["__proto__"] = y simply assigns a new value to the property * __proto__ in x even when the feature is on. - * + *

+ * This feature no longer applies to the non-standard __parent__ + * property which has been removed completely. * By default {@link #hasFeature(int)} returns true. */ public static final int FEATURE_PARENT_PROTO_PROPERTIES = 5; diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 3bcc2e3d4d..574fa4f172 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -2052,7 +2052,7 @@ public static Object refDel(Ref ref, Context cx) static boolean isSpecialProperty(String s) { - return s.equals("__proto__") || s.equals("__parent__"); + return "__proto__".equals(s); } public static Ref specialRef(Object obj, String specialProperty, diff --git a/src/org/mozilla/javascript/SpecialRef.java b/src/org/mozilla/javascript/SpecialRef.java index f1d42692dd..7b437a8b48 100644 --- a/src/org/mozilla/javascript/SpecialRef.java +++ b/src/org/mozilla/javascript/SpecialRef.java @@ -44,7 +44,6 @@ class SpecialRef extends Ref private static final int SPECIAL_NONE = 0; private static final int SPECIAL_PROTO = 1; - private static final int SPECIAL_PARENT = 2; private Scriptable target; private int type; @@ -67,8 +66,6 @@ static Ref createSpecial(Context cx, Object object, String name) int type; if (name.equals("__proto__")) { type = SPECIAL_PROTO; - } else if (name.equals("__parent__")) { - type = SPECIAL_PARENT; } else { throw new IllegalArgumentException(name); } @@ -92,9 +89,9 @@ public Object get(Context cx) if (proto != ScriptableObject.NOT_FOUND) { return proto; } - return target.getPrototype(); - case SPECIAL_PARENT: - return target.getParentScope(); + // 'null' prototype is reported as 'undefined' to follow spidermonkey + proto = target.getPrototype(); + return (proto != null ? proto : Undefined.instance); default: throw Kit.codeBug(); } @@ -108,9 +105,12 @@ public Object set(Context cx, Object value) // TODO: default for 'checked' set to 'false' for now return ScriptRuntime.setObjectProp(target, name, value, false, cx); case SPECIAL_PROTO: - case SPECIAL_PARENT: { - Scriptable obj = ScriptRuntime.toObjectOrNull(cx, value); + // ignore unless value is 'null' or Scriptable + if (!(value == null || value instanceof Scriptable)) { + return value; + } + Scriptable obj = (Scriptable) value; if (obj != null) { // Check that obj does not contain on its prototype/scope // chain to prevent cycles @@ -120,19 +120,11 @@ public Object set(Context cx, Object value) throw Context.reportRuntimeError1( "msg.cyclic.value", name); } - if (type == SPECIAL_PROTO) { - search = search.getPrototype(); - } else { - search = search.getParentScope(); - } + search = search.getPrototype(); } while (search != null); } - if (type == SPECIAL_PROTO) { - target.setPrototype(obj); - } else { - target.setParentScope(obj); - } - return obj; + target.setPrototype(obj); + return value; } default: throw Kit.codeBug(); @@ -155,7 +147,8 @@ public boolean delete(Context cx) // TODO: default for 'checked' set to 'false' for now return ScriptRuntime.deleteObjectElem(target, name, cx, false); } - return false; + // always report true to follow spidermonkey + return true; } } diff --git a/testsrc/doctests/object.getownpropertydescriptor.doctest b/testsrc/doctests/object.getownpropertydescriptor.doctest index d0538fe719..2cac2aee8b 100644 --- a/testsrc/doctests/object.getownpropertydescriptor.doctest +++ b/testsrc/doctests/object.getownpropertydescriptor.doctest @@ -41,8 +41,6 @@ true js> desc.__proto__ === Object.prototype true -js> desc.__parent__; -[object global] js> var func = function(){} js> func.a = 1; Object.getOwnPropertyDescriptor(func, 'a').toSource() From 46c8489efdea49cd5fe7e16e0a8b963b65259378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 095/111] Disallow E4X syntax in ES5 strict mode (https://bugzilla.mozilla.org/show_bug.cgi?id=695577) --- src/org/mozilla/javascript/Parser.java | 9 ++++++--- src/org/mozilla/javascript/resources/Messages.properties | 7 +++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index b720f066dd..b1a84928b5 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -419,6 +419,9 @@ private boolean mustMatchToken(int toMatch, String msgId, int pos, int len) } private void mustHaveXML() { + if (inUseStrictDirective) { + reportError("msg.XML.not.available.strict"); + } if (!compilerEnv.isXmlAvailable()) { reportError("msg.XML.not.available"); } @@ -2465,7 +2468,7 @@ private AstNode unaryExpr() case Token.LT: // XML stream encountered in expression. - if (compilerEnv.isXmlAvailable()) { + if (!inUseStrictDirective && compilerEnv.isXmlAvailable()) { consumeToken(); return memberExprTail(true, xmlInitializer()); } @@ -2723,7 +2726,7 @@ private AstNode propertyAccess(int tt, AstNode pn) memberTypeFlags = Node.DESCENDANTS_FLAG; } - if (!compilerEnv.isXmlAvailable()) { + if (inUseStrictDirective || !compilerEnv.isXmlAvailable()) { mustMatchToken(Token.NAME, "msg.no.name.after.dot"); Name name = createNameNode(true, Token.GETPROP); PropertyGet pg = new PropertyGet(pn, name, dotPos); @@ -3032,7 +3035,7 @@ private AstNode name(int ttFlagged, int tt) throws IOException { // bounds in instance vars and createNameNode uses them. saveNameTokenData(namePos, nameString, nameLineno); - if (compilerEnv.isXmlAvailable()) { + if (!inUseStrictDirective && compilerEnv.isXmlAvailable()) { return propertyName(-1, 0); } else { return createNameNode(true, Token.NAME); diff --git a/src/org/mozilla/javascript/resources/Messages.properties b/src/org/mozilla/javascript/resources/Messages.properties index 82abe8257c..20a4190d4e 100644 --- a/src/org/mozilla/javascript/resources/Messages.properties +++ b/src/org/mozilla/javascript/resources/Messages.properties @@ -544,10 +544,13 @@ msg.bad.id.strict =\ "{0}" is not a valid identifier for this use in strict mode. msg.strict.function.stmt =\ - "SyntaxError: in strict mode code, functions may be declared only at top level or immediately within another function" + "in strict mode code, functions may be declared only at top level or immediately within another function" msg.unnamed.function.stmt =\ - "SyntaxError: function statement requires a name" + "function statement requires a name" + +msg.XML.not.available.strict =\ + "strict mode does not allow XML syntax" # ScriptRuntime From fa7b3056d3503f1c14697e420447b62f18e962e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 096/111] Added JavaDoc and more supplemental comments; Marked methods as final for now; And some other minor tweaks --- .../javascript/PropertyDescriptor.java | 202 ++++++++++++------ 1 file changed, 141 insertions(+), 61 deletions(-) diff --git a/src/org/mozilla/javascript/PropertyDescriptor.java b/src/org/mozilla/javascript/PropertyDescriptor.java index 819152e587..4006c8ae57 100755 --- a/src/org/mozilla/javascript/PropertyDescriptor.java +++ b/src/org/mozilla/javascript/PropertyDescriptor.java @@ -22,6 +22,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * André Bargull * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which @@ -37,16 +38,14 @@ package org.mozilla.javascript; -import static org.mozilla.javascript.Scriptable.NOT_FOUND; import static org.mozilla.javascript.ScriptableObject.DONTENUM; -import static org.mozilla.javascript.ScriptableObject.EMPTY; import static org.mozilla.javascript.ScriptableObject.PERMANENT; import static org.mozilla.javascript.ScriptableObject.READONLY; -import static org.mozilla.javascript.ScriptableObject.ensureScriptableObject; +import static org.mozilla.javascript.ScriptableObject.ensureScriptable; import static org.mozilla.javascript.ScriptableObject.getProperty; /** - * + * Property Descriptor per ECMAScript 5 [8.10] * */ class PropertyDescriptor { @@ -63,6 +62,7 @@ class PropertyDescriptor { | ENUMERABLE | CONFIGURABLE; private int present = 0; + // default attribute values per 8.6.1, table 7 private Object value = Undefined.instance; private Object getter = Undefined.instance; private Object setter = Undefined.instance; @@ -71,6 +71,9 @@ class PropertyDescriptor { private PropertyDescriptor() { } + /** + * Creates a shallow copy of the supplied property descriptor + */ PropertyDescriptor(PropertyDescriptor desc) { this.present = desc.present; this.value = desc.value; @@ -79,23 +82,44 @@ private PropertyDescriptor() { this.attributes = desc.attributes; } + /** + * Creates a new data property descriptor with an initial value:
+ * {[[Value]]: ?} + */ PropertyDescriptor(Object value) { this.value = value; this.present = VALUE; } + /** + * Creates a new data property descriptor with an initial value and initial + * attributes:
+ * {[[Value]]: ?, [[Writable]]: ?, [[Enumerable]]: ?, + * [[Configurable]]: ?} + */ PropertyDescriptor(Object value, int attributes) { this.value = value; this.attributes = attributes; this.present = VALUE | WRITABLE | ENUMERABLE | CONFIGURABLE; } + /** + * Creates a new accessor property descriptor with initial getter and + * setter:
+ * {[[Get]]: ?, [[Set]]: ?} + */ PropertyDescriptor(Object getter, Object setter) { this.getter = getter; this.setter = setter; this.present = GET | SET; } + /** + * Creates a new accessor property descriptor with initial getter and setter + * and initial attributes:
+ * {[[Get]]: ?, [[Set]]: ?, [[Enumerable]]: ?, [[Configurable]]: ?} + * + */ PropertyDescriptor(Object getter, Object setter, int attributes) { this.getter = getter; this.setter = setter; @@ -103,8 +127,15 @@ private PropertyDescriptor() { this.present = GET | SET | ENUMERABLE | CONFIGURABLE; } + /** + * ECMAScript 5: 8.10.4 FromPropertyDescriptor (Desc)
+ * Returns {@code undefined} if the input property descriptor is + * {@code null}, otherwise returns a {@link Scriptable} representing the + * fields of this property descriptor. + */ static Object fromPropertyDescriptor(Context cx, Scriptable scope, - PropertyDescriptor desc) { + PropertyDescriptor desc) throws IllegalArgumentException { + /* 8.10.4, step 1. */ if (desc == null) { return Undefined.instance; } @@ -115,45 +146,51 @@ static Object fromPropertyDescriptor(Context cx, Scriptable scope, throw new IllegalArgumentException(String.valueOf(present)); } - ScriptableObject obj = ensureScriptableObject(cx.newObject(scope)); + /* 8.10.4, step 2. */ + Scriptable obj = ensureScriptable(cx.newObject(scope)); + /* 8.10.4, step 3-6. */ if (desc.isDataDescriptor()) { - obj.defineProperty("value", desc.getValue(), EMPTY, false); - obj.defineProperty("writable", desc.isWritable(), EMPTY, false); + obj.put("value", obj, desc.getValue(), false); + obj.put("writable", obj, desc.isWritable(), false); } else { - obj.defineProperty("get", desc.getGetter(), EMPTY, false); - obj.defineProperty("set", desc.getSetter(), EMPTY, false); + obj.put("get", obj, desc.getGetter(), false); + obj.put("set", obj, desc.getSetter(), false); } - obj.defineProperty("enumerable", desc.isEnumerable(), EMPTY, false); - obj.defineProperty("configurable", desc.isConfigurable(), EMPTY, false); + obj.put("enumerable", obj, desc.isEnumerable(), false); + obj.put("configurable", obj, desc.isConfigurable(), false); + /* 8.10.4, step 7. */ return obj; } + /** + * ECMAScript 5: 8.10.5 ToPropertyDescriptor (Desc)
+ * Returns a new property descriptor from the input argument {@code object}, + * if {@code object} is not an instance of {@link Scriptable}, a TypeError + * is thrown. + */ static PropertyDescriptor toPropertyDescriptor(Object object) { - ScriptableObject obj = ensureScriptableObject(object); + /* 8.10.5, step 1. */ + Scriptable obj = ensureScriptable(object); + /* 8.10.5, step 2-8. */ PropertyDescriptor desc = new PropertyDescriptor(); if (ScriptableObject.hasProperty(obj, "enumerable")) { Object enumerable = getProperty(obj, "enumerable"); - if (enumerable == NOT_FOUND) enumerable = Boolean.FALSE; desc.setEnumerable(ScriptRuntime.toBoolean(enumerable)); } if (ScriptableObject.hasProperty(obj, "configurable")) { Object configurable = getProperty(obj, "configurable"); - if (configurable == NOT_FOUND) configurable = Boolean.FALSE; desc.setConfigurable(ScriptRuntime.toBoolean(configurable)); } if (ScriptableObject.hasProperty(obj, "value")) { Object value = ScriptableObject.getProperty(obj, "value"); - if (value == NOT_FOUND) value = Undefined.instance; desc.setValue(value); } if (ScriptableObject.hasProperty(obj, "writable")) { Object writable = getProperty(obj, "writable"); - if (writable == NOT_FOUND) writable = Boolean.FALSE; desc.setWritable(ScriptRuntime.toBoolean(writable)); } if (ScriptableObject.hasProperty(obj, "get")) { Object getter = ScriptableObject.getProperty(obj, "get"); - if (getter == NOT_FOUND) getter = Undefined.instance; if (getter != Undefined.instance && !(getter instanceof Callable)) { throw ScriptRuntime.notFunctionError(getter); } @@ -161,36 +198,63 @@ static PropertyDescriptor toPropertyDescriptor(Object object) { } if (ScriptableObject.hasProperty(obj, "set")) { Object setter = ScriptableObject.getProperty(obj, "set"); - if (setter == NOT_FOUND) setter = Undefined.instance; if (setter != Undefined.instance && !(setter instanceof Callable)) { throw ScriptRuntime.notFunctionError(setter); } desc.setSetter(setter); } + /* 8.10.5, step 9. */ if ((desc.present & (GET | SET)) != 0 && (desc.present & (VALUE | WRITABLE)) != 0) { throw ScriptRuntime.typeError0("msg.both.data.and.accessor.desc"); } + /* 8.10.5, step 10. */ return desc; } - public boolean isAccessorDescriptor() { + /** + * ECMAScript 5: 8.10.1 IsAccessorDescriptor (Desc)
+ * Returns {@code true} if this object is an accessor property descriptor + */ + public final boolean isAccessorDescriptor() { return (present & (GET | SET)) != 0; } - public boolean isDataDescriptor() { + /** + * ECMAScript 5: 8.10.2 IsDataDescriptor (Desc)
+ * Returns {@code true} if this object is a data property descriptor + */ + public final boolean isDataDescriptor() { return (present & (VALUE | WRITABLE)) != 0; } - public boolean isGenericDescriptor() { + /** + * ECMAScript 5: 8.10.3 IsGenericDescriptor (Desc)
+ * Returns {@code true} if this object is a generic property descriptor + */ + public final boolean isGenericDescriptor() { return (present & (GET | SET | VALUE | WRITABLE)) == 0; } - public int getAttributes() { + /** + * Returns the attribute set of this property descriptor + * + * @see ScriptableObject#READONLY + * @see ScriptableObject#DONTENUM + * @see ScriptableObject#PERMANENT + */ + public final int getAttributes() { return attributes; } - public int getAttributeMask() { + /** + * Returns a bit mask of the currently set attributes + * + * @see ScriptableObject#READONLY + * @see ScriptableObject#DONTENUM + * @see ScriptableObject#PERMANENT + */ + public final int getAttributeMask() { int mask = 0; if ((present & WRITABLE) != 0) { mask |= READONLY; @@ -204,127 +268,143 @@ public int getAttributeMask() { return mask; } - public int getPresent() { + /** + * Returns a bit mask of the currently set fields of this property + * descriptor + */ + public final int getPresent() { return present; } - public boolean hasValue() { + /** + * Returns {@code true} if the [[Value]] field is present + */ + public final boolean hasValue() { return (present & VALUE) != 0; } - public boolean hasGetter() { + /** + * Returns {@code true} if the [[Get]] field is present + */ + public final boolean hasGetter() { return (present & GET) != 0; } - public boolean hasSetter() { + /** + * Returns {@code true} if the [[Set]] field is present + */ + public final boolean hasSetter() { return (present & SET) != 0; } - public boolean hasWritable() { + /** + * Returns {@code true} if the [[Writable]] field is present + */ + public final boolean hasWritable() { return (present & WRITABLE) != 0; } - public boolean hasEnumerable() { + /** + * Returns {@code true} if the [[Enumerable]] field is present + */ + public final boolean hasEnumerable() { return (present & ENUMERABLE) != 0; } - public boolean hasConfigurable() { + /** + * Returns {@code true} if the [[Configurable]] field is present + */ + public final boolean hasConfigurable() { return (present & CONFIGURABLE) != 0; } /** - * @return the value + * Returns the [[Value]] field or its default value */ - public Object getValue() { + public final Object getValue() { return value; } /** - * @param value - * the value to set + * Sets the [[Value]] field to the argument value */ - public void setValue(Object value) { + public final void setValue(Object value) { present |= VALUE; this.value = value; } /** - * @return the getter + * Returns the [[Get]] field or its default value */ - public Object getGetter() { + public final Object getGetter() { return getter; } /** - * @param getter - * the getter to set + * Sets the [[Get]] field to the argument value */ - public void setGetter(Object getter) { + public final void setGetter(Object getter) { present |= GET; this.getter = getter; } /** - * @return the setter + * Returns the [[Set]] field or its default value */ - public Object getSetter() { + public final Object getSetter() { return setter; } /** - * @param setter - * the setter to set + * Sets the [[Set]] field to the argument value */ - public void setSetter(Object setter) { + public final void setSetter(Object setter) { present |= SET; this.setter = setter; } /** - * @return the writable + * Returns the [[Writable]] field or its default value */ - public boolean isWritable() { + public final boolean isWritable() { return (attributes & READONLY) == 0; } /** - * @param writable - * the writable to set + * Sets the [[Writable]] field to the argument value */ - public void setWritable(boolean writable) { + public final void setWritable(boolean writable) { present |= WRITABLE; attributes = (writable ? attributes & ~READONLY : attributes | READONLY); } /** - * @return the enumerable + * Returns the [[Enumerable]] field or its default value */ - public boolean isEnumerable() { + public final boolean isEnumerable() { return (attributes & DONTENUM) == 0; } /** - * @param enumerable - * the enumerable to set + * Sets the [[Enumerable]] field to the argument value */ - public void setEnumerable(boolean enumerable) { + public final void setEnumerable(boolean enumerable) { present |= ENUMERABLE; attributes = (enumerable ? attributes & ~DONTENUM : attributes | DONTENUM); } /** - * @return the configurable + * Returns the [[Configurable]] field or its default value */ - public boolean isConfigurable() { + public final boolean isConfigurable() { return (attributes & PERMANENT) == 0; } /** - * @param configurable - * the configurable to set + * Sets the [[Configurable]] field to the argument value */ - public void setConfigurable(boolean configurable) { + public final void setConfigurable(boolean configurable) { present |= CONFIGURABLE; attributes = (configurable ? attributes & ~PERMANENT : attributes | PERMANENT); From 0e7241ec1df61a97b8e4ccd8076029ae5a08a4c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 097/111] Added documentation and fixed two brain-ohs in {define,update}OwnProperty() --- .../mozilla/javascript/ScriptableObject.java | 193 ++++++++++++++---- 1 file changed, 150 insertions(+), 43 deletions(-) diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 287642167c..a34292eac1 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -2694,6 +2694,7 @@ private boolean putImpl(String name, int index, Scriptable start, } else if (!isExtensible) { slot = getSlot(name, index, SLOT_QUERY); if (slot == null) { + // TODO: error message if (checked) { throw ScriptRuntime.typeError("[[Extensible]]"); } return true; } @@ -3152,28 +3153,35 @@ private static Object call(Callable call, ScriptableObject thisObject, private static boolean isPrimitive(Object value) { return (value == null || value == Undefined.instance - || value instanceof CharSequence || value instanceof Number || value instanceof Boolean); + || value instanceof CharSequence || value instanceof Number + || value instanceof Boolean); } private static class SlotPropertyDescriptor extends PropertyDescriptor { private final Slot slot; + // always null unless assertions are enabled + private ScriptableObject owner; - SlotPropertyDescriptor(Slot slot) { + SlotPropertyDescriptor(Slot slot, ScriptableObject owner) { super(slot.value, slot.attributes); this.slot = slot; + this.owner = null; + assert (this.owner = owner) != null; } - SlotPropertyDescriptor(GetterSlot slot) { + SlotPropertyDescriptor(GetterSlot slot, ScriptableObject owner) { super(slot.getter == null ? Undefined.instance : slot.getter, slot.setter == null ? Undefined.instance : slot.setter, ((Slot) slot).attributes); this.slot = slot; + this.owner = null; + assert (this.owner = owner) != null; } } /** * Internal helper method - * + * * @see IdScriptableObject#getSlotOrProtoValue(String, int, Object, Context, Scriptable) */ final Object getSlotValue(String name, int index, Object base, @@ -3197,9 +3205,9 @@ final Object getSlotValue(String name, int index, Object base, /** * ECMAScript 5 - * + * * 8.12.1 [[GetOwnProperty]] (P) - * + * */ protected PropertyDescriptor getOwnProperty(String name) { Slot slot; @@ -3209,30 +3217,37 @@ protected PropertyDescriptor getOwnProperty(String name) { } else { slot = getSlot(name, 0, SLOT_QUERY); } + /* 8.12.2, step 1. */ if (slot == null) { return null; } slot = unwrapSlot(slot); + /* 8.12.2, step 2-7. */ PropertyDescriptor desc; if (slot instanceof GetterSlot) { - desc = new SlotPropertyDescriptor((GetterSlot) slot); + desc = new SlotPropertyDescriptor((GetterSlot) slot, this); } else { - desc = new SlotPropertyDescriptor(slot); + desc = new SlotPropertyDescriptor(slot, this); } + /* 8.12.2, step 8. */ return desc; } /** * ECMAScript 5 - * + * * 8.12.2 [[GetProperty]] (P) - * + * + *

This method is subject to change without further notice - do + * not use unless you know what you are doing!

*/ protected final PropertyDescriptor $getProperty(String name) { + /* 8.12.2, step 1-2. */ PropertyDescriptor prop = getOwnProperty(name); if (prop != null) { return prop; } + /* 8.12.2, step 3-5. */ Scriptable proto = getPrototype(); if (proto == null || !(proto instanceof ScriptableObject)) { return null; @@ -3242,32 +3257,40 @@ protected PropertyDescriptor getOwnProperty(String name) { /** * ECMAScript 5 - * + * * 8.12.3 [[Get]] (P) - * + * + *

This method is subject to change without further notice - do + * not use unless you know what you are doing!

*/ protected final Object $get(String name) { + /* 8.12.3, step 1-2. */ PropertyDescriptor desc = $getProperty(name); if (desc == null) { return Undefined.instance; - } else if (desc.isDataDescriptor()) { + } + /* 8.12.3, step 3. */ + if (desc.isDataDescriptor()) { return desc.getValue(); - } else { - Object getter = desc.getGetter(); - if (getter == Undefined.instance) { - return Undefined.instance; - } - return call((Callable) getter, this, ScriptRuntime.emptyArgs); } + /* 8.12.3, step 4-6. */ + Object getter = desc.getGetter(); + if (getter == Undefined.instance) { + return Undefined.instance; + } + return call((Callable) getter, this, ScriptRuntime.emptyArgs); } /** * ECMAScript 5 - * + * * 8.12.4 [[CanPut]] (P) - * + * + *

This method is subject to change without further notice - do + * not use unless you know what you are doing!

*/ protected final boolean $canPut(String name) { + /* 8.12.4, step 1-2. */ PropertyDescriptor desc = getOwnProperty(name); if (desc != null) { if (desc.isAccessorDescriptor()) { @@ -3276,14 +3299,17 @@ protected PropertyDescriptor getOwnProperty(String name) { return desc.isWritable(); } } + /* 8.12.4, step 3-4. */ Scriptable proto = getPrototype(); if (proto == null || !(proto instanceof ScriptableObject)) { return isExtensible(); } + /* 8.12.4, step 5-6. */ PropertyDescriptor inherited = ((ScriptableObject) proto).$getProperty(name); if (inherited == null) { return isExtensible(); } + /* 8.12.4, step 7-8. */ if (inherited.isAccessorDescriptor()) { return inherited.getSetter() != Undefined.instance; } else { @@ -3293,23 +3319,28 @@ protected PropertyDescriptor getOwnProperty(String name) { /** * ECMAScript 5 - * + * * 8.12.5 [[Put]] (P, V, Throw) - * + * + *

This method is subject to change without further notice - do + * not use unless you know what you are doing!

*/ protected final void $put(String name, Object value, boolean checked) { + /* 8.12.5, step 1. */ if (!$canPut(name)) { if (checked) { throw ScriptRuntime.typeError("cannot [[Put]]:" + name); } return; } + /* 8.12.5, step 2-3. */ PropertyDescriptor ownDesc = getOwnProperty(name); if (ownDesc != null && ownDesc.isDataDescriptor()) { PropertyDescriptor valueDesc = new PropertyDescriptor(value); defineOwnProperty(name, valueDesc, checked); return; } + /* 8.12.5, step 4-6. */ PropertyDescriptor desc = $getProperty(name); if (desc != null && desc.isAccessorDescriptor()) { Object setter = desc.getSetter(); @@ -3322,25 +3353,33 @@ protected PropertyDescriptor getOwnProperty(String name) { /** * ECMAScript 5 - * + * * 8.12.6 [[HasProperty]] (P) - * + * + *

This method is subject to change without further notice - do + * not use unless you know what you are doing!

*/ protected final boolean $hasProperty(String name) { + /* 8.12.6, step 1-3. */ return $getProperty(name) != null; } /** * ECMAScript 5 - * + * * 8.12.7 [[Delete]] (P, Throw) - * + * + *

This method is subject to change without further notice - do + * not use unless you know what you are doing!

*/ protected final boolean $delete(String name, boolean checked) { + /* 8.12.7, step 1-2. */ PropertyDescriptor desc = getOwnProperty(name); if (desc == null) { return true; - } else if (desc.isConfigurable()) { + } + /* 8.12.7, step 3. */ + if (desc.isConfigurable()) { long index = ScriptRuntime.indexFromString(name); if (index >= 0) { removeSlot(null, (int) index, checked); @@ -3348,16 +3387,26 @@ protected PropertyDescriptor getOwnProperty(String name) { removeSlot(name, 0, checked); } return true; - } else if (checked) { + } + /* 8.12.7, step 4. */ + if (checked) { throw ScriptRuntime.typeError("cannot [[Delete]]: " + name); } + /* 8.12.7, step 5. */ return false; } /** * ECMAScript 5 - * + * * 8.12.8 [[DefaultValue]] (hint) + * + * Strict specification compliant implementation of [[DefaultValue]], + * in general {@link #getDefaultValue(Class)} should rather be used which + * also provides Rhino specific additions. + * + *

This method is subject to change without further notice - do + * not use unless you know what you are doing!

*/ protected final Object $defaultValue(String hint) { if (hint == null) { @@ -3373,34 +3422,40 @@ protected PropertyDescriptor getOwnProperty(String name) { } else { throw Context.reportRuntimeError1("msg.invalid.type", hint); } - Object o = get(tryFirst); + /* 8.12.8, step 1-2. */ + Object o = $get(tryFirst); if (o instanceof Callable) { Object val = call((Callable) o, this, ScriptRuntime.emptyArgs); if (isPrimitive(val)) { return val; } } - o = get(trySecond); + /* 8.12.8, step 3-4. */ + o = $get(trySecond); if (o instanceof Callable) { Object val = call((Callable) o, this, ScriptRuntime.emptyArgs); if (isPrimitive(val)) { return val; } } + /* 8.12.8, step 5. */ throw ScriptRuntime.typeError1("msg.default.value", hint); } /** * ECMAScript 5 - * + * * 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw) - * + * + * @see #updateOwnProperty(String, PropertyDescriptor, PropertyDescriptor) */ protected boolean defineOwnProperty(String name, PropertyDescriptor desc, boolean checked) { + /* 8.12.9, step 1. */ PropertyDescriptor current = getOwnProperty(name); reject: { if (current == null) { + /* 8.12.9, step 3-4. */ if (isExtensible()) { updateOwnProperty(name, desc, current); return true; @@ -3408,10 +3463,13 @@ protected boolean defineOwnProperty(String name, PropertyDescriptor desc, break reject; } } - if (desc.getPresent() == 0) { + int present = desc.getPresent(); + /* 8.12.9, step 5. */ + if (present == 0) { return true; } - if (desc.getPresent() == current.getPresent()) { + /* 8.12.9, step 6. */ + if ((present & current.getPresent()) == present) { int mask = desc.getAttributeMask(); if ((desc.getAttributes() & mask) == (current.getAttributes() & mask) && (!desc.hasValue() || sameValue(desc.getValue(), @@ -3423,59 +3481,98 @@ protected boolean defineOwnProperty(String name, PropertyDescriptor desc, return true; } } + /* 8.12.9, step 7. */ if (!current.isConfigurable()) { + /* 8.12.9, step 7a. */ if (desc.isConfigurable()) { break reject; } + /* 8.12.9, step 7b. */ if (desc.hasEnumerable() && desc.isEnumerable() != current.isEnumerable()) { break reject; } } + /* 8.12.9, step 8-11. */ if (desc.isGenericDescriptor()) { // no further validation required } else if (current.isDataDescriptor() != desc.isDataDescriptor()) { + /* 8.12.9, step 8a. */ if (!current.isConfigurable()) { break reject; } } else if (current.isDataDescriptor() && desc.isDataDescriptor()) { if (!current.isConfigurable() && !current.isWritable()) { + /* 8.12.9, step 10a i. */ if (desc.isWritable()) { break reject; - } else if (desc.hasValue() + } + /* 8.12.9, step 10a ii. */ + if (desc.hasValue() && !sameValue(desc.getValue(), current.getValue())) { break reject; } } } else { if (!current.isConfigurable()) { + /* 8.12.9, step 11a i. */ if (desc.hasSetter() && !sameValue(desc.getSetter(), current.getSetter())) { break reject; - } else if (desc.hasGetter() + } + /* 8.12.9, step 11a ii. */ + if (desc.hasGetter() && !sameValue(desc.getGetter(), current.getGetter())) { break reject; } } } - // update + /* 8.12.9, step 12. */ updateOwnProperty(name, desc, current); + /* 8.12.9, step 13. */ return true; } + /* 8.12.9, introductionary text */ if (checked) { throw ScriptRuntime.typeError("[[DefineOwnProperty]]"); } return false; } + /** + * ECMAScript 5 + * + * 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw) - Part II
+ * + * {@link #defineOwnProperty(String, PropertyDescriptor, boolean)} primarily + * performs the required checks from [[DefineOwnProperty]], whereas this + * method performs the actual property creation resp. update. This maps to + * the following steps of [[DefineOwnProperty]]: + *
    + *
  • 8.12.9, step 4a i
  • + *
  • 8.12.9, step 4b i
  • + *
  • 8.12.9, step 9b i
  • + *
  • 8.12.9, step 9c i
  • + *
  • 8.12.9, step 12
  • + *
+ * + * This method should never be invoked on its own, though subclasses are + * allowed to override it to implement special behaviour. + * + * The {@code currrent} parameter must either be an own property descriptor + * for this object or {@code null}. That means it must not be shared between + * {@link ScriptableObject} instances. + */ protected void updateOwnProperty(String name, PropertyDescriptor desc, PropertyDescriptor current) { boolean isAccessor = desc.isAccessorDescriptor(); boolean isNew = current == null; long index; Slot slot; - if (desc instanceof SlotPropertyDescriptor) { - slot = ((SlotPropertyDescriptor) desc).slot; + if (current instanceof SlotPropertyDescriptor) { + // this is the fast path for known slots + assert ((SlotPropertyDescriptor) current).owner == this; + slot = ((SlotPropertyDescriptor) current).slot; name = slot.name; index = slot.indexOrHash; } else { @@ -3494,25 +3591,32 @@ protected void updateOwnProperty(String name, PropertyDescriptor desc, slot = unwrapSlot(slot); } - int baseAttrs, mask; + // the old attributes if any available + int baseAttrs; + // the attributes which are to applied to the property + int mask; if (isNew) { baseAttrs = 0; mask = isAccessor ? (DONTENUM | PERMANENT) : (READONLY | DONTENUM | PERMANENT); } else { - baseAttrs = slot.getAttributes(); + baseAttrs = current.getAttributes(); mask = desc.getAttributeMask(); } if (isAccessor) { + // set getter/setter on property creation or when supplied... boolean hasGetter = isNew || desc.hasGetter(); boolean hasSetter = isNew || desc.hasSetter(); if (!(slot instanceof GetterSlot)) { + // and always set when converting from data to accessor property hasGetter = hasSetter = true; + /* 8.12.9, step 9b i */ baseAttrs = (baseAttrs & (DONTENUM | PERMANENT)); slot = getSlot(name, (int) index, SLOT_MODIFY_GETTER_SETTER); } GetterSlot gslot = (GetterSlot) slot; + // always set value to undefined! gslot.value = Undefined.instance; if (hasGetter) { gslot.getter = desc.getGetter(); @@ -3521,10 +3625,13 @@ protected void updateOwnProperty(String name, PropertyDescriptor desc, gslot.setter = desc.getSetter(); } } else { + // set value on property creation or when supplied... boolean hasValue = isNew || desc.hasValue(); if (slot instanceof GetterSlot && desc.isDataDescriptor()) { + // and always set when converting from accessor to data property hasValue = true; mask |= READONLY; + /* 8.12.9, step 9c i */ baseAttrs = (baseAttrs & (DONTENUM | PERMANENT)); slot = getSlot(name, (int) index, SLOT_CONVERT_ACCESSOR_TO_DATA); } From b8c03878f5af6122ccdb46a03bd86e01acd81295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 098/111] Remove defineProperty() with integer index from ScriptableObject since it's no longer needed; Follow spec more closely for Array.isArray and Array.prototype.concat; Call set/getProperty() methods from ScriptableObject directly in NativeArray to avoid unnecessary round-trips --- src/org/mozilla/javascript/NativeArray.java | 49 +++++++------ src/org/mozilla/javascript/ScriptRuntime.java | 5 +- .../mozilla/javascript/ScriptableObject.java | 68 ------------------- 3 files changed, 29 insertions(+), 93 deletions(-) diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index 4a863df353..bd01fdc08c 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -287,7 +287,7 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, } case ConstructorId_isArray: - return args.length > 0 && (args[0] instanceof NativeArray); + return args.length > 0 && js_isArray(args[0]); case Id_constructor: { boolean inNewExpr = (thisObj == null); @@ -912,16 +912,21 @@ static long getLengthProperty(Context cx, Scriptable obj) { } else if (obj instanceof NativeArray) { return ((NativeArray)obj).getLength(); } - return ScriptRuntime.toUint32( - ScriptRuntime.getObjectProp(obj, "length", cx)); + Object len = ScriptableObject.getProperty(obj, "length"); + if (len == Scriptable.NOT_FOUND) { + // toUint32(undefined) == 0 + return 0; + } + return ScriptRuntime.toUint32(len); } private static Object setLengthProperty(Context cx, Scriptable target, long length) { // [[Put]](_, _, true) -> always throw error for invalid [[Put]] - return ScriptRuntime.setObjectProp( - target, "length", ScriptRuntime.wrapNumber(length), true, cx); + Object len = ScriptRuntime.wrapNumber(length); + ScriptableObject.putProperty(target, "length", len, true); + return len; } /* Utility functions to encapsulate index > Integer.MAX_VALUE @@ -937,12 +942,8 @@ private static void deleteElem(Scriptable target, long index) { private static Object getElem(Context cx, Scriptable target, long index) { - if (index > Integer.MAX_VALUE) { - String id = Long.toString(index); - return ScriptRuntime.getObjectProp(target, id, cx); - } else { - return ScriptRuntime.getObjectIndex(target, (int)index, cx); - } + Object elem = getRawElem(target, index); + return (elem != Scriptable.NOT_FOUND ? elem : Undefined.instance); } // same as getElem, but without converting NOT_FOUND to undefined @@ -961,11 +962,9 @@ private static void defineElem(Context cx, Scriptable target, long index, // invalid [[DefineOwnProperty]] if (index > Integer.MAX_VALUE) { String id = Long.toString(index); - ScriptableObject.defineProperty(target, id, value, - ScriptableObject.EMPTY, false); + target.put(id, target, value, false); } else { - ScriptableObject.defineProperty(target, (int)index, value, - ScriptableObject.EMPTY, false); + target.put((int)index, target, value, false); } } @@ -975,9 +974,9 @@ private static void setElem(Context cx, Scriptable target, long index, // [[Put]](_, _, true) -> always throw error for invalid [[Put]] if (index > Integer.MAX_VALUE) { String id = Long.toString(index); - ScriptRuntime.setObjectProp(target, id, value, true, cx); + ScriptableObject.putProperty(target, id, value, true); } else { - ScriptRuntime.setObjectIndex(target, (int)index, value, true, cx); + ScriptableObject.putProperty(target, (int)index, value, true); } } @@ -1563,8 +1562,7 @@ private static Scriptable js_concat(Context cx, Scriptable scope, // create an empty Array to return. Scriptable thisObj = ScriptRuntime.toObject(cx, scope, thisObject); scope = getTopLevelScope(scope); - Function ctor = ScriptRuntime.getExistingCtor(cx, scope, "Array"); - Scriptable result = ctor.construct(cx, scope, ScriptRuntime.emptyArgs); + Scriptable result = cx.newArray(scope, 0); if (thisObj instanceof NativeArray && result instanceof NativeArray) { NativeArray denseThis = (NativeArray) thisObj; NativeArray denseResult = (NativeArray) result; @@ -1610,7 +1608,7 @@ private static Scriptable js_concat(Context cx, Scriptable scope, /* Put the target in the result array; only add it as an array * if it looks like one. */ - if (ScriptRuntime.instanceOf(thisObj, ctor, cx)) { + if (js_isArray(thisObj)) { length = getLengthProperty(cx, thisObj); // Copy from the target object into the result @@ -1629,8 +1627,8 @@ private static Scriptable js_concat(Context cx, Scriptable scope, * elements separately; otherwise, just copy the argument. */ for (int i = 0; i < args.length; i++) { - if (ScriptRuntime.instanceOf(args[i], ctor, cx)) { - // ScriptRuntime.instanceOf => instanceof Scriptable + if (js_isArray(args[i])) { + // js_isArray => instanceof Scriptable Scriptable arg = (Scriptable)args[i]; length = getLengthProperty(cx, arg); for (long j = 0; j < length; j++, slot++) { @@ -1910,6 +1908,13 @@ private static Object reduceMethod(Context cx, int id, Scriptable scope, return value; } + private static boolean js_isArray(Object o) { + if (!(o instanceof Scriptable)) { + return false; + } + return "Array".equals(((Scriptable)o).getClassName()); + } + // methods to implement java.util.List public boolean contains(Object o) { diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 574fa4f172..67a73b038d 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -3067,7 +3067,7 @@ public static Object evalSpecial(Context cx, Scriptable scope, reporter, sourceName, 1, null, strictMode); evaluator.setEvalScriptFlag(script); if (script instanceof InterpretedFunction) { - // TODO: extend Script interface to make this less voodoo + // TODO: extend Script interface to make this less voodoo? strictMode |= ((InterpretedFunction) script).isStrict(); } if (strictMode) { @@ -4178,8 +4178,7 @@ public static Scriptable newArrayLiteral(Object[] objects, ++skip; continue; } - ScriptableObject.defineProperty(array, i, objects[j], - ScriptableObject.EMPTY, false); + array.put(i, array, objects[j], false); ++j; } return array; diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index a34292eac1..25a2d38f4d 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -1602,74 +1602,6 @@ private static Class extendsScriptable(Class c) return null; } - /** - * Define a JavaScript property. - * - * Creates the property with an initial value and sets its attributes. - * - * @param propertyName the name of the property to define. - * @param value the initial value of the property - * @param attributes the attributes of the JavaScript property - * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object) - */ - @Deprecated - public final void defineProperty(int index, Object value, - int attributes) - { - defineProperty(index, value, attributes, false); - } - - /** - * Define a JavaScript property. - * - * Creates the property with an initial value and sets its attributes. - * - * @param propertyName the name of the property to define. - * @param value the initial value of the property - * @param attributes the attributes of the JavaScript property - * @param checked controls error handling - * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object) - */ - public void defineProperty(int index, Object value, - int attributes, boolean checked) - { - checkNotSealed(null, index); - put(index, this, value, checked); - setAttributes(index, attributes); - } - - /** - * Utility method to add properties to arbitrary Scriptable object. - * If destination is instance of ScriptableObject, calls - * defineProperty there, otherwise calls put in destination - * ignoring attributes - */ - @Deprecated - public static void defineProperty(Scriptable destination, - int index, Object value, - int attributes) - { - defineProperty(destination, index, value, attributes, false); - } - - /** - * Utility method to add properties to arbitrary Scriptable object. - * If destination is instance of ScriptableObject, calls - * defineProperty there, otherwise calls put in destination - * ignoring attributes - */ - public static void defineProperty(Scriptable destination, - int index, Object value, - int attributes, boolean checked) - { - if (!(destination instanceof ScriptableObject)) { - destination.put(index, destination, value, checked); - return; - } - ScriptableObject so = (ScriptableObject)destination; - so.defineProperty(index, value, attributes, checked); - } - /** * Define a JavaScript property. * From aa3545d2aff18a568db002587ee0acdcedc40eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 099/111] Remove duplicate code from Context; Use newBuiltinObject() instead of newObject() in NativeGlobal --- src/org/mozilla/javascript/Context.java | 6 +----- src/org/mozilla/javascript/NativeGlobal.java | 3 ++- src/org/mozilla/javascript/ScriptRuntime.java | 3 ++- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/org/mozilla/javascript/Context.java b/src/org/mozilla/javascript/Context.java index 9bac9de8d3..aeaf533603 100644 --- a/src/org/mozilla/javascript/Context.java +++ b/src/org/mozilla/javascript/Context.java @@ -1539,11 +1539,7 @@ public Scriptable newObject(Scriptable scope, String constructorName) public Scriptable newObject(Scriptable scope, String constructorName, Object[] args) { - scope = ScriptableObject.getTopLevelScope(scope); - Function ctor = ScriptRuntime.getExistingCtor(this, scope, - constructorName); - if (args == null) { args = ScriptRuntime.emptyArgs; } - return ctor.construct(this, scope, args); + return ScriptRuntime.newObject(this, scope, constructorName, args); } /** diff --git a/src/org/mozilla/javascript/NativeGlobal.java b/src/org/mozilla/javascript/NativeGlobal.java index a3350b2bde..3fad2a9f8a 100644 --- a/src/org/mozilla/javascript/NativeGlobal.java +++ b/src/org/mozilla/javascript/NativeGlobal.java @@ -147,7 +147,8 @@ public static void init(Context cx, Scriptable scope, boolean sealed) { for (int i = 0; i < errorMethods.length; i++) { String name = errorMethods[i]; ScriptableObject errorProto = - (ScriptableObject) ScriptRuntime.newObject(cx, scope, "Error", + (ScriptableObject) ScriptRuntime.newBuiltinObject(cx, scope, + TopLevel.Builtins.Error, ScriptRuntime.emptyArgs); errorProto.put("name", errorProto, name, false); errorProto.put("message", errorProto, "", false); diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 67a73b038d..738a8e28bf 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -3950,7 +3950,8 @@ public static Scriptable newCatchScope(Throwable t, args = new Object[] { errorMsg, sourceUri }; } - Scriptable errorObject = cx.newObject(scope, errorName, args); + // FIXME: This must resolve the original error object! + Scriptable errorObject = newObject(cx, scope, errorName, args); // set "name" unless already present with the same value Object oldName = ScriptableObject.getProperty(errorObject, "name"); if (oldName == ScriptableObject.NOT_FOUND From 22562efce37be582c55c6ce10508866b0cd69495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 100/111] Add cache for native error objects to the TopLevel class for the very same reason we've got a cache for the other built-ins; But don't add helper methods to TopLevel to retrieve the prototype property of native errors since its always the same one; And finally use the new helper method to construct the error object in ScriptRuntime#newCatchScope() --- src/org/mozilla/javascript/ScriptRuntime.java | 29 ++++---- src/org/mozilla/javascript/TopLevel.java | 67 +++++++++++++++++++ 2 files changed, 83 insertions(+), 13 deletions(-) diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 738a8e28bf..3e169e3396 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -1119,6 +1119,16 @@ public static Scriptable newBuiltinObject(Context cx, Scriptable scope, return ctor.construct(cx, scope, args); } + public static Scriptable newNativeError(Context cx, Scriptable scope, + TopLevel.NativeErrors type, + Object[] args) + { + scope = ScriptableObject.getTopLevelScope(scope); + Function ctor = TopLevel.getNativeErrorCtor(cx, scope, type); + if (args == null) { args = ScriptRuntime.emptyArgs; } + return ctor.construct(cx, scope, args); + } + /** * * See ECMA 9.4. @@ -3904,33 +3914,33 @@ public static Scriptable newCatchScope(Throwable t, } RhinoException re; - String errorName; + TopLevel.NativeErrors type; String errorMsg; Throwable javaException = null; if (t instanceof EcmaError) { EcmaError ee = (EcmaError)t; re = ee; - errorName = ee.getName(); + type = TopLevel.NativeErrors.valueOf(ee.getName()); errorMsg = ee.getErrorMessage(); } else if (t instanceof WrappedException) { WrappedException we = (WrappedException)t; re = we; javaException = we.getWrappedException(); - errorName = "JavaException"; + type = TopLevel.NativeErrors.JavaException; errorMsg = javaException.getClass().getName() +": "+javaException.getMessage(); } else if (t instanceof EvaluatorException) { // Pure evaluator exception, nor WrappedException instance EvaluatorException ee = (EvaluatorException)t; re = ee; - errorName = "InternalError"; + type = TopLevel.NativeErrors.InternalError; errorMsg = ee.getMessage(); } else if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) { // With FEATURE_ENHANCED_JAVA_ACCESS, scripts can catch // all exception types re = new WrappedException(t); - errorName = "JavaException"; + type = TopLevel.NativeErrors.JavaException; errorMsg = t.toString(); } else { // Script can catch only instances of JavaScriptException, @@ -3950,14 +3960,7 @@ public static Scriptable newCatchScope(Throwable t, args = new Object[] { errorMsg, sourceUri }; } - // FIXME: This must resolve the original error object! - Scriptable errorObject = newObject(cx, scope, errorName, args); - // set "name" unless already present with the same value - Object oldName = ScriptableObject.getProperty(errorObject, "name"); - if (oldName == ScriptableObject.NOT_FOUND - || !errorName.equals(toString(oldName))) { - ScriptableObject.putProperty(errorObject, "name", errorName, false); - } + Scriptable errorObject = newNativeError(cx, scope, type, args); // set exception in Error objects to enable non-ECMA "stack" property if (errorObject instanceof NativeError) { ((NativeError) errorObject).setStackProvider(re); diff --git a/src/org/mozilla/javascript/TopLevel.java b/src/org/mozilla/javascript/TopLevel.java index 6b6e8fd3c4..ae18fa7c85 100644 --- a/src/org/mozilla/javascript/TopLevel.java +++ b/src/org/mozilla/javascript/TopLevel.java @@ -93,7 +93,30 @@ public enum Builtins { Error } + /** + * An enumeration of built-in native errors. [ECMAScript 5 - 15.11.6] + */ + public enum NativeErrors { + /** The native EvalError. */ + EvalError, + /** The native RangeError. */ + RangeError, + /** The native ReferenceError. */ + ReferenceError, + /** The native SyntaxError. */ + SyntaxError, + /** The native TypeError. */ + TypeError, + /** The native URIError. */ + URIError, + /** The native InternalError (non-standard). */ + InternalError, + /** The native JavaException (non-standard). */ + JavaException + } + private EnumMap ctors; + private EnumMap errors; @Override public String getClassName() { @@ -116,6 +139,13 @@ public void cacheBuiltins() { ctors.put(builtin, (BaseFunction)value); } } + errors = new EnumMap(NativeErrors.class); + for (NativeErrors error : NativeErrors.values()) { + Object value = ScriptableObject.getProperty(this, error.name()); + if (value instanceof BaseFunction) { + errors.put(error, (BaseFunction)value); + } + } } /** @@ -144,6 +174,32 @@ public static Function getBuiltinCtor(Context cx, return ScriptRuntime.getExistingCtor(cx, scope, type.name()); } + /** + * Static helper method to get a native error constructor with the given + * type from the given scope. If the scope is not + * an instance of this class or does have a cache of native errors, + * the constructor is looked up via normal property lookup. + * + * @param cx the current Context + * @param scope the top-level scope + * @param type the native error type + * @return the native error constructor + */ + public static Function getNativeErrorCtor(Context cx, + Scriptable scope, + NativeErrors type) { + // must be called with top level scope + assert scope.getParentScope() == null; + if (scope instanceof TopLevel) { + Function result = ((TopLevel)scope).getNativeErrorCtor(type); + if (result != null) { + return result; + } + } + // fall back to normal constructor lookup + return ScriptRuntime.getExistingCtor(cx, scope, type.name()); + } + /** * Static helper method to get a built-in object prototype with the given * type from the given scope. If the scope is not @@ -180,6 +236,17 @@ public BaseFunction getBuiltinCtor(Builtins type) { return ctors != null ? ctors.get(type) : null; } + /** + * Get the cached native error constructor from this scope with the + * given type. Returns null if {@link #cacheBuiltins()} has not + * been called on this object. + * @param type the native error type + * @return the native error constructor + */ + public BaseFunction getNativeErrorCtor(NativeErrors type) { + return errors != null ? errors.get(type) : null; + } + /** * Get the cached built-in object prototype from this scope with the * given type. Returns null if {@link #cacheBuiltins()} has not From 118a44a1e42a8f8e5112c910a0c7d033629e5fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 101/111] arguments object should not have its own 'constructor' property, instead it inherits 'constructor' through its prototype --- src/org/mozilla/javascript/Arguments.java | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/org/mozilla/javascript/Arguments.java b/src/org/mozilla/javascript/Arguments.java index 88591904ae..1ebb8eb5e2 100644 --- a/src/org/mozilla/javascript/Arguments.java +++ b/src/org/mozilla/javascript/Arguments.java @@ -68,9 +68,6 @@ public Arguments(NativeCall activation, boolean strict) NativeFunction f = activation.function; calleeObj = f; - Scriptable topLevel = getTopLevelScope(parent); - constructor = getProperty(topLevel, "Object"); - int version = f.getLanguageVersion(); if (version <= Context.VERSION_1_3 && version != Context.VERSION_DEFAULT) @@ -236,9 +233,8 @@ public void delete(int index, boolean checked) Id_callee = 1, Id_length = 2, Id_caller = 3, - Id_constructor = 4, - MAX_INSTANCE_ID = Id_constructor; + MAX_INSTANCE_ID = Id_caller; @Override protected int getMaxInstanceId() @@ -259,7 +255,6 @@ protected int findInstanceIdInfo(String s) else if (c=='h') { X="length";id=Id_length; } else if (c=='r') { X="caller";id=Id_caller; } } - else if (s_length==11) { X="constructor";id=Id_constructor; } if (X!=null && X!=s && !X.equals(s)) id = 0; break L0; } @@ -278,9 +273,6 @@ protected int findInstanceIdInfo(String s) case Id_length: attr = lengthAttr; break; - case Id_constructor: - attr = constructorAttr; - break; default: throw new IllegalStateException(); } return instanceIdInfo(attr, id); @@ -295,7 +287,6 @@ protected String getInstanceIdName(int id) case Id_callee: return "callee"; case Id_length: return "length"; case Id_caller: return "caller"; - case Id_constructor: return "constructor"; } return null; } @@ -317,8 +308,6 @@ else if (value == null) { } return value; } - case Id_constructor: - return constructor; } return super.getInstanceIdValue(id); } @@ -332,7 +321,6 @@ protected void setInstanceIdValue(int id, Object value) case Id_caller: callerObj = (value != null) ? value : UniqueTag.NULL_VALUE; return; - case Id_constructor: constructor = value; return; } super.setInstanceIdValue(id, value); } @@ -343,7 +331,6 @@ protected void setInstanceIdAttributes(int id, int attr) { case Id_callee: calleeAttr = attr; return; case Id_length: lengthAttr = attr; return; case Id_caller: callerAttr = attr; return; - case Id_constructor: constructorAttr = attr; return; } super.setInstanceIdAttributes(id, attr); } @@ -460,12 +447,10 @@ protected PropertyDescriptor getOwnProperty(String name) { private Object callerObj; private Object calleeObj; private Object lengthObj; - private Object constructor; private int callerAttr = DONTENUM; private int calleeAttr = DONTENUM; private int lengthAttr = DONTENUM; - private int constructorAttr = DONTENUM; private final NativeCall activation; private final boolean strict; From e1add70ca14d2e419a895963189211bbdcde28f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 102/111] Fix 10.5 Binding Instantiation according to ES5.1 errata (https://bugs.ecmascript.org/show_bug.cgi?id=78) --- src/org/mozilla/javascript/ScriptRuntime.java | 15 ++++++++------- src/org/mozilla/javascript/ScriptableObject.java | 5 +++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 3e169e3396..8e59609c6a 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -3827,13 +3827,13 @@ public static void initScript(NativeFunction funObj, Object thisObj, // 10.5 Declaration Binding Instantiation, step 8 // -> 10.2.1.1.2 CreateMutableBinding (N, D) // -> 10.2.1.2.2 CreateMutableBinding (N, D) - boolean strict = true; for (int i = varCount; i-- != 0;) { String name = funObj.getParamOrVarName(i); boolean isConst = funObj.getParamOrVarConst(i); // Don't overwrite existing def if already defined in object - // or prototypes of object. - if (!ScriptableObject.hasProperty(scope, name)) { + // or prototypes of object. Except for global, cf. ES5.1 errata + if (!ScriptableObject.hasProperty(scope, name) || + (scope.getParentScope() == null && !scope.has(name, scope))) { if (!evalScript) { // Global var definitions are supposed to be DONTDELETE if (isConst) @@ -3841,9 +3841,9 @@ public static void initScript(NativeFunction funObj, Object thisObj, else ScriptableObject.defineProperty( varScope, name, Undefined.instance, - ScriptableObject.PERMANENT, strict); + ScriptableObject.PERMANENT, true); } else { - varScope.put(name, varScope, Undefined.instance, strict); + varScope.put(name, varScope, Undefined.instance, true); } } else { ScriptableObject.redefineProperty(scope, name, isConst); @@ -4114,8 +4114,9 @@ public static void initFunction(Context cx, Scriptable scope, } else if (scope.getParentScope() == null) { // 10.5, step 5e: env is the environment record component of the global environment ScriptableObject global = ScriptableObject.ensureScriptableObject(scope); - PropertyDescriptor desc = global.$getProperty(name); - if (desc.isConfigurable()) { + // ES5.1 errata: [[GetProperty]] -> [[GetOwnProperty]] + PropertyDescriptor desc = global.getOwnProperty(name); + if (desc == null || desc.isConfigurable()) { int attrs = fromEvalCode ? ScriptableObject.EMPTY : ScriptableObject.PERMANENT; desc = new PropertyDescriptor(Undefined.instance, attrs); global.defineOwnProperty(name, desc, true); diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 25a2d38f4d..676b9aca80 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -1296,7 +1296,7 @@ public static String defineClass( if (ctor == null) return null; String name = ctor.getClassPrototype().getClassName(); - defineProperty(scope, name, ctor, ScriptableObject.DONTENUM); + defineProperty(scope, name, ctor, ScriptableObject.DONTENUM, false); return name; } @@ -1504,7 +1504,7 @@ else if (ctors[1].getParameterTypes().length == 0) throw Context.reportRuntimeError1 ("msg.varargs.fun", ctorMember.getName()); } - defineProperty(isStatic ? ctor : proto, name, f, DONTENUM); + defineProperty(isStatic ? ctor : proto, name, f, DONTENUM, false); if (sealed) { f.sealObject(); } @@ -1683,6 +1683,7 @@ public static void defineConstProperty(Scriptable destination, ConstProperties cp = (ConstProperties)destination; cp.defineConst(propertyName, destination); } else + // TODO: strict flag! defineProperty(destination, propertyName, Undefined.instance, CONST); } From 7037e8d485131cde40890be1e6320c9e1a7071cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 103/111] Restructure SpecialRef to use OOP and improve handling for own '__proto__' property (WIP) --- src/org/mozilla/javascript/Interpreter.java | 3 +- src/org/mozilla/javascript/ScriptRuntime.java | 6 +- src/org/mozilla/javascript/SpecialRef.java | 150 ++++++++++-------- .../mozilla/javascript/optimizer/Codegen.java | 2 + 4 files changed, 88 insertions(+), 73 deletions(-) diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index b162fb870b..efc48be396 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -2010,7 +2010,8 @@ private static Object interpretLoop(Context cx, CallFrame frame, //stringReg: name of special property Object obj = stack[stackTop]; if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.specialRef(obj, stringReg, cx); + stack[stackTop] = ScriptRuntime.specialRef(obj, stringReg, cx, + frame.scope); continue Loop; } case Token.REF_MEMBER: { diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 8e59609c6a..4482d135f2 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -2066,9 +2066,9 @@ static boolean isSpecialProperty(String s) } public static Ref specialRef(Object obj, String specialProperty, - Context cx) + Context cx, Scriptable scope) { - return SpecialRef.createSpecial(cx, obj, specialProperty); + return SpecialRef.createSpecial(cx, scope, obj, specialProperty); } /** @@ -4218,7 +4218,7 @@ public static Scriptable newObjectLiteral(Object[] propertyIds, if (id instanceof String) { if (getterSetter == 0) { if (isSpecialProperty((String)id)) { - specialRef(object, (String)id, cx).set(cx, value); + specialRef(object, (String)id, cx, scope).set(cx, value); } else { object.put((String)id, object, value, false); } diff --git a/src/org/mozilla/javascript/SpecialRef.java b/src/org/mozilla/javascript/SpecialRef.java index 7b437a8b48..96270d2dfe 100644 --- a/src/org/mozilla/javascript/SpecialRef.java +++ b/src/org/mozilla/javascript/SpecialRef.java @@ -42,70 +42,33 @@ class SpecialRef extends Ref { static final long serialVersionUID = -7521596632456797847L; - private static final int SPECIAL_NONE = 0; - private static final int SPECIAL_PROTO = 1; - - private Scriptable target; - private int type; - private String name; - - private SpecialRef(Scriptable target, int type, String name) - { - this.target = target; - this.type = type; - this.name = name; - } - - static Ref createSpecial(Context cx, Object object, String name) + /** + * Helper class for the __proto__ special property + */ + private static class ProtoSpecialRef extends SpecialRef { - Scriptable target = ScriptRuntime.toObjectOrNull(cx, object); - if (target == null) { - throw ScriptRuntime.undefReadError(object, name); - } + static final long serialVersionUID = -6410092416752016016L; - int type; - if (name.equals("__proto__")) { - type = SPECIAL_PROTO; - } else { - throw new IllegalArgumentException(name); + private ProtoSpecialRef(Scriptable target, String name) + { + super(target, name); } - if (!cx.hasFeature(Context.FEATURE_PARENT_PROTO_PROPERTIES)) { - // Clear special after checking for valid name! - type = SPECIAL_NONE; - } - - return new SpecialRef(target, type, name); - } - - @Override - public Object get(Context cx) - { - switch (type) { - case SPECIAL_NONE: - return ScriptRuntime.getObjectProp(target, name, cx); - case SPECIAL_PROTO: - Object proto = target.get("__proto__", target); - if (proto != ScriptableObject.NOT_FOUND) { - return proto; + @Override + public Object get(Context cx) + { + if (!ScriptRuntime.hasObjectElem(target, name, cx)) { + // 'null' prototype is reported as 'undefined' + Object proto = target.getPrototype(); + return (proto != null ? proto : Undefined.instance); } - // 'null' prototype is reported as 'undefined' to follow spidermonkey - proto = target.getPrototype(); - return (proto != null ? proto : Undefined.instance); - default: - throw Kit.codeBug(); + return super.get(cx); } - } - @Override - public Object set(Context cx, Object value) - { - switch (type) { - case SPECIAL_NONE: - // TODO: default for 'checked' set to 'false' for now - return ScriptRuntime.setObjectProp(target, name, value, false, cx); - case SPECIAL_PROTO: - { + @Override + public Object set(Context cx, Object value) + { + if (!ScriptRuntime.hasObjectElem(target, name, cx)) { // ignore unless value is 'null' or Scriptable if (!(value == null || value instanceof Scriptable)) { return value; @@ -126,29 +89,78 @@ public Object set(Context cx, Object value) target.setPrototype(obj); return value; } - default: - throw Kit.codeBug(); + return super.set(cx, value); + } + + @Override + public boolean has(Context cx) + { + // always return true to follow spidermonkey + return true; + } + + @Override + public boolean delete(Context cx) + { + if (!ScriptRuntime.hasObjectElem(target, name, cx)) { + // always return true to follow spidermonkey + return true; + } + return super.delete(cx); + } + } + + protected final Scriptable target; + protected final String name; + + private SpecialRef(Scriptable target, String name) + { + this.target = target; + this.name = name; + } + + static Ref createSpecial(Context cx, Scriptable scope, Object object, + String name) + { + Scriptable target = ScriptRuntime.toObjectOrNull(cx, object, scope); + if (target == null) { + throw ScriptRuntime.undefReadError(object, name); + } + + if ("__proto__".equals(name)) { + if (cx.hasFeature(Context.FEATURE_PARENT_PROTO_PROPERTIES)) { + return new ProtoSpecialRef(target, name); + } + return new SpecialRef(target, name); } + + throw new IllegalArgumentException(name); + } + + @Override + public Object get(Context cx) + { + return ScriptRuntime.getObjectProp(target, name, cx); + } + + @Override + public Object set(Context cx, Object value) + { + // TODO: default for 'checked' set to 'false' for now + return ScriptRuntime.setObjectProp(target, name, value, false, cx); } @Override public boolean has(Context cx) { - if (type == SPECIAL_NONE) { - return ScriptRuntime.hasObjectElem(target, name, cx); - } - return true; + return ScriptRuntime.hasObjectElem(target, name, cx); } @Override public boolean delete(Context cx) { - if (type == SPECIAL_NONE) { - // TODO: default for 'checked' set to 'false' for now - return ScriptRuntime.deleteObjectElem(target, name, cx, false); - } - // always report true to follow spidermonkey - return true; + // TODO: default for 'checked' set to 'false' for now + return ScriptRuntime.deleteObjectElem(target, name, cx, false); } } diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index f600bfe48b..3e45ec2a06 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -2813,11 +2813,13 @@ private void generateExpression(Node node, Node parent) generateExpression(child, node); cfw.addPush(special); cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); addScriptRuntimeInvoke( "specialRef", "(Ljava/lang/Object;" +"Ljava/lang/String;" +"Lorg/mozilla/javascript/Context;" + +"Lorg/mozilla/javascript/Scriptable;" +")Lorg/mozilla/javascript/Ref;"); } break; From c6dc96aac46697e51edf0eef959cade19d513d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 104/111] Add strict-mode support to SpecialRef --- src/org/mozilla/javascript/Interpreter.java | 3 ++- src/org/mozilla/javascript/ScriptRuntime.java | 6 +++--- src/org/mozilla/javascript/SpecialRef.java | 20 +++++++++---------- .../mozilla/javascript/optimizer/Codegen.java | 2 ++ 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index efc48be396..302cce95f2 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -2010,7 +2010,8 @@ private static Object interpretLoop(Context cx, CallFrame frame, //stringReg: name of special property Object obj = stack[stackTop]; if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.specialRef(obj, stringReg, cx, + stack[stackTop] = ScriptRuntime.specialRef(obj, stringReg, + frame.idata.isStrict, cx, frame.scope); continue Loop; } diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 4482d135f2..05b87c001a 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -2066,9 +2066,9 @@ static boolean isSpecialProperty(String s) } public static Ref specialRef(Object obj, String specialProperty, - Context cx, Scriptable scope) + boolean strict, Context cx, Scriptable scope) { - return SpecialRef.createSpecial(cx, scope, obj, specialProperty); + return SpecialRef.createSpecial(cx, scope, obj, specialProperty, strict); } /** @@ -4218,7 +4218,7 @@ public static Scriptable newObjectLiteral(Object[] propertyIds, if (id instanceof String) { if (getterSetter == 0) { if (isSpecialProperty((String)id)) { - specialRef(object, (String)id, cx, scope).set(cx, value); + specialRef(object, (String)id, false, cx, scope).set(cx, value); } else { object.put((String)id, object, value, false); } diff --git a/src/org/mozilla/javascript/SpecialRef.java b/src/org/mozilla/javascript/SpecialRef.java index 96270d2dfe..9c45b6aa1d 100644 --- a/src/org/mozilla/javascript/SpecialRef.java +++ b/src/org/mozilla/javascript/SpecialRef.java @@ -49,9 +49,9 @@ private static class ProtoSpecialRef extends SpecialRef { static final long serialVersionUID = -6410092416752016016L; - private ProtoSpecialRef(Scriptable target, String name) + private ProtoSpecialRef(Scriptable target, String name, boolean strict) { - super(target, name); + super(target, name, strict); } @Override @@ -112,15 +112,17 @@ public boolean delete(Context cx) protected final Scriptable target; protected final String name; + protected final boolean strict; - private SpecialRef(Scriptable target, String name) + private SpecialRef(Scriptable target, String name, boolean strict) { this.target = target; this.name = name; + this.strict = strict; } static Ref createSpecial(Context cx, Scriptable scope, Object object, - String name) + String name, boolean strict) { Scriptable target = ScriptRuntime.toObjectOrNull(cx, object, scope); if (target == null) { @@ -129,9 +131,9 @@ static Ref createSpecial(Context cx, Scriptable scope, Object object, if ("__proto__".equals(name)) { if (cx.hasFeature(Context.FEATURE_PARENT_PROTO_PROPERTIES)) { - return new ProtoSpecialRef(target, name); + return new ProtoSpecialRef(target, name, strict); } - return new SpecialRef(target, name); + return new SpecialRef(target, name, strict); } throw new IllegalArgumentException(name); @@ -146,8 +148,7 @@ public Object get(Context cx) @Override public Object set(Context cx, Object value) { - // TODO: default for 'checked' set to 'false' for now - return ScriptRuntime.setObjectProp(target, name, value, false, cx); + return ScriptRuntime.setObjectProp(target, name, value, strict, cx); } @Override @@ -159,8 +160,7 @@ public boolean has(Context cx) @Override public boolean delete(Context cx) { - // TODO: default for 'checked' set to 'false' for now - return ScriptRuntime.deleteObjectElem(target, name, cx, false); + return ScriptRuntime.deleteObjectElem(target, name, cx, strict); } } diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index 3e45ec2a06..127829b025 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -2812,12 +2812,14 @@ private void generateExpression(Node node, Node parent) String special = (String)node.getProp(Node.NAME_PROP); generateExpression(child, node); cfw.addPush(special); + cfw.addPush(scriptOrFn.isInStrictMode()); cfw.addALoad(contextLocal); cfw.addALoad(variableObjectLocal); addScriptRuntimeInvoke( "specialRef", "(Ljava/lang/Object;" +"Ljava/lang/String;" + +"Z" +"Lorg/mozilla/javascript/Context;" +"Lorg/mozilla/javascript/Scriptable;" +")Lorg/mozilla/javascript/Ref;"); From 98b532909494d8fbcaa04980803a1c3b33c16776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 105/111] Don't clobber 'this' value in SpecialRef -> consider setter/getter properties --- src/org/mozilla/javascript/SpecialRef.java | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/org/mozilla/javascript/SpecialRef.java b/src/org/mozilla/javascript/SpecialRef.java index 9c45b6aa1d..a063ec4cb7 100644 --- a/src/org/mozilla/javascript/SpecialRef.java +++ b/src/org/mozilla/javascript/SpecialRef.java @@ -49,9 +49,10 @@ private static class ProtoSpecialRef extends SpecialRef { static final long serialVersionUID = -6410092416752016016L; - private ProtoSpecialRef(Scriptable target, String name, boolean strict) + private ProtoSpecialRef(Object object, Scriptable target, String name, + boolean strict, Scriptable scope) { - super(target, name, strict); + super(object, target, name, strict, scope); } @Override @@ -110,15 +111,20 @@ public boolean delete(Context cx) } } + protected final Object object; protected final Scriptable target; protected final String name; protected final boolean strict; + protected final Scriptable scope; - private SpecialRef(Scriptable target, String name, boolean strict) + private SpecialRef(Object object, Scriptable target, String name, + boolean strict, Scriptable scope) { + this.object = object; this.target = target; this.name = name; this.strict = strict; + this.scope = scope; } static Ref createSpecial(Context cx, Scriptable scope, Object object, @@ -131,9 +137,9 @@ static Ref createSpecial(Context cx, Scriptable scope, Object object, if ("__proto__".equals(name)) { if (cx.hasFeature(Context.FEATURE_PARENT_PROTO_PROPERTIES)) { - return new ProtoSpecialRef(target, name, strict); + return new ProtoSpecialRef(object, target, name, strict, scope); } - return new SpecialRef(target, name, strict); + return new SpecialRef(object, target, name, strict, scope); } throw new IllegalArgumentException(name); @@ -142,13 +148,14 @@ static Ref createSpecial(Context cx, Scriptable scope, Object object, @Override public Object get(Context cx) { - return ScriptRuntime.getObjectProp(target, name, cx); + return ScriptRuntime.getObjectValue(object, name, strict, cx, scope); } @Override public Object set(Context cx, Object value) { - return ScriptRuntime.setObjectProp(target, name, value, strict, cx); + ScriptRuntime.putObjectValue(object, name, strict, value, cx, scope); + return value; } @Override From d0a51fe1e0f79ce8a202666f8113124eb8a065c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 106/111] Reuse TopLevel.NativeErrors enum for native error names --- src/org/mozilla/javascript/NativeGlobal.java | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/org/mozilla/javascript/NativeGlobal.java b/src/org/mozilla/javascript/NativeGlobal.java index 3fad2a9f8a..34d4da7f02 100644 --- a/src/org/mozilla/javascript/NativeGlobal.java +++ b/src/org/mozilla/javascript/NativeGlobal.java @@ -129,23 +129,12 @@ public static void init(Context cx, Scriptable scope, boolean sealed) { scope, "undefined", Undefined.instance, READONLY|DONTENUM|PERMANENT, false); - String[] errorMethods = { - "EvalError", - "RangeError", - "ReferenceError", - "SyntaxError", - "TypeError", - "URIError", - "InternalError", - "JavaException" - }; - /* Each error constructor gets its own Error object as a prototype, with the 'name' property set to the name of the error. */ - for (int i = 0; i < errorMethods.length; i++) { - String name = errorMethods[i]; + for (TopLevel.NativeErrors error : TopLevel.NativeErrors.values()) { + String name = error.name(); ScriptableObject errorProto = (ScriptableObject) ScriptRuntime.newBuiltinObject(cx, scope, TopLevel.Builtins.Error, From f265ab08e91413d66d7e83723c2b2c4e59112626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 107/111] Add 'throw' flag to ConstProperties just like in Scriptable to control strict-mode behaviour --- .../mozilla/javascript/ConstProperties.java | 7 +- src/org/mozilla/javascript/Interpreter.java | 2 +- .../mozilla/javascript/ScriptableObject.java | 82 +++++++++++++++++-- 3 files changed, 80 insertions(+), 11 deletions(-) diff --git a/src/org/mozilla/javascript/ConstProperties.java b/src/org/mozilla/javascript/ConstProperties.java index 860db792fa..4039e5ed75 100644 --- a/src/org/mozilla/javascript/ConstProperties.java +++ b/src/org/mozilla/javascript/ConstProperties.java @@ -83,12 +83,14 @@ public interface ConstProperties { * @param name the name of the property * @param start the object whose property is being set * @param value value to set the property to + * @param checked controls error handling * @see org.mozilla.javascript.Scriptable#has(String, Scriptable) * @see org.mozilla.javascript.Scriptable#get(String, Scriptable) * @see org.mozilla.javascript.ScriptableObject#putProperty(Scriptable, String, Object) * @see org.mozilla.javascript.Context#toObject(Object, Scriptable) */ - public void putConst(String name, Scriptable start, Object value); + public void putConst(String name, Scriptable start, Object value, + boolean checked); /** * Reserves a definition spot for a const. This will set up a definition @@ -96,8 +98,9 @@ public interface ConstProperties { * the start parameter is the same as for putConst. * @param name The name of the property. * @param start The object whose property is being reserved. + * @param checked controls error handling */ - public void defineConst(String name, Scriptable start); + public void defineConst(String name, Scriptable start, boolean checked); /** * Returns true if the named property is defined as a const on this object. diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index 302cce95f2..274c2472f8 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -1861,7 +1861,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, stringReg = frame.idata.argNames[indexReg]; if (frame.scope instanceof ConstProperties) { ConstProperties cp = (ConstProperties)frame.scope; - cp.putConst(stringReg, frame.scope, val); + cp.putConst(stringReg, frame.scope, val, frame.idata.isStrict); } else throw Kit.codeBug(); } diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 676b9aca80..85f49a4f4c 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -634,28 +634,57 @@ public void delete(int index, boolean checked) * @param start the object whose property is being set * @param value value to set the property to */ + @Deprecated public void putConst(String name, Scriptable start, Object value) + { + putConst(name, start, value); + } + + /** + * Sets the value of the named const property, creating it if need be. + * + * If the property was created using defineProperty, the + * appropriate setter method is called.

+ * + * If the property's attributes include READONLY, no action is + * taken. + * This method will actually set the property in the start + * object. + * + * @param name the name of the property + * @param start the object whose property is being set + * @param value value to set the property to + * @param checked controls error handling + */ + public void putConst(String name, Scriptable start, Object value, + boolean checked) { if (putConstImpl(name, 0, start, value, READONLY)) return; if (start == this) throw Kit.codeBug(); if (start instanceof ConstProperties) - ((ConstProperties)start).putConst(name, start, value); + ((ConstProperties)start).putConst(name, start, value, checked); else - // TODO: 'throw' flag? - start.put(name, start, value, false); + start.put(name, start, value, checked); } + @Deprecated public void defineConst(String name, Scriptable start) + { + defineConst(name, start, false); + } + + public void defineConst(String name, Scriptable start, boolean checked) { if (putConstImpl(name, 0, start, Undefined.instance, UNINITIALIZED_CONST)) return; if (start == this) throw Kit.codeBug(); if (start instanceof ConstProperties) - ((ConstProperties)start).defineConst(name, start); + ((ConstProperties)start).defineConst(name, start, checked); } + /** * Returns true if the named property is defined as a const on this object. * @param name @@ -1676,15 +1705,29 @@ public static void defineProperty(Scriptable destination, * defineProperty there, otherwise calls put in destination * ignoring attributes */ + @Deprecated public static void defineConstProperty(Scriptable destination, String propertyName) + { + defineConstProperty(destination, propertyName, false); + } + + /** + * Utility method to add properties to arbitrary Scriptable object. + * If destination is instance of ScriptableObject, calls + * defineProperty there, otherwise calls put in destination + * ignoring attributes + */ + public static void defineConstProperty(Scriptable destination, + String propertyName, + boolean checked) { if (destination instanceof ConstProperties) { ConstProperties cp = (ConstProperties)destination; - cp.defineConst(propertyName, destination); + cp.defineConst(propertyName, destination, checked); } else - // TODO: strict flag! - defineProperty(destination, propertyName, Undefined.instance, CONST); + defineProperty(destination, propertyName, Undefined.instance, CONST, + checked); } /** @@ -2292,13 +2335,36 @@ public static void putProperty(Scriptable obj, String name, Object value, * @param value any JavaScript value accepted by Scriptable.put * @since 1.5R2 */ + @Deprecated public static void putConstProperty(Scriptable obj, String name, Object value) + { + putConstProperty(obj, name, value, false); + } + + /** + * Puts a named property in an object or in an object in its prototype chain. + *

+ * Searches for the named property in the prototype chain. If it is found, + * the value of the property in obj is changed through a call + * to {@link Scriptable#put(String, Scriptable, Object)} on the + * prototype passing obj as the start argument. + * This allows the prototype to veto the property setting in case the + * prototype defines the property with [[ReadOnly]] attribute. If the + * property is not found, it is added in obj. + * @param obj a JavaScript object + * @param name a property name + * @param value any JavaScript value accepted by Scriptable.put + * @param checked controls error handling + * @since 1.7R4 + */ + public static void putConstProperty(Scriptable obj, String name, + Object value, boolean checked) { Scriptable base = getBase(obj, name); if (base == null) base = obj; if (base instanceof ConstProperties) - ((ConstProperties)base).putConst(name, obj, value); + ((ConstProperties)base).putConst(name, obj, value, checked); } /** From a06e513b9cecc95892ddb2be1d22704da6513463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 108/111] Next update for strict-mode compatible const properties --- src/org/mozilla/javascript/Interpreter.java | 3 ++- src/org/mozilla/javascript/ScriptRuntime.java | 24 ++++++++++--------- .../mozilla/javascript/ScriptableObject.java | 3 ++- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index 274c2472f8..37f3c9682f 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -1394,7 +1394,8 @@ private static Object interpretLoop(Context cx, CallFrame frame, if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); --stackTop; Scriptable lhs = (Scriptable)stack[stackTop]; - stack[stackTop] = ScriptRuntime.setConst(lhs, rhs, cx, stringReg); + stack[stackTop] = ScriptRuntime.setConst(lhs, rhs, cx, stringReg, + frame.idata.isStrict); continue Loop; } case Token.DELPROP : diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 05b87c001a..22c9556ab5 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -2326,31 +2326,32 @@ public static Object setName(Scriptable bound, Object value, public static Object strictSetName(Scriptable bound, Object value, Context cx, Scriptable scope, String id) { if (bound != null) { - // TODO: The LeftHandSide also may not be a reference to a - // data property with the attribute value {[[Writable]]:false}, - // to an accessor property with the attribute value - // {[[Put]]:undefined}, nor to a non-existent property of an - // object whose [[Extensible]] internal property has the value - // false. In these cases a TypeError exception is thrown (11.13.1). // TODO: we used to special-case XMLObject here, but putProperty // seems to work for E4X and we should optimize the common case ScriptableObject.putProperty(bound, id, value, true); return value; } else { + // TODO: error message! // See ES5 8.7.2 String msg = "Assignment to undefined \"" + id + "\" in strict mode"; throw constructError("ReferenceError", msg); } } + @Deprecated + public static Object setConst(Scriptable bound, Object value, + Context cx, String id) + { + return setConst(bound, value, cx, id, false); + } + public static Object setConst(Scriptable bound, Object value, - Context cx, String id) + Context cx, String id, boolean strict) { - // TODO: strict mode flag? if (bound instanceof XMLObject) { - bound.put(id, bound, value, false); + bound.put(id, bound, value, strict); } else { - ScriptableObject.putConstProperty(bound, id, value); + ScriptableObject.putConstProperty(bound, id, value, strict); } return value; } @@ -3837,7 +3838,8 @@ public static void initScript(NativeFunction funObj, Object thisObj, if (!evalScript) { // Global var definitions are supposed to be DONTDELETE if (isConst) - ScriptableObject.defineConstProperty(varScope, name); + ScriptableObject.defineConstProperty(varScope, name, + true); else ScriptableObject.defineProperty( varScope, name, Undefined.instance, diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 85f49a4f4c..01a5b6bbd8 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -637,7 +637,7 @@ public void delete(int index, boolean checked) @Deprecated public void putConst(String name, Scriptable start, Object value) { - putConst(name, start, value); + putConst(name, start, value, false); } /** @@ -1970,6 +1970,7 @@ public void defineFunctionProperties(String[] names, Class clazz, throw Context.reportRuntimeError2( "msg.method.not.found", name, clazz.getName()); } + // TODO: deprecated! FunctionObject f = new FunctionObject(name, m, this); defineProperty(name, f, attributes); } From d24bdd19324c4071016d3f72b2ab2e79ea5dc43a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 109/111] Add strict-mode checks to ScriptableObject#putConstImpl() --- src/org/mozilla/javascript/ScriptableObject.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 01a5b6bbd8..773c9ecccb 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -659,7 +659,7 @@ public void putConst(String name, Scriptable start, Object value) public void putConst(String name, Scriptable start, Object value, boolean checked) { - if (putConstImpl(name, 0, start, value, READONLY)) + if (putConstImpl(name, 0, start, value, READONLY, checked)) return; if (start == this) throw Kit.codeBug(); @@ -677,7 +677,8 @@ public void defineConst(String name, Scriptable start) public void defineConst(String name, Scriptable start, boolean checked) { - if (putConstImpl(name, 0, start, Undefined.instance, UNINITIALIZED_CONST)) + if (putConstImpl(name, 0, start, Undefined.instance, + UNINITIALIZED_CONST, checked)) return; if (start == this) throw Kit.codeBug(); @@ -2718,7 +2719,7 @@ private boolean putImpl(String name, int index, Scriptable start, * or this != start and a READONLY slot was found. */ private boolean putConstImpl(String name, int index, Scriptable start, - Object value, int constFlag) + Object value, int constFlag, boolean checked) { assert (constFlag != EMPTY); Slot slot; @@ -2730,6 +2731,8 @@ private boolean putConstImpl(String name, int index, Scriptable start, } else if (!isExtensible()) { slot = getSlot(name, index, SLOT_QUERY); if (slot == null) { + // TODO: error message + if (checked) { throw ScriptRuntime.typeError("[[Extensible]]"); } return true; } } else { @@ -2747,8 +2750,7 @@ private boolean putConstImpl(String name, int index, Scriptable start, } return true; } - // TODO: strict flag? - return slot.setValue(value, this, start, false); + return slot.setValue(value, this, start, checked); } private Slot findAttributeSlot(String name, int index, int accessType) From 9ce9228195705310253957aa94749e4f9ca5b348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 08:00:00 +0100 Subject: [PATCH 110/111] Add missing error messages --- src/org/mozilla/javascript/Arguments.java | 6 +-- src/org/mozilla/javascript/BoundFunction.java | 2 +- .../javascript/IdScriptableObject.java | 16 +++---- src/org/mozilla/javascript/NativeArray.java | 10 ++--- src/org/mozilla/javascript/NativeCall.java | 1 - .../mozilla/javascript/NativeFunction.java | 2 +- src/org/mozilla/javascript/NativeScript.java | 4 +- src/org/mozilla/javascript/NativeString.java | 4 +- src/org/mozilla/javascript/Parser.java | 4 +- src/org/mozilla/javascript/ScriptRuntime.java | 19 +++----- .../mozilla/javascript/ScriptableObject.java | 44 +++++++++++++------ .../javascript/resources/Messages.properties | 25 +++++++++-- 12 files changed, 80 insertions(+), 57 deletions(-) diff --git a/src/org/mozilla/javascript/Arguments.java b/src/org/mozilla/javascript/Arguments.java index 1ebb8eb5e2..b27427bb05 100644 --- a/src/org/mozilla/javascript/Arguments.java +++ b/src/org/mozilla/javascript/Arguments.java @@ -386,12 +386,8 @@ protected boolean defineOwnProperty(String name, PropertyDescriptor desc, int index = (int) d; boolean isMapped = (d == index && arg(index) != NOT_FOUND); - boolean allowed = super.defineOwnProperty(name, desc, false); + boolean allowed = super.defineOwnProperty(name, desc, checked); if (!allowed) { - if (checked) { - // TODO: error message - throw ScriptRuntime.typeError("[[DefineOwnProperty]]"); - } return false; } diff --git a/src/org/mozilla/javascript/BoundFunction.java b/src/org/mozilla/javascript/BoundFunction.java index 3f3bd0a630..f961b346bd 100644 --- a/src/org/mozilla/javascript/BoundFunction.java +++ b/src/org/mozilla/javascript/BoundFunction.java @@ -107,7 +107,7 @@ public void delete(String name, boolean checked) { // see comment in constructor if ("arguments".equals(name)) { if (checked) { - throw ScriptRuntime.typeError0("[[Permanent]]"); + throw ScriptRuntime.typeError1("msg.delete.permanent", name); } return; } diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index 205cdcf052..57fc102ae9 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -214,8 +214,9 @@ final void set(int id, Scriptable start, Object value, boolean checked) start.put(name, start, value, checked); } } else if (checked) { - // TODO: error message - throw ScriptRuntime.typeError("[[ReadOnly]]"); + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + String name = (String)valueArray[nameSlot]; + throw ScriptRuntime.typeError1("msg.modify.readonly", name); } } @@ -230,8 +231,9 @@ final void delete(int id, boolean checked) attributeArray[id - 1] = EMPTY; } } else if (checked) { - // TODO: error message - throw ScriptRuntime.typeError("[[Permanent]]"); + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + String name = (String)valueArray[nameSlot]; + throw ScriptRuntime.typeError1("msg.delete.permanent", name); } } @@ -408,8 +410,7 @@ public void put(String name, Scriptable start, Object value, boolean checked) start.put(name, start, value, checked); } } else if (checked) { - // TODO: error message - throw ScriptRuntime.typeError("[[ReadOnly]]"); + throw ScriptRuntime.typeError1("msg.modify.readonly", name); } return; } @@ -439,8 +440,7 @@ public void delete(String name, boolean checked) int id = (info & 0xFFFF); setInstanceIdValue(id, NOT_FOUND); } else if (checked) { - // TODO: error message - throw ScriptRuntime.typeError("[[Permanent]]"); + throw ScriptRuntime.typeError1("msg.delete.permanent", name); } return; } diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index bd01fdc08c..aad4e96536 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -605,9 +605,9 @@ private boolean defineOwnPropertyLength(PropertyDescriptor desc, newLenDesc.setWritable(true); } boolean succeeded = super.defineOwnProperty("length", newLenDesc, - checked); + checked); if (!succeeded) { - return false; + break reject; } // length updated to expected value? succeeded = (length == newLen); @@ -624,7 +624,7 @@ private boolean defineOwnPropertyLength(PropertyDescriptor desc, return true; } if (checked) { - throw ScriptRuntime.typeError("[[DefineOwnProperty]]"); + throw ScriptRuntime.typeError1("msg.modify.readonly", "length"); } return false; } @@ -650,7 +650,7 @@ private boolean defineOwnPropertyIndex(String name, } } } - boolean succeeded = super.defineOwnProperty(name, desc, false); + boolean succeeded = super.defineOwnProperty(name, desc, checked); if (!succeeded) { break reject; } @@ -662,7 +662,7 @@ private boolean defineOwnPropertyIndex(String name, return true; } if (checked) { - throw ScriptRuntime.typeError("[[DefineOwnProperty]]"); + throw ScriptRuntime.typeError1("msg.modify.readonly", "length"); } return false; } diff --git a/src/org/mozilla/javascript/NativeCall.java b/src/org/mozilla/javascript/NativeCall.java index bca58cbbca..21f69e6f18 100644 --- a/src/org/mozilla/javascript/NativeCall.java +++ b/src/org/mozilla/javascript/NativeCall.java @@ -69,7 +69,6 @@ static void init(Scriptable scope, boolean sealed) // leave prototype null this.originalArgs = (args == null) ? ScriptRuntime.emptyArgs : args; - // TODO: which strict-mode setting is required here? boolean strict = function.isStrict(); // initialize values of arguments diff --git a/src/org/mozilla/javascript/NativeFunction.java b/src/org/mozilla/javascript/NativeFunction.java index ff09348d96..79fd5e3e97 100644 --- a/src/org/mozilla/javascript/NativeFunction.java +++ b/src/org/mozilla/javascript/NativeFunction.java @@ -118,7 +118,7 @@ public void delete(String name, boolean checked) { // see comment in constructor if ("arguments".equals(name) && isStrict()) { if (checked) { - throw ScriptRuntime.typeError("[[Permanent]]"); + throw ScriptRuntime.typeError1("msg.delete.permanent", name); } return; } diff --git a/src/org/mozilla/javascript/NativeScript.java b/src/org/mozilla/javascript/NativeScript.java index 9bddc1f735..1b213c759a 100644 --- a/src/org/mozilla/javascript/NativeScript.java +++ b/src/org/mozilla/javascript/NativeScript.java @@ -191,10 +191,8 @@ private static Script compile(Context cx, String source) } ErrorReporter reporter; reporter = DefaultErrorReporter.forEval(cx.getErrorReporter()); - // TODO: which strict-mode setting needs to be applied here? - boolean strictMode = false; return cx.compileString(source, null, reporter, filename, - linep[0], null, strictMode); + linep[0], null, false); } // #string_id_map# diff --git a/src/org/mozilla/javascript/NativeString.java b/src/org/mozilla/javascript/NativeString.java index 0050f974b8..1a0c56f80c 100644 --- a/src/org/mozilla/javascript/NativeString.java +++ b/src/org/mozilla/javascript/NativeString.java @@ -530,8 +530,8 @@ public boolean has(int index, Scriptable start) { public void delete(int index, boolean checked) { if (0 <= index && index < string.length()) { if (checked) { - // TODO: error message - throw ScriptRuntime.typeError("[[Permanent]]"); + throw ScriptRuntime.typeError1("msg.delete.permanent", + ScriptRuntime.toString(index)); } return; } diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index b1a84928b5..a806827ad6 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -976,7 +976,7 @@ private boolean checkStrictAssignment(AstNode node) { private boolean checkStrictDelete(UnaryExpression expr) { AstNode op = removeParens(expr.getOperand()); if (op instanceof Name) { - reportError("msg.bad.id.strict", op.getString()); + reportError("msg.unqualified.delete.strict", op.getString()); return false; } return true; @@ -3429,7 +3429,7 @@ private ObjectProperty getterSetterProperty(int pos, AstNode propName, reportError("msg.bad.prop"); } if (fn.getParams().size() != (isGetter ? 0 : 1)) { - reportError("msg.bad.prop"); + reportError(isGetter ? "msg.bad.getter.arg" : "msg.bad.setter.arg"); } ObjectProperty pn = new ObjectProperty(pos); if (isGetter) { diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 22c9556ab5..ea7735326a 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -1568,24 +1568,21 @@ private static void putPrimitiveValue(Object base, Scriptable obj, ScriptableObject sobj = (ScriptableObject) obj; if (!sobj.$canPut(property)) { if (checked) { - // TODO: error message - throw typeError("[[ReadOnly]]"); + throw typeError1("msg.modify.readonly", property); } return; } PropertyDescriptor ownDesc = sobj.getOwnProperty(property); if (ownDesc != null && ownDesc.isDataDescriptor()) { if (checked) { - // TODO: error message - throw typeError("[[ReadOnly]]"); + throw typeError1("msg.modify.readonly", property); } return; } PropertyDescriptor desc = sobj.$getProperty(property); if (desc == null || !desc.isAccessorDescriptor()) { if (checked) { - // TODO: error message - throw typeError("[[ReadOnly]]"); + throw typeError1("msg.modify.readonly", property); } return; } @@ -2106,8 +2103,8 @@ public static Boolean delete(Object obj, Object id, Context cx, boolean isName, boolean strict) { if (isName && strict) { - // TODO: error message - throw constructError("SyntaxError", ""); + throw constructError("SyntaxError", + getMessage0("msg.unqualified.delete.strict")); } Scriptable sobj = toObjectOrNull(cx, obj); if (sobj == null) { @@ -2331,9 +2328,8 @@ public static Object strictSetName(Scriptable bound, Object value, ScriptableObject.putProperty(bound, id, value, true); return value; } else { - // TODO: error message! // See ES5 8.7.2 - String msg = "Assignment to undefined \"" + id + "\" in strict mode"; + String msg = getMessage1("msg.assign.undefined.strict", id); throw constructError("ReferenceError", msg); } } @@ -4124,8 +4120,7 @@ public static void initFunction(Context cx, Scriptable scope, global.defineOwnProperty(name, desc, true); } else if (desc.isAccessorDescriptor() || !desc.isEnumerable() || !desc.isWritable()) { - // TODO: error message - throw typeError("[[Bad Function Binding]]"); + throw typeError1("msg.redefine.permanent", name); } } // 10.5, step 5f: SetMutableBinding diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 773c9ecccb..3d2f980465 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -201,8 +201,7 @@ private void readObject(ObjectInputStream in) boolean setValue(Object value, Scriptable owner, Scriptable start, boolean checked) { if ((attributes & READONLY) != 0) { - // TODO: proper error message - if (checked) { throw ScriptRuntime.typeError("[[ReadOnly]]"); } + if (checked) { throw ScriptRuntime.typeError1("msg.modify.readonly", name); } return true; } if (owner == start) { @@ -289,8 +288,7 @@ boolean setValue(Object value, Scriptable owner, Scriptable start, } else { // ES5 setters are defaulted to 'Undefined.instance' instead // of 'null', so test here for non-existent setter - // TODO: proper error message - if (checked) { throw ScriptRuntime.typeError("[[Setter]]"); } + if (checked) { throw ScriptRuntime.typeError1("msg.set.prop.no.setter", name); } } return true; } @@ -2695,8 +2693,7 @@ private boolean putImpl(String name, int index, Scriptable start, } else if (!isExtensible) { slot = getSlot(name, index, SLOT_QUERY); if (slot == null) { - // TODO: error message - if (checked) { throw ScriptRuntime.typeError("[[Extensible]]"); } + if (checked) { throw ScriptRuntime.typeError0("msg.not.extensible"); } return true; } } else { @@ -2731,8 +2728,7 @@ private boolean putConstImpl(String name, int index, Scriptable start, } else if (!isExtensible()) { slot = getSlot(name, index, SLOT_QUERY); if (slot == null) { - // TODO: error message - if (checked) { throw ScriptRuntime.typeError("[[Extensible]]"); } + if (checked) { throw ScriptRuntime.typeError0("msg.not.extensible"); } return true; } } else { @@ -2939,8 +2935,8 @@ private synchronized void removeSlot(String name, int index, boolean checked) { return; } else if ((slot.getAttributes() & PERMANENT) != 0) { if (checked) { - // TODO: proper error message - throw ScriptRuntime.typeError("[[Permanent]]"); + name = (name != null ? name : Integer.toString(index)); + throw ScriptRuntime.typeError1("msg.delete.permanent", name); } return; } else { @@ -3331,7 +3327,7 @@ protected PropertyDescriptor getOwnProperty(String name) { /* 8.12.5, step 1. */ if (!$canPut(name)) { if (checked) { - throw ScriptRuntime.typeError("cannot [[Put]]:" + name); + throw ScriptRuntime.typeError1("msg.modify.readonly", name); } return; } @@ -3392,7 +3388,7 @@ protected PropertyDescriptor getOwnProperty(String name) { } /* 8.12.7, step 4. */ if (checked) { - throw ScriptRuntime.typeError("cannot [[Delete]]: " + name); + throw ScriptRuntime.typeError1("msg.delete.permanent", name); } /* 8.12.7, step 5. */ return false; @@ -3453,6 +3449,8 @@ protected PropertyDescriptor getOwnProperty(String name) { */ protected boolean defineOwnProperty(String name, PropertyDescriptor desc, boolean checked) { + String msg; + boolean useName = true; /* 8.12.9, step 1. */ PropertyDescriptor current = getOwnProperty(name); reject: { @@ -3462,6 +3460,8 @@ protected boolean defineOwnProperty(String name, PropertyDescriptor desc, updateOwnProperty(name, desc, current); return true; } else { + useName = false; + msg = "msg.not.extensible"; break reject; } } @@ -3487,11 +3487,13 @@ protected boolean defineOwnProperty(String name, PropertyDescriptor desc, if (!current.isConfigurable()) { /* 8.12.9, step 7a. */ if (desc.isConfigurable()) { + msg = "msg.change.configurable.false.to.true"; break reject; } /* 8.12.9, step 7b. */ if (desc.hasEnumerable() && desc.isEnumerable() != current.isEnumerable()) { + msg = "msg.change.enumerable.with.configurable.false"; break reject; } } @@ -3501,17 +3503,24 @@ protected boolean defineOwnProperty(String name, PropertyDescriptor desc, } else if (current.isDataDescriptor() != desc.isDataDescriptor()) { /* 8.12.9, step 8a. */ if (!current.isConfigurable()) { + if (current.isDataDescriptor()) { + msg = "msg.change.property.data.to.accessor.with.configurable.false"; + } else { + msg = "msg.change.property.accessor.to.data.with.configurable.false"; + } break reject; } } else if (current.isDataDescriptor() && desc.isDataDescriptor()) { if (!current.isConfigurable() && !current.isWritable()) { /* 8.12.9, step 10a i. */ if (desc.isWritable()) { + msg = "msg.change.writable.false.to.true.with.configurable.false"; break reject; } /* 8.12.9, step 10a ii. */ if (desc.hasValue() && !sameValue(desc.getValue(), current.getValue())) { + msg = "msg.change.value.with.writable.false"; break reject; } } @@ -3520,11 +3529,13 @@ protected boolean defineOwnProperty(String name, PropertyDescriptor desc, /* 8.12.9, step 11a i. */ if (desc.hasSetter() && !sameValue(desc.getSetter(), current.getSetter())) { + msg = "msg.change.setter.with.configurable.false"; break reject; } /* 8.12.9, step 11a ii. */ if (desc.hasGetter() && !sameValue(desc.getGetter(), current.getGetter())) { + msg = "msg.change.getter.with.configurable.false"; break reject; } } @@ -3534,9 +3545,14 @@ protected boolean defineOwnProperty(String name, PropertyDescriptor desc, /* 8.12.9, step 13. */ return true; } - /* 8.12.9, introductionary text */ + /* 8.12.9, introductory text */ if (checked) { - throw ScriptRuntime.typeError("[[DefineOwnProperty]]"); + if (useName) { + msg = ScriptRuntime.getMessage1(msg, name); + } else { + msg = ScriptRuntime.getMessage0(msg); + } + throw ScriptRuntime.typeError(msg); } return false; } diff --git a/src/org/mozilla/javascript/resources/Messages.properties b/src/org/mozilla/javascript/resources/Messages.properties index 20a4190d4e..2d33b7a0da 100644 --- a/src/org/mozilla/javascript/resources/Messages.properties +++ b/src/org/mozilla/javascript/resources/Messages.properties @@ -544,13 +544,23 @@ msg.bad.id.strict =\ "{0}" is not a valid identifier for this use in strict mode. msg.strict.function.stmt =\ - "in strict mode code, functions may be declared only at top level or immediately within another function" + in strict mode code, functions may be declared only at top level or immediately within another function msg.unnamed.function.stmt =\ - "function statement requires a name" + function statement requires a name msg.XML.not.available.strict =\ - "strict mode does not allow XML syntax" + strict mode does not allow XML syntax + +msg.bad.getter.arg =\ + getter functions must have no arguments + +msg.bad.setter.arg =\ + setter functions must have one argument + +msg.unqualified.delete.strict =\ + applying the 'delete' operator to an unqualified name is deprecated + # ScriptRuntime @@ -656,6 +666,9 @@ msg.in.not.object = \ msg.bad.radix = \ illegal radix {0}. +msg.assign.undefined.strict =\ + Assignment to undefined "{0}" in strict mode + # ScriptableObject msg.default.value =\ Cannot find default value for object. @@ -743,6 +756,12 @@ msg.change.property.accessor.to.data.with.configurable.false =\ msg.not.extensible =\ Cannot add properties to this object because extensible is false. +msg.delete.permanent =\ + Cannot delete non-configurable property: {0}. + +msg.redefine.permanent =\ + Cannot redefine non-configurable property: {0}. + # TokenStream msg.missing.exponent =\ missing exponent From 638d09dd36ef0f2a6a60d2b53ea1a400517a9bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 21 Mar 2012 18:23:00 +0100 Subject: [PATCH 111/111] IRFactory: no longer treat 'eval' in Token.GETPROP as Token.SPECIALCALL_EVAL Parser: remove merger marker, d'oh...! NativeRegExpCtor: implement setInstanceIdAttributes() so that `Object.freeze(RegExp)` works again arguments.doctest: update doctest to assert correct behaviour (double checked with current spidermonkey) object.defineproperty.doctest: update doctest to assert correct behaviour object.getownpropertydescriptor.doctest: update doctest to assert correct behaviour --- src/org/mozilla/javascript/IRFactory.java | 5 -- src/org/mozilla/javascript/Parser.java | 1 - .../javascript/regexp/NativeRegExpCtor.java | 49 ++++++++++++++++++- testsrc/doctests/arguments.doctest | 6 +-- .../doctests/object.defineproperty.doctest | 2 +- .../object.getownpropertydescriptor.doctest | 4 +- 6 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/org/mozilla/javascript/IRFactory.java b/src/org/mozilla/javascript/IRFactory.java index af4ca51896..e0d0512d76 100644 --- a/src/org/mozilla/javascript/IRFactory.java +++ b/src/org/mozilla/javascript/IRFactory.java @@ -1921,11 +1921,6 @@ private Node createCallOrNew(int nodeType, Node child) { } else if (name.equals("With")) { type = Node.SPECIALCALL_WITH; } - } else if (child.getType() == Token.GETPROP) { - String name = child.getLastChild().getString(); - if (name.equals("eval")) { - type = Node.SPECIALCALL_EVAL; - } } Node node = new Node(nodeType, child); if (type != Node.NON_SPECIALCALL) { diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index a806827ad6..2897e37c4c 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -644,7 +644,6 @@ private AstNode parseFunctionBody(FunctionNode fnNode) pn.setLineno(ts.lineno); try { -<<<<<<< HEAD if (isExpressionClosure) { ReturnStatement n = new ReturnStatement(ts.lineno); n.setReturnValue(assignExpr()); diff --git a/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java b/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java index 6e4cae04c8..3ce50f9afa 100644 --- a/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java +++ b/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java @@ -189,10 +189,16 @@ protected int findInstanceIdInfo(String s) { int attr; switch (id) { case Id_multiline: + attr = multilineAttr; + break; case Id_STAR: + attr = starAttr; + break; case Id_input: + attr = inputAttr; + break; case Id_UNDERSCORE: - attr = PERMANENT; + attr = underscoreAttr; break; default: attr = PERMANENT | READONLY; @@ -319,4 +325,45 @@ protected void setInstanceIdValue(int id, Object value) super.setInstanceIdValue(id, value); } + @Override + protected void setInstanceIdAttributes(int id, int attr) { + int shifted = id - super.getMaxInstanceId(); + switch (shifted) { + case Id_multiline: + multilineAttr = attr; + return; + case Id_STAR: + starAttr = attr; + return; + case Id_input: + inputAttr = attr; + return; + case Id_UNDERSCORE: + underscoreAttr = attr; + return; + + case Id_lastMatch: + case Id_AMPERSAND: + case Id_lastParen: + case Id_PLUS: + case Id_leftContext: + case Id_BACK_QUOTE: + case Id_rightContext: + case Id_QUOTE: + // non-configurable + non-writable + return; + default: + int substring_number = shifted - DOLLAR_ID_BASE - 1; + if (0 <= substring_number && substring_number <= 8) { + // non-configurable + non-writable + return; + } + } + super.setInstanceIdAttributes(id, attr); + } + + private int multilineAttr = PERMANENT; + private int starAttr = PERMANENT; + private int inputAttr = PERMANENT; + private int underscoreAttr = PERMANENT; } diff --git a/testsrc/doctests/arguments.doctest b/testsrc/doctests/arguments.doctest index a36f128426..16622b40c0 100644 --- a/testsrc/doctests/arguments.doctest +++ b/testsrc/doctests/arguments.doctest @@ -3,10 +3,8 @@ js> Object.getPrototypeOf(args) === Object.prototype true js> args.constructor === Object.prototype.constructor true -js> Object.getOwnPropertyDescriptor(args, 'constructor').toSource(); -({value:function Object() { [native code for Object.Object, arity=1] } -, writable:true, enumerable:false, configurable:true}) - +js> Object.getOwnPropertyDescriptor(args, "constructor") === void 0 +true js> args.toString() [object Arguments] diff --git a/testsrc/doctests/object.defineproperty.doctest b/testsrc/doctests/object.defineproperty.doctest index 1ca8c62c37..04788b8049 100644 --- a/testsrc/doctests/object.defineproperty.doctest +++ b/testsrc/doctests/object.defineproperty.doctest @@ -124,7 +124,7 @@ js> var obj = define(obj, 'a', {get : function() { return 4 }}); js> obj.a 4 js> describe(obj, 'a').toSource() -({enumerable:false, configurable:true, get:(function () {return 4;})}) +({get:(function () {return 4;}), set:undefined, enumerable:false, configurable:true}) js> // can change from accessor property to data property when configurable is true js> var obj = define({}, 'a', {get : function() { return 2 }, configurable:true}); diff --git a/testsrc/doctests/object.getownpropertydescriptor.doctest b/testsrc/doctests/object.getownpropertydescriptor.doctest index 2cac2aee8b..3a8f83e613 100644 --- a/testsrc/doctests/object.getownpropertydescriptor.doctest +++ b/testsrc/doctests/object.getownpropertydescriptor.doctest @@ -25,7 +25,7 @@ true js> desc.configurable true -js> var desc = Object.getOwnPropertyDescriptor({ get p() {}, set p() {} }, 'p'); +js> var desc = Object.getOwnPropertyDescriptor({ get p() {}, set p(x) {} }, 'p'); js> desc.value === undefined; true js> desc.writable === undefined; @@ -33,7 +33,7 @@ true js> desc.get.toSource() (function () {}) js> desc.set.toSource() -(function () {}) +(function (x) {}) js> desc.enumerable true js> desc.configurable