diff --git a/src/org/mozilla/javascript/Arguments.java b/src/org/mozilla/javascript/Arguments.java index bfca2c4a52..5d89e3bcad 100644 --- a/src/org/mozilla/javascript/Arguments.java +++ b/src/org/mozilla/javascript/Arguments.java @@ -120,6 +120,10 @@ public Object get(int index, Scriptable start) private boolean sharedWithActivation(int index) { + Context cx = Context.getContext(); + if (cx.isStrictMode()) { + return false; + } NativeFunction f = activation.function; int definedCount = f.getParamCount(); if (index < definedCount) { @@ -148,6 +152,12 @@ public void put(int index, Scriptable start, Object value) } } + @Override + public void put(String name, Scriptable start, Object value) + { + super.put(name, start, value); + } + @Override public void delete(int index) { @@ -189,6 +199,13 @@ protected int findInstanceIdInfo(String s) break L0; } // #/generated# + Context cx = Context.getContext(); + if (cx.isStrictMode()) { + if (id == Id_callee || id == Id_caller) { + return super.findInstanceIdInfo(s); + } + } + if (id == 0) return super.findInstanceIdInfo(s); @@ -363,6 +380,27 @@ protected void defineOwnProperty(Context cx, Object id, } } + // ECMAScript2015 + // 9.4.4.6 CreateUnmappedArgumentsObject(argumentsList) + // 8. Perform DefinePropertyOrThrow(obj, "caller", PropertyDescriptor {[[Get]]: %ThrowTypeError%, + // [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}). + // 9. Perform DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Get]]: %ThrowTypeError%, + // [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}). + void defineAttributesForStrictMode() { + Context cx = Context.getContext(); + if (!cx.isStrictMode()) { + return; + } + setGetterOrSetter("caller", 0, new ThrowTypeError("caller"), true); + setGetterOrSetter("caller", 0, new ThrowTypeError("caller"), false); + setGetterOrSetter("callee", 0, new ThrowTypeError("callee"), true); + setGetterOrSetter("callee", 0, new ThrowTypeError("callee"), false); + setAttributes("caller", DONTENUM | PERMANENT); + setAttributes("callee", DONTENUM | PERMANENT); + callerObj = null; + calleeObj = null; + } + private static BaseFunction iteratorMethod = new BaseFunction() { @Override public Object call(Context cx, Scriptable scope, Scriptable thisObj, @@ -375,6 +413,19 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, } }; + private static class ThrowTypeError extends BaseFunction { + private String propertyName; + + ThrowTypeError(String propertyName) { + this.propertyName = propertyName; + } + + @Override + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + throw ScriptRuntime.typeError1("msg.arguments.not.access.strict", propertyName); + } + } + // 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 diff --git a/src/org/mozilla/javascript/CodeGenerator.java b/src/org/mozilla/javascript/CodeGenerator.java index 8fd909b35c..423bc38d07 100644 --- a/src/org/mozilla/javascript/CodeGenerator.java +++ b/src/org/mozilla/javascript/CodeGenerator.java @@ -71,10 +71,11 @@ public InterpreterData compile(CompilerEnvirons compilerEnv, } else { scriptOrFn = tree; } + itsData = new InterpreterData(compilerEnv.getLanguageVersion(), scriptOrFn.getSourceName(), encodedSource, - ((AstRoot)tree).isInStrictMode()); + scriptOrFn.isInStrictMode()); itsData.topLevel = true; if (returnFunction) { @@ -100,6 +101,9 @@ private void generateFunctionICode() addIcode(Icode_GENERATOR); addUint16(theFunction.getBaseLineno() & 0xFFFF); } + if (theFunction.isInStrictMode()) { + itsData.isStrict = true; + } generateICodeFromTree(theFunction.getLastChild()); } diff --git a/src/org/mozilla/javascript/Context.java b/src/org/mozilla/javascript/Context.java index e9afcbf073..484d4b73ce 100644 --- a/src/org/mozilla/javascript/Context.java +++ b/src/org/mozilla/javascript/Context.java @@ -1312,7 +1312,7 @@ public Object callFunctionWithContinuations(Callable function, // Annotate so we can check later to ensure no java code in // intervening frames isContinuationsTopCall = true; - return ScriptRuntime.doTopCall(function, this, scope, scope, args); + return ScriptRuntime.doTopCall(function, this, scope, scope, args, isTopLevelStrict); } /** @@ -2486,6 +2486,9 @@ private Object compileImpl(Scriptable scope, if (returnFunction) { p.calledByCompileFunction = true; } + if (isStrictMode()) { + p.setDefaultUseStrictDirective(true); + } AstRoot ast; if (sourceString != null) { ast = p.parse(sourceString, sourceName, lineno); @@ -2672,6 +2675,10 @@ public void removeActivationName(String name) activationNames.remove(name); } + public final boolean isStrictMode() { + return isTopLevelStrict || (currentActivationCall != null && currentActivationCall.isStrict); + } + private static String implementationVersion; private final ContextFactory factory; @@ -2741,4 +2748,6 @@ public void removeActivationName(String name) // Generate an observer count on compiled code public boolean generateObserverCount = false; + + boolean isTopLevelStrict; } diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index 0a11257b0a..ca1c153392 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -188,7 +188,15 @@ final void delete(int id) { ensureId(id); int attr = attributeArray[id - 1]; - if ((attr & PERMANENT) == 0) { + // non-configurable + if ((attr & PERMANENT) != 0) { + Context cx = Context.getContext(); + if (cx.isStrictMode()) { + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + String name = (String)valueArray[nameSlot]; + throw ScriptRuntime.typeError1("msg.delete.property.with.configurable.false", name); + } + } else { int valueSlot = (id - 1) * SLOT_SPAN; synchronized (this) { valueArray[valueSlot] = NOT_FOUND; @@ -394,7 +402,13 @@ public void delete(String name) // Let the super class to throw exceptions for sealed objects if (!isSealed()) { int attr = (info >>> 16); - if ((attr & PERMANENT) == 0) { + // non-configurable + if ((attr & PERMANENT) != 0) { + Context cx = Context.getContext(); + if (cx.isStrictMode()) { + throw ScriptRuntime.typeError1("msg.delete.property.with.configurable.false", name); + } + } else { int id = (info & 0xFFFF); setInstanceIdValue(id, NOT_FOUND); } diff --git a/src/org/mozilla/javascript/InterpretedFunction.java b/src/org/mozilla/javascript/InterpretedFunction.java index 92fb16a170..37b2e4dda3 100644 --- a/src/org/mozilla/javascript/InterpretedFunction.java +++ b/src/org/mozilla/javascript/InterpretedFunction.java @@ -75,7 +75,7 @@ static InterpretedFunction createFunction(Context cx,Scriptable scope, * Create function embedded in script or another function. */ static InterpretedFunction createFunction(Context cx, Scriptable scope, - InterpretedFunction parent, + InterpretedFunction parent, int index) { InterpretedFunction f = new InterpretedFunction(parent, index); @@ -104,7 +104,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (!ScriptRuntime.hasTopCall(cx)) { - return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args); + return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args, idata.isStrict); } return Interpreter.interpret(this, cx, scope, thisObj, args); } @@ -118,7 +118,7 @@ public Object exec(Context cx, Scriptable scope) if (!ScriptRuntime.hasTopCall(cx)) { // It will go through "call" path. but they are equivalent return ScriptRuntime.doTopCall( - this, cx, scope, scope, ScriptRuntime.emptyArgs); + this, cx, scope, scope, ScriptRuntime.emptyArgs, idata.isStrict); } return Interpreter.interpret( this, cx, scope, scope, ScriptRuntime.emptyArgs); diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index 521fa7c2c3..c591b1c8c1 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -853,7 +853,7 @@ public static Object restartContinuation(NativeContinuation c, Context cx, Scriptable scope, Object[] args) { if (!ScriptRuntime.hasTopCall(cx)) { - return ScriptRuntime.doTopCall(c, cx, scope, null, args); + return ScriptRuntime.doTopCall(c, cx, scope, null, args, cx.isTopLevelStrict); } Object arg; @@ -2759,9 +2759,9 @@ private static void initFrame(Context cx, Scriptable callerScope, if (useActivation) { if (idata.itsFunctionType == FunctionNode.ARROW_FUNCTION) { - scope = ScriptRuntime.createArrowFunctionActivation(fnOrScript, scope, args); + scope = ScriptRuntime.createArrowFunctionActivation(fnOrScript, scope, args, idata.isStrict); } else { - scope = ScriptRuntime.createFunctionActivation(fnOrScript, scope, args); + scope = ScriptRuntime.createFunctionActivation(fnOrScript, scope, args, idata.isStrict); } } } else { diff --git a/src/org/mozilla/javascript/InterpreterData.java b/src/org/mozilla/javascript/InterpreterData.java index 657bff4cf5..817d52c730 100644 --- a/src/org/mozilla/javascript/InterpreterData.java +++ b/src/org/mozilla/javascript/InterpreterData.java @@ -34,7 +34,7 @@ final class InterpreterData implements Serializable, DebuggableScript this.languageVersion = parent.languageVersion; this.itsSourceFile = parent.itsSourceFile; this.encodedSource = parent.encodedSource; - + this.isStrict = parent.isStrict; init(); } diff --git a/src/org/mozilla/javascript/NativeCall.java b/src/org/mozilla/javascript/NativeCall.java index f60fde03b8..3bca84b9bb 100644 --- a/src/org/mozilla/javascript/NativeCall.java +++ b/src/org/mozilla/javascript/NativeCall.java @@ -28,12 +28,7 @@ static void init(Scriptable scope, boolean sealed) NativeCall() { } - NativeCall(NativeFunction function, Scriptable scope, Object[] args) - { - this(function, scope, args, false); - } - - NativeCall(NativeFunction function, Scriptable scope, Object[] args, boolean isArrow) + NativeCall(NativeFunction function, Scriptable scope, Object[] args, boolean isArrow, boolean isStrict) { this.function = function; @@ -41,6 +36,7 @@ static void init(Scriptable scope, boolean sealed) // leave prototype null this.originalArgs = (args == null) ? ScriptRuntime.emptyArgs : args; + this.isStrict = isStrict; // initialize values of arguments int paramAndVarCount = function.getParamAndVarCount(); @@ -57,7 +53,8 @@ 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) && !isArrow) { - defineProperty("arguments", new Arguments(this), PERMANENT); + arguments = new Arguments(this); + defineProperty("arguments", arguments, PERMANENT); } if (paramAndVarCount != 0) { @@ -118,12 +115,20 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, throw new IllegalArgumentException(String.valueOf(id)); } + public void defineAttributesForArguments() { + if (arguments != null) { + arguments.defineAttributesForStrictMode(); + } + } + private static final int Id_constructor = 1, MAX_PROTOTYPE_ID = 1; NativeFunction function; Object[] originalArgs; + boolean isStrict; + private Arguments arguments; transient NativeCall parentActivationCall; } diff --git a/src/org/mozilla/javascript/NativeGenerator.java b/src/org/mozilla/javascript/NativeGenerator.java index a63a49b1c6..1cbc843f7e 100644 --- a/src/org/mozilla/javascript/NativeGenerator.java +++ b/src/org/mozilla/javascript/NativeGenerator.java @@ -72,27 +72,6 @@ public String getClassName() { return "Generator"; } - private static class CloseGeneratorAction implements ContextAction { - private NativeGenerator generator; - - CloseGeneratorAction(NativeGenerator generator) { - this.generator = generator; - } - - 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) { - return ((NativeGenerator)thisObj).resume(cx, scope, - GENERATOR_CLOSE, new GeneratorClosedException()); - } - }; - return ScriptRuntime.doTopCall(closeGenerator, cx, scope, - generator, null); - } - } - @Override protected void initPrototypeId(int id) { String s; diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java index 198f972842..2ca9c79675 100644 --- a/src/org/mozilla/javascript/Parser.java +++ b/src/org/mozilla/javascript/Parser.java @@ -93,6 +93,8 @@ public class Parser private String prevNameTokenString = ""; private int prevNameTokenLineno; + private boolean defaultUseStrictDirective; + // Exception to unwind private static class ParserException extends RuntimeException { @@ -549,8 +551,11 @@ private AstRoot parse() throws IOException boolean inDirectivePrologue = true; boolean savedStrictMode = inUseStrictDirective; - // TODO: eval code should get strict mode from invoking code - inUseStrictDirective = false; + + inUseStrictDirective = defaultUseStrictDirective; + if (inUseStrictDirective) { + root.setInStrictMode(true); + } try { for (;;) { @@ -621,7 +626,7 @@ private AstRoot parse() throws IOException return root; } - private AstNode parseFunctionBody(int type) + private AstNode parseFunctionBody(int type, FunctionNode fnNode) throws IOException { boolean isExpressionClosure = false; @@ -675,6 +680,10 @@ private AstNode parseFunctionBody(int type) inDirectivePrologue = false; } else if (directive.equals("use strict")) { inUseStrictDirective = true; + fnNode.setInStrictMode(true); + if (!savedStrictMode) { + setRequiresActivation(); + } } } break; @@ -829,7 +838,7 @@ private FunctionNode function(int type) PerFunctionVariables savedVars = new PerFunctionVariables(fnNode); try { parseFunctionParams(fnNode); - fnNode.setBody(parseFunctionBody(type)); + fnNode.setBody(parseFunctionBody(type, fnNode)); fnNode.setEncodedSourceBounds(functionSourceStart, ts.tokenEnd); fnNode.setLength(ts.tokenEnd - functionSourceStart); @@ -910,7 +919,7 @@ private AstNode arrowFunction(AstNode params) throws IOException { fnNode.putProp(Node.DESTRUCTURING_PARAMS, destructuringNode); } - fnNode.setBody(parseFunctionBody(FunctionNode.ARROW_FUNCTION)); + fnNode.setBody(parseFunctionBody(FunctionNode.ARROW_FUNCTION, fnNode)); fnNode.setEncodedSourceBounds(functionSourceStart, ts.tokenEnd); fnNode.setLength(ts.tokenEnd - functionSourceStart); } finally { @@ -4006,11 +4015,11 @@ protected Node simpleAssignment(Node left, Node right) { int nodeType = left.getType(); switch (nodeType) { case Token.NAME: + String name = ((Name) left).getIdentifier(); 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); @@ -4088,4 +4097,8 @@ private RuntimeException codeBug() + ", ts.tokenBeg=" + ts.tokenBeg + ", currentToken=" + currentToken); } + + public void setDefaultUseStrictDirective(boolean useStrict) { + defaultUseStrictDirective = useStrict; + } } diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 2eb4ffda91..0e6549af0a 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -3330,9 +3330,19 @@ public static Scriptable getTopCallScope(Context cx) return scope; } + /** + * @deprecated Use {@link #doTopCall(Callable, Context, Scriptable, Scriptable, Object[], boolean)} instead + */ public static Object doTopCall(Callable callable, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) + { + return doTopCall(callable, cx, scope, thisObj, args, cx.isTopLevelStrict); + } + + public static Object doTopCall(Callable callable, + Context cx, Scriptable scope, + Scriptable thisObj, Object[] args, boolean isTopLevelStrict) { if (scope == null) throw new IllegalArgumentException(); @@ -3341,6 +3351,8 @@ public static Object doTopCall(Callable callable, Object result; cx.topCallScope = ScriptableObject.getTopLevelScope(scope); cx.useDynamicScope = cx.hasFeature(Context.FEATURE_DYNAMIC_SCOPE); + boolean previousTopLevelStrict = cx.isTopLevelStrict; + cx.isTopLevelStrict = isTopLevelStrict; ContextFactory f = cx.getFactory(); try { result = f.doTopCall(callable, cx, scope, thisObj, args); @@ -3348,6 +3360,7 @@ public static Object doTopCall(Callable callable, cx.topCallScope = null; // Cleanup cached references cx.cachedXMLLib = null; + cx.isTopLevelStrict = previousTopLevelStrict; if (cx.currentActivationCall != null) { // Function should always call exitActivationFunction @@ -3433,18 +3446,31 @@ public static void initScript(NativeFunction funObj, Scriptable thisObj, } } + /** + * @deprecated Use {@link #createFunctionActivation(NativeFunction, Scriptable, Object[], boolean)} instead + */ + @Deprecated public static Scriptable createFunctionActivation(NativeFunction funObj, Scriptable scope, Object[] args) { - return new NativeCall(funObj, scope, args); + return createFunctionActivation(funObj, scope, args, false); + } + + public static Scriptable createFunctionActivation(NativeFunction funObj, + Scriptable scope, + Object[] args, + boolean isStrict) + { + return new NativeCall(funObj, scope, args, false, isStrict); } public static Scriptable createArrowFunctionActivation(NativeFunction funObj, Scriptable scope, - Object[] args) + Object[] args, + boolean isStrict) { - return new NativeCall(funObj, scope, args, true); + return new NativeCall(funObj, scope, args, true, isStrict); } public static void enterActivationFunction(Context cx, @@ -3455,6 +3481,7 @@ public static void enterActivationFunction(Context cx, NativeCall call = (NativeCall)scope; call.parentActivationCall = cx.currentActivationCall; cx.currentActivationCall = call; + call.defineAttributesForArguments(); } public static void exitActivationFunction(Context cx) diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 244c1112d8..c972dbbc3d 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -174,6 +174,10 @@ private void readObject(ObjectInputStream in) boolean setValue(Object value, Scriptable owner, Scriptable start) { if ((attributes & READONLY) != 0) { + Context cx = Context.getContext(); + if (cx.isStrictMode()) { + throw ScriptRuntime.typeError1("msg.modify.readonly", name); + } return true; } if (owner == start) { @@ -242,7 +246,9 @@ ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) { ScriptRuntime.setBuiltinProtoAndParent(desc, scope, TopLevel.Builtins.Object); desc.defineProperty("enumerable", (attr & DONTENUM) == 0, EMPTY); desc.defineProperty("configurable", (attr & PERMANENT) == 0, EMPTY); - desc.defineProperty("writable", (attr & READONLY) == 0, EMPTY); + if (getter == null && setter == null) { + desc.defineProperty("writable", (attr & READONLY) == 0, EMPTY); + } if (getter != null) desc.defineProperty("get", getter, EMPTY); if (setter != null) desc.defineProperty("set", setter, EMPTY); return desc; @@ -252,9 +258,11 @@ ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) { boolean setValue(Object value, Scriptable owner, Scriptable start) { if (setter == null) { if (getter != null) { - if (Context.getContext().hasFeature(Context.FEATURE_STRICT_MODE)) { + Context cx = Context.getContext(); + if (cx.isStrictMode() || // Based on TC39 ES3.1 Draft of 9-Feb-2009, 8.12.4, step 2, // we should throw a TypeError in this case. + cx.hasFeature(Context.FEATURE_STRICT_MODE)) { throw ScriptRuntime.typeError1("msg.set.prop.no.setter", name); } // Assignment to a property with only a getter defined. The @@ -2710,6 +2718,12 @@ private boolean putImpl(String name, int index, Scriptable start, { // This method is very hot (basically called on each assignment) // so we inline the extensible/sealed checks below. + if (!isExtensible) { + Context cx = Context.getContext(); + if (cx.isStrictMode()) { + throw ScriptRuntime.typeError0("msg.not.extensible"); + } + } Slot slot; if (this != start) { slot = getSlot(name, index, SLOT_QUERY); @@ -2744,6 +2758,12 @@ private boolean putConstImpl(String name, int index, Scriptable start, Object value, int constFlag) { assert (constFlag != EMPTY); + if (!isExtensible) { + Context cx = Context.getContext(); + if (cx.isStrictMode()) { + throw ScriptRuntime.typeError0("msg.not.extensible"); + } + } Slot slot; if (this != start) { slot = getSlot(name, index, SLOT_QUERY); @@ -2955,7 +2975,15 @@ private synchronized void removeSlot(String name, int index) { prev = slot; slot = slot.next; } - if (slot != null && (slot.getAttributes() & PERMANENT) == 0) { + if (slot != null) { + // non-configurable + if ((slot.getAttributes() & PERMANENT) != 0) { + Context cx = Context.getContext(); + if (cx.isStrictMode()) { + throw ScriptRuntime.typeError1("msg.delete.property.with.configurable.false", name); + } + return; + } count--; // remove slot from hash table if (prev == slot) { diff --git a/src/org/mozilla/javascript/ast/AstRoot.java b/src/org/mozilla/javascript/ast/AstRoot.java index 9a3e1d66f8..bb09040425 100644 --- a/src/org/mozilla/javascript/ast/AstRoot.java +++ b/src/org/mozilla/javascript/ast/AstRoot.java @@ -25,7 +25,6 @@ public class AstRoot extends ScriptNode { private SortedSet comments; - private boolean inStrictMode; { type = Token.SCRIPT; @@ -76,14 +75,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 0788d628ca..5841bb9986 100644 --- a/src/org/mozilla/javascript/ast/ScriptNode.java +++ b/src/org/mozilla/javascript/ast/ScriptNode.java @@ -36,6 +36,7 @@ public class ScriptNode extends Scope { private Object compilerData; private int tempNumber = 0; + private boolean inStrictMode; { // during parsing, a ScriptNode or FunctionNode's top scope is itself @@ -305,6 +306,14 @@ public String getNextTempName() { return "$" + tempNumber++; } + public void setInStrictMode(boolean inStrictMode) { + this.inStrictMode = inStrictMode; + } + + public boolean isInStrictMode() { + return inStrictMode; + } + @Override public void visit(NodeVisitor v) { if (v.visit(this)) { diff --git a/src/org/mozilla/javascript/optimizer/Codegen.java b/src/org/mozilla/javascript/optimizer/Codegen.java index 716f622004..3d3c8033b7 100644 --- a/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/src/org/mozilla/javascript/optimizer/Codegen.java @@ -264,6 +264,7 @@ private byte[] generateCode(String encodedSource) { boolean hasScript = (scriptOrFnNodes[0].getType() == Token.SCRIPT); boolean hasFunctions = (scriptOrFnNodes.length > 1 || !hasScript); + boolean isStrictMode = scriptOrFnNodes[0].isInStrictMode(); String sourceFile = null; if (compilerEnv.isGenerateDebugInfo()) { @@ -286,7 +287,7 @@ private byte[] generateCode(String encodedSource) generateExecute(cfw); } - generateCallMethod(cfw); + generateCallMethod(cfw, isStrictMode); generateResumeGenerator(cfw); generateNativeFunctionOverrides(cfw, encodedSource); @@ -465,7 +466,7 @@ private void generateResumeGenerator(ClassFileWriter cfw) cfw.stopMethod((short)6); } - private void generateCallMethod(ClassFileWriter cfw) + private void generateCallMethod(ClassFileWriter cfw, boolean isStrictMode) { cfw.startMethod("call", "(Lorg/mozilla/javascript/Context;" + @@ -492,6 +493,7 @@ private void generateCallMethod(ClassFileWriter cfw) cfw.addALoad(2); cfw.addALoad(3); cfw.addALoad(4); + cfw.addPush(isStrictMode); cfw.addInvoke(ByteCode.INVOKESTATIC, "org/mozilla/javascript/ScriptRuntime", "doTopCall", @@ -500,6 +502,7 @@ private void generateCallMethod(ClassFileWriter cfw) +"Lorg/mozilla/javascript/Scriptable;" +"Lorg/mozilla/javascript/Scriptable;" +"[Ljava/lang/Object;" + +"Z" +")Ljava/lang/Object;"); cfw.add(ByteCode.ARETURN); cfw.markLabel(nonTopCallLabel); @@ -1346,10 +1349,12 @@ private void generateGenerator() cfw.addALoad(funObjLocal); cfw.addALoad(variableObjectLocal); cfw.addALoad(argsLocal); + cfw.addPush(scriptOrFn.isInStrictMode()); addScriptRuntimeInvoke("createFunctionActivation", "(Lorg/mozilla/javascript/NativeFunction;" +"Lorg/mozilla/javascript/Scriptable;" +"[Ljava/lang/Object;" + +"Z" +")Lorg/mozilla/javascript/Scriptable;"); cfw.addAStore(variableObjectLocal); @@ -1627,10 +1632,12 @@ private void generatePrologue() cfw.addALoad(variableObjectLocal); cfw.addALoad(argsLocal); String methodName = isArrow ? "createArrowFunctionActivation" : "createFunctionActivation"; + cfw.addPush(scriptOrFn.isInStrictMode()); addScriptRuntimeInvoke(methodName, "(Lorg/mozilla/javascript/NativeFunction;" +"Lorg/mozilla/javascript/Scriptable;" +"[Ljava/lang/Object;" + +"Z" +")Lorg/mozilla/javascript/Scriptable;"); cfw.addAStore(variableObjectLocal); cfw.addALoad(contextLocal); diff --git a/src/org/mozilla/javascript/resources/Messages.properties b/src/org/mozilla/javascript/resources/Messages.properties index ddd4b348d2..1b13fbc09c 100644 --- a/src/org/mozilla/javascript/resources/Messages.properties +++ b/src/org/mozilla/javascript/resources/Messages.properties @@ -707,6 +707,9 @@ msg.change.property.accessor.to.data.with.configurable.false =\ msg.not.extensible =\ Cannot add properties to this object because extensible is false. +msg.delete.property.with.configurable.false =\ + Cannot delete "{0}" property because configurable is false. + # TokenStream msg.missing.exponent =\ missing exponent @@ -852,3 +855,7 @@ msg.first.arg.not.regexp=\ msg.arrowfunction.generator =\ arrow function can not become generator + +# Arguments +msg.arguments.not.access.strict =\ + Cannot access "{0}" property of the arguments object in strict mode. diff --git a/testsrc/doctests/object.defineproperty.doctest b/testsrc/doctests/object.defineproperty.doctest index 23657b6cdd..05a40ad240 100644 --- a/testsrc/doctests/object.defineproperty.doctest +++ b/testsrc/doctests/object.defineproperty.doctest @@ -128,7 +128,7 @@ js> var obj = define(obj, 'a', {get : function() { return 4 }}); js> obj.a 4 js> describe(obj, 'a').toSource() -({enumerable:false, configurable:true, writable:false, get:(function () {return 4;})}) +({enumerable:false, configurable:true, get:(function () {return 4;})}) 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 07e674f852..b0cac1b725 100644 --- a/testsrc/doctests/object.getownpropertydescriptor.doctest +++ b/testsrc/doctests/object.getownpropertydescriptor.doctest @@ -33,7 +33,6 @@ js> var desc = Object.getOwnPropertyDescriptor({ get p() {}, set p() {} }, 'p'); js> desc.value === undefined; true js> desc.writable -true js> desc.get.toSource() (function () {}) js> desc.set.toSource() diff --git a/testsrc/jstests/inside-strict-mode.js b/testsrc/jstests/inside-strict-mode.js new file mode 100644 index 0000000000..b8ff5e8c03 --- /dev/null +++ b/testsrc/jstests/inside-strict-mode.js @@ -0,0 +1,30 @@ +load("testsrc/assert.js"); + +assertThrows(function() { + 'use strict'; + return arguments.caller; +}, TypeError); + +assertThrows(function() { + 'use strict'; + return arguments.callee; +}, TypeError); + +assertEquals(undefined, function() { + return arguments.caller; +}()); + +function f1() { + return arguments.callee; +} + +assertEquals(f1, f1()); + +// A simple function is not required activation. +// But when it changes to strict mode, requires activation. +assertThrows(function() { + 'use strict'; + delete Math.LN2; +}, TypeError); + +"success"; diff --git a/testsrc/jstests/top-level-strict-mode.js b/testsrc/jstests/top-level-strict-mode.js new file mode 100644 index 0000000000..0f8402d16a --- /dev/null +++ b/testsrc/jstests/top-level-strict-mode.js @@ -0,0 +1,13 @@ +'use strict'; + +load("testsrc/assert.js"); + +assertThrows(function() { + return arguments.caller; +}, TypeError); + +assertThrows(function() { + return arguments.callee; +}, TypeError); + +"success"; diff --git a/testsrc/org/mozilla/javascript/tests/InsideStrictModeTest.java b/testsrc/org/mozilla/javascript/tests/InsideStrictModeTest.java new file mode 100644 index 0000000000..0341c4dd92 --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/InsideStrictModeTest.java @@ -0,0 +1,12 @@ +package org.mozilla.javascript.tests; + +import org.mozilla.javascript.drivers.RhinoTest; +import org.mozilla.javascript.drivers.ScriptTestsBase; + +@RhinoTest( + value = "testsrc/jstests/inside-strict-mode.js" +) +public class InsideStrictModeTest + extends ScriptTestsBase +{ +} diff --git a/testsrc/org/mozilla/javascript/tests/Test262SuiteTest.java b/testsrc/org/mozilla/javascript/tests/Test262SuiteTest.java index e835c8329a..44b5fc521b 100644 --- a/testsrc/org/mozilla/javascript/tests/Test262SuiteTest.java +++ b/testsrc/org/mozilla/javascript/tests/Test262SuiteTest.java @@ -11,7 +11,7 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.mozilla.javascript.Context; -import org.mozilla.javascript.EcmaError; +import org.mozilla.javascript.EvaluatorException; import org.mozilla.javascript.RhinoException; import org.mozilla.javascript.Script; import org.mozilla.javascript.Scriptable; @@ -103,14 +103,22 @@ private Object executeRhinoScript() { return result; } catch (RhinoException ex) { - if (errorType == EcmaErrorType.NONE || !(ex instanceof EcmaError)) { + if (errorType == EcmaErrorType.NONE) { fail(String.format("%s%n%s", ex.getMessage(), ex.getScriptStackTrace())); } else { if (errorType == EcmaErrorType.ANY) { // passed } else { - EcmaError ecmaError = (EcmaError)ex; - assertEquals(errorType.name(), ecmaError.getName()); + String exceptionName; + if (ex instanceof EvaluatorException) { + exceptionName = "SyntaxError"; + } else { + exceptionName = ex.details(); + if (exceptionName.contains(":")) { + exceptionName = exceptionName.substring(0, exceptionName.indexOf(":")); + } + } + assertEquals(ex.details(), errorType.name(), exceptionName); } } return null; diff --git a/testsrc/org/mozilla/javascript/tests/TopLevelStrictModeTest.java b/testsrc/org/mozilla/javascript/tests/TopLevelStrictModeTest.java new file mode 100644 index 0000000000..0f9d6d048d --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/TopLevelStrictModeTest.java @@ -0,0 +1,12 @@ +package org.mozilla.javascript.tests; + +import org.mozilla.javascript.drivers.RhinoTest; +import org.mozilla.javascript.drivers.ScriptTestsBase; + +@RhinoTest( + value = "testsrc/jstests/top-level-strict-mode.js" +) +public class TopLevelStrictModeTest + extends ScriptTestsBase +{ +} diff --git a/testsrc/test262.properties b/testsrc/test262.properties index e4dd877274..b9396ae4f6 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -16,7 +16,6 @@ built-ins/String language/statements/for-of ! Array.prototype.entries.js ! Array.prototype.keys.js - ! arguments-unmapped-aliasing.js ! body-dstr-assign-error.js ! body-put-error.js ! for-of/break @@ -40,7 +39,6 @@ language/statements/for-of ! for-of/set ! for-of/throw ! for-of/yield - ! raw/zero-literal-segments.js built-ins/Array # incorrect length handling @@ -167,3 +165,48 @@ built-ins/Array ! concat/ ! entries/ ! keys/ + +language/arguments-object + ! arguments-object/mapped/Symbol.iterator.js + ! arguments-object/mapped/mapped-arguments-nonconfigurable-3.js + ! arguments-object/mapped/mapped-arguments-nonconfigurable-delete-1.js + ! arguments-object/mapped/mapped-arguments-nonconfigurable-delete-2.js + ! arguments-object/mapped/mapped-arguments-nonconfigurable-delete-3.js + ! arguments-object/mapped/mapped-arguments-nonconfigurable-delete-4.js + ! arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-1.js + ! arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-2.js + ! arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-3.js + ! arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-4.js + ! arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-5.js + ! arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-1.js + ! arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-2.js + ! arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-3.js + ! arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-4.js + ! arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-1.js + ! arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-2.js + ! arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-3.js + ! arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-4.js + ! arguments-object/unmapped/Symbol.iterator.js + +language/types/reference + ! S8.7.2_A1_T1.js + ! S8.7.2_A1_T2.js + +built-ins/Object/defineProperties/15.2.3.7-6-a-26.js +built-ins/Object/defineProperties/15.2.3.7-6-a-27.js +built-ins/Object/defineProperties/15.2.3.7-6-a-32.js +built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-230.js +language/expressions/delete/11.4.1-4-a-1-s.js +language/expressions/delete/11.4.1-4-a-2-s.js +language/expressions/delete/11.4.1-4-a-4-s.js +language/expressions/delete/11.4.1-4.a-2.js +language/expressions/delete/11.4.1-4.a-3-s.js +language/expressions/delete/11.4.1-4.a-3.js +language/expressions/delete/11.4.1-4.a-4.js +language/expressions/delete/11.4.1-4.a-8-s.js +language/expressions/delete/11.4.1-4.a-9-s.js +language/expressions/delete/11.4.1-4.a-9.js +language/expressions/delete/11.4.4-4.a-3-s.js +language/types/reference/8.7.2-3-s.js +language/types/reference/8.7.2-4-s.js +language/types/reference/8.7.2-7-s.js \ No newline at end of file