Wed, 06 Feb 2013 17:56:12 +0530
8007619: Add support for deprecated properties of RegExp constructor
Reviewed-by: lagergren, hannesw
1.1 --- a/src/jdk/nashorn/internal/objects/Global.java Wed Feb 06 10:31:58 2013 +0100 1.2 +++ b/src/jdk/nashorn/internal/objects/Global.java Wed Feb 06 17:56:12 2013 +0530 1.3 @@ -1351,6 +1351,13 @@ 1.4 final ScriptObject regExpProto = getRegExpPrototype(); 1.5 regExpProto.addBoundProperties(DEFAULT_REGEXP); 1.6 1.7 + // add hook to support "deprecated" "static" properties of RegExp constructor object 1.8 + final ScriptFunction handler = ScriptFunctionImpl.makeFunction(NO_SUCH_METHOD_NAME, NativeRegExp.REGEXP_STATICS_HANDLER); 1.9 + builtinRegExp.addOwnProperty(NO_SUCH_PROPERTY_NAME, Attribute.NOT_ENUMERABLE, handler); 1.10 + 1.11 + // add initial undefined "last successful match" property RegExp 1.12 + builtinRegExp.addOwnProperty(NativeRegExp.LAST_REGEXP_MATCH, Attribute.NOT_ENUMERABLE, UNDEFINED); 1.13 + 1.14 // Error stuff 1.15 initErrorObjects(); 1.16
2.1 --- a/src/jdk/nashorn/internal/objects/NativeRegExp.java Wed Feb 06 10:31:58 2013 +0100 2.2 +++ b/src/jdk/nashorn/internal/objects/NativeRegExp.java Wed Feb 06 17:56:12 2013 +0530 2.3 @@ -27,7 +27,10 @@ 2.4 2.5 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 2.6 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 2.7 +import static jdk.nashorn.internal.runtime.linker.Lookup.MH; 2.8 2.9 +import java.lang.invoke.MethodHandle; 2.10 +import java.lang.invoke.MethodHandles; 2.11 import java.util.ArrayList; 2.12 import java.util.Arrays; 2.13 import java.util.List; 2.14 @@ -54,6 +57,8 @@ 2.15 */ 2.16 @ScriptClass("RegExp") 2.17 public final class NativeRegExp extends ScriptObject { 2.18 + static final MethodHandle REGEXP_STATICS_HANDLER = findOwnMH("regExpStaticsHandler", Object.class, Object.class, Object.class); 2.19 + 2.20 /** ECMA 15.10.7.5 lastIndex property */ 2.21 @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) 2.22 public Object lastIndex; 2.23 @@ -75,6 +80,9 @@ 2.24 2.25 private BitVector groupsInNegativeLookahead; 2.26 2.27 + // RegExp constructor object. Needed to support RegExp "static" properties, 2.28 + private Object constructor; 2.29 + 2.30 /* 2.31 public NativeRegExp() { 2.32 init(); 2.33 @@ -420,10 +428,81 @@ 2.34 final RegExpMatch m = execInner(string); 2.35 // the input string 2.36 if (m == null) { 2.37 - return null; 2.38 + return setLastRegExpMatch(null); 2.39 } 2.40 2.41 - return new NativeRegExpExecResult(m); 2.42 + return setLastRegExpMatch(new NativeRegExpExecResult(m)); 2.43 + } 2.44 + 2.45 + // Name of the "last successful match" property of the RegExp constructor 2.46 + static final String LAST_REGEXP_MATCH = "__last_regexp_match__"; 2.47 + 2.48 + /** 2.49 + * Handles "static" properties of RegExp constructor. These are "deprecated" 2.50 + * properties of RegExp constructor. 2.51 + * 2.52 + * @param self self object passed to this method 2.53 + * @param name name of the property being searched 2.54 + * 2.55 + * @return value of the specified property or undefined if not found 2.56 + */ 2.57 + public static Object regExpStaticsHandler(final Object self, final Object name) { 2.58 + final String propName = JSType.toString(name); 2.59 + if (self instanceof ScriptObject) { 2.60 + final ScriptObject sobj = (ScriptObject)self; 2.61 + final Object value = sobj.get(LAST_REGEXP_MATCH); 2.62 + if (! (value instanceof NativeRegExpExecResult)) { 2.63 + return UNDEFINED; 2.64 + } 2.65 + 2.66 + // get the last match object 2.67 + final NativeRegExpExecResult lastMatch = (NativeRegExpExecResult)value; 2.68 + 2.69 + // look for $1... $9 2.70 + if (propName.length() > 0 && propName.charAt(0) == '$') { 2.71 + int index = 0; 2.72 + try { 2.73 + index = Integer.parseInt(propName.substring(1)); 2.74 + } catch (final Exception ignored) { 2.75 + return UNDEFINED; 2.76 + } 2.77 + 2.78 + // index out of range 2.79 + if (index < 1 && index > 9) { 2.80 + return UNDEFINED; 2.81 + } 2.82 + 2.83 + // retrieve indexed value from last match object. 2.84 + return lastMatch.get(index); 2.85 + } 2.86 + 2.87 + // misc. "static" properties supported 2.88 + switch (propName) { 2.89 + case "input": { 2.90 + return lastMatch.input; 2.91 + } 2.92 + 2.93 + case "lastMatch": { 2.94 + return lastMatch.get(0); 2.95 + } 2.96 + 2.97 + case "lastParen": { 2.98 + final int len = ((Number)NativeRegExpExecResult.length(lastMatch)).intValue(); 2.99 + return (len > 0)? lastMatch.get(len - 1) : UNDEFINED; 2.100 + } 2.101 + } 2.102 + } 2.103 + 2.104 + return UNDEFINED; 2.105 + } 2.106 + 2.107 + // Support for RegExp static properties. We set last successful match 2.108 + // to the RegExp constructor object. 2.109 + private Object setLastRegExpMatch(final Object match) { 2.110 + if (constructor instanceof ScriptObject) { 2.111 + ((ScriptObject)constructor).set(LAST_REGEXP_MATCH, match, isStrictContext()); 2.112 + } 2.113 + return match; 2.114 } 2.115 2.116 /** 2.117 @@ -665,20 +744,15 @@ 2.118 * @return Index of match. 2.119 */ 2.120 Object search(final String string) { 2.121 - final Matcher matcher = pattern.matcher(string); 2.122 - 2.123 - int start = 0; 2.124 - if (global) { 2.125 - start = getLastIndex(); 2.126 + final RegExpMatch m = execInner(string); 2.127 + // the input string 2.128 + if (m == null) { 2.129 + setLastRegExpMatch(null); 2.130 + return -1; 2.131 } 2.132 2.133 - start = matcher.find(start) ? matcher.start() : -1; 2.134 - 2.135 - if (global) { 2.136 - setLastIndex(start == -1? -1 : matcher.end()); 2.137 - } 2.138 - 2.139 - return start; 2.140 + setLastRegExpMatch(new NativeRegExpExecResult(m)); 2.141 + return m.getIndex(); 2.142 } 2.143 2.144 /** 2.145 @@ -706,7 +780,10 @@ 2.146 } 2.147 2.148 private void init() { 2.149 - this.setProto(Global.instance().getRegExpPrototype()); 2.150 + final ScriptObject proto = Global.instance().getRegExpPrototype(); 2.151 + this.setProto(proto); 2.152 + // retrieve constructor to support "static" properties of RegExp 2.153 + this.constructor = PrototypeObject.getConstructor(proto); 2.154 } 2.155 2.156 private static NativeRegExp checkRegExp(final Object self) { 2.157 @@ -769,4 +846,7 @@ 2.158 this.groupsInNegativeLookahead = groupsInNegativeLookahead; 2.159 } 2.160 2.161 + private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 2.162 + return MH.findStatic(MethodHandles.publicLookup(), NativeRegExp.class, name, MH.type(rtype, types)); 2.163 + } 2.164 }
3.1 --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java Wed Feb 06 10:31:58 2013 +0100 3.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java Wed Feb 06 17:56:12 2013 +0530 3.3 @@ -89,10 +89,10 @@ 3.4 public abstract class ScriptObject extends PropertyListenerManager implements PropertyAccess { 3.5 3.6 /** Search fall back routine name for "no such method" */ 3.7 - static final String NO_SUCH_METHOD_NAME = "__noSuchMethod__"; 3.8 + public static final String NO_SUCH_METHOD_NAME = "__noSuchMethod__"; 3.9 3.10 /** Search fall back routine name for "no such property" */ 3.11 - static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__"; 3.12 + public static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__"; 3.13 3.14 /** Per ScriptObject flag - is this a scope object? */ 3.15 public static final int IS_SCOPE = 0b0000_0001;
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/script/basic/JDK-8007619.js Wed Feb 06 17:56:12 2013 +0530 4.3 @@ -0,0 +1,49 @@ 4.4 +/* 4.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.7 + * 4.8 + * This code is free software; you can redistribute it and/or modify it 4.9 + * under the terms of the GNU General Public License version 2 only, as 4.10 + * published by the Free Software Foundation. 4.11 + * 4.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 4.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.15 + * version 2 for more details (a copy is included in the LICENSE file that 4.16 + * accompanied this code). 4.17 + * 4.18 + * You should have received a copy of the GNU General Public License version 4.19 + * 2 along with this work; if not, write to the Free Software Foundation, 4.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.21 + * 4.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 4.23 + * or visit www.oracle.com if you need additional information or have any 4.24 + * questions. 4.25 + */ 4.26 + 4.27 +/** 4.28 + * JDK-8007619: Add support for deprecated properties of RegExp constructor 4.29 + * 4.30 + * @test 4.31 + * @run 4.32 + */ 4.33 + 4.34 + 4.35 +var emailPattern = /(\w+)@(\w+)\.(\w+)/g; 4.36 +var input= "Please send mail to foo@acme.com and bar@gov.in ASAP!"; 4.37 + 4.38 +var match = emailPattern.exec(input); 4.39 + 4.40 +while (match != null) { 4.41 + print("Match = " + match); 4.42 + print("RegExp.lastMatch = " + RegExp.lastMatch); 4.43 + 4.44 + print("RegExp.$1 = " + RegExp.$1); 4.45 + print("RegExp.$2 = " + RegExp.$2); 4.46 + print("RegExp.$3 = " + RegExp.$3); 4.47 + 4.48 + print("RegExp.lastParen = " + RegExp.lastParen) 4.49 + print("RegExp.input = " + RegExp.input); 4.50 + 4.51 + match = emailPattern.exec(input); 4.52 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/test/script/basic/JDK-8007619.js.EXPECTED Wed Feb 06 17:56:12 2013 +0530 5.3 @@ -0,0 +1,14 @@ 5.4 +Match = foo@acme.com,foo,acme,com 5.5 +RegExp.lastMatch = foo@acme.com 5.6 +RegExp.$1 = foo 5.7 +RegExp.$2 = acme 5.8 +RegExp.$3 = com 5.9 +RegExp.lastParen = com 5.10 +RegExp.input = Please send mail to foo@acme.com and bar@gov.in ASAP! 5.11 +Match = bar@gov.in,bar,gov,in 5.12 +RegExp.lastMatch = bar@gov.in 5.13 +RegExp.$1 = bar 5.14 +RegExp.$2 = gov 5.15 +RegExp.$3 = in 5.16 +RegExp.lastParen = in 5.17 +RegExp.input = Please send mail to foo@acme.com and bar@gov.in ASAP!