8007619: Add support for deprecated properties of RegExp constructor
authorsundar
Wed, 06 Feb 2013 17:56:12 +0530
changeset 16217 c5ac9be5c444
parent 16216 46ed48fd84d3
child 16218 87059b530a33
8007619: Add support for deprecated properties of RegExp constructor Reviewed-by: lagergren, hannesw
nashorn/src/jdk/nashorn/internal/objects/Global.java
nashorn/src/jdk/nashorn/internal/objects/NativeRegExp.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java
nashorn/test/script/basic/JDK-8007619.js
nashorn/test/script/basic/JDK-8007619.js.EXPECTED
--- a/nashorn/src/jdk/nashorn/internal/objects/Global.java	Wed Feb 06 10:31:58 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/Global.java	Wed Feb 06 17:56:12 2013 +0530
@@ -1351,6 +1351,13 @@
         final ScriptObject regExpProto = getRegExpPrototype();
         regExpProto.addBoundProperties(DEFAULT_REGEXP);
 
+        // add hook to support "deprecated" "static" properties of RegExp constructor object
+        final ScriptFunction handler = ScriptFunctionImpl.makeFunction(NO_SUCH_METHOD_NAME, NativeRegExp.REGEXP_STATICS_HANDLER);
+        builtinRegExp.addOwnProperty(NO_SUCH_PROPERTY_NAME, Attribute.NOT_ENUMERABLE, handler);
+
+        // add initial undefined "last successful match" property RegExp
+        builtinRegExp.addOwnProperty(NativeRegExp.LAST_REGEXP_MATCH, Attribute.NOT_ENUMERABLE, UNDEFINED);
+
         // Error stuff
         initErrorObjects();
 
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeRegExp.java	Wed Feb 06 10:31:58 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeRegExp.java	Wed Feb 06 17:56:12 2013 +0530
@@ -27,7 +27,10 @@
 
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
 
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -54,6 +57,8 @@
  */
 @ScriptClass("RegExp")
 public final class NativeRegExp extends ScriptObject {
+    static final MethodHandle REGEXP_STATICS_HANDLER = findOwnMH("regExpStaticsHandler", Object.class, Object.class, Object.class);
+
     /** ECMA 15.10.7.5 lastIndex property */
     @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
     public Object lastIndex;
@@ -75,6 +80,9 @@
 
     private BitVector groupsInNegativeLookahead;
 
+    // RegExp constructor object. Needed to support RegExp "static" properties,
+    private Object constructor;
+
     /*
     public NativeRegExp() {
         init();
@@ -420,10 +428,81 @@
         final RegExpMatch m = execInner(string);
         // the input string
         if (m == null) {
-            return null;
+            return setLastRegExpMatch(null);
         }
 
-        return new NativeRegExpExecResult(m);
+        return setLastRegExpMatch(new NativeRegExpExecResult(m));
+    }
+
+    // Name of the "last successful match" property of the RegExp constructor
+    static final String LAST_REGEXP_MATCH = "__last_regexp_match__";
+
+    /**
+     * Handles "static" properties of RegExp constructor. These are "deprecated"
+     * properties of RegExp constructor.
+     *
+     * @param self self object passed to this method
+     * @param name name of the property being searched
+     *
+     * @return value of the specified property or undefined if not found
+     */
+    public static Object regExpStaticsHandler(final Object self, final  Object name) {
+        final String propName = JSType.toString(name);
+        if (self instanceof ScriptObject) {
+            final ScriptObject sobj = (ScriptObject)self;
+            final Object value = sobj.get(LAST_REGEXP_MATCH);
+            if (! (value instanceof NativeRegExpExecResult)) {
+                return UNDEFINED;
+            }
+
+            // get the last match object
+            final NativeRegExpExecResult lastMatch = (NativeRegExpExecResult)value;
+
+            // look for $1... $9
+            if (propName.length() > 0 && propName.charAt(0) == '$') {
+                int index = 0;
+                try {
+                    index = Integer.parseInt(propName.substring(1));
+                } catch (final Exception ignored) {
+                    return UNDEFINED;
+                }
+
+                // index out of range
+                if (index < 1 && index > 9) {
+                    return UNDEFINED;
+                }
+
+                // retrieve indexed value from last match object.
+                return lastMatch.get(index);
+            }
+
+            // misc. "static" properties supported
+            switch (propName) {
+                case "input": {
+                    return lastMatch.input;
+                }
+
+                case "lastMatch": {
+                    return lastMatch.get(0);
+                }
+
+                case "lastParen": {
+                    final int len = ((Number)NativeRegExpExecResult.length(lastMatch)).intValue();
+                    return (len > 0)? lastMatch.get(len - 1) : UNDEFINED;
+                }
+            }
+        }
+
+        return UNDEFINED;
+    }
+
+    // Support for RegExp static properties. We set last successful match
+    // to the RegExp constructor object.
+    private Object setLastRegExpMatch(final Object match) {
+        if (constructor instanceof ScriptObject) {
+            ((ScriptObject)constructor).set(LAST_REGEXP_MATCH, match, isStrictContext());
+        }
+        return match;
     }
 
     /**
@@ -665,20 +744,15 @@
      * @return Index of match.
      */
     Object search(final String string) {
-        final Matcher matcher = pattern.matcher(string);
-
-        int start = 0;
-        if (global) {
-            start = getLastIndex();
+        final RegExpMatch m = execInner(string);
+        // the input string
+        if (m == null) {
+            setLastRegExpMatch(null);
+            return -1;
         }
 
-        start = matcher.find(start) ? matcher.start() : -1;
-
-        if (global) {
-            setLastIndex(start == -1? -1 : matcher.end());
-        }
-
-        return start;
+        setLastRegExpMatch(new NativeRegExpExecResult(m));
+        return m.getIndex();
     }
 
     /**
@@ -706,7 +780,10 @@
     }
 
     private void init() {
-        this.setProto(Global.instance().getRegExpPrototype());
+        final ScriptObject proto = Global.instance().getRegExpPrototype();
+        this.setProto(proto);
+        // retrieve constructor to support "static" properties of RegExp
+        this.constructor = PrototypeObject.getConstructor(proto);
     }
 
     private static NativeRegExp checkRegExp(final Object self) {
@@ -769,4 +846,7 @@
         this.groupsInNegativeLookahead = groupsInNegativeLookahead;
     }
 
+    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
+        return MH.findStatic(MethodHandles.publicLookup(), NativeRegExp.class, name, MH.type(rtype, types));
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java	Wed Feb 06 10:31:58 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java	Wed Feb 06 17:56:12 2013 +0530
@@ -89,10 +89,10 @@
 public abstract class ScriptObject extends PropertyListenerManager implements PropertyAccess {
 
     /** Search fall back routine name for "no such method" */
-    static final String NO_SUCH_METHOD_NAME   = "__noSuchMethod__";
+    public static final String NO_SUCH_METHOD_NAME   = "__noSuchMethod__";
 
     /** Search fall back routine name for "no such property" */
-    static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__";
+    public static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__";
 
     /** Per ScriptObject flag - is this a scope object? */
     public static final int IS_SCOPE       = 0b0000_0001;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8007619.js	Wed Feb 06 17:56:12 2013 +0530
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8007619: Add support for deprecated properties of RegExp constructor
+ *
+ * @test
+ * @run
+ */
+
+
+var emailPattern = /(\w+)@(\w+)\.(\w+)/g;
+var input= "Please send mail to foo@acme.com and bar@gov.in ASAP!";
+
+var match = emailPattern.exec(input);
+
+while (match != null) {
+    print("Match = " + match);
+    print("RegExp.lastMatch = " + RegExp.lastMatch);
+   
+    print("RegExp.$1 = " + RegExp.$1);
+    print("RegExp.$2 = " + RegExp.$2);
+    print("RegExp.$3 = " + RegExp.$3);
+ 
+    print("RegExp.lastParen = " + RegExp.lastParen)
+    print("RegExp.input = " + RegExp.input);
+
+    match = emailPattern.exec(input);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8007619.js.EXPECTED	Wed Feb 06 17:56:12 2013 +0530
@@ -0,0 +1,14 @@
+Match = foo@acme.com,foo,acme,com
+RegExp.lastMatch = foo@acme.com
+RegExp.$1 = foo
+RegExp.$2 = acme
+RegExp.$3 = com
+RegExp.lastParen = com
+RegExp.input = Please send mail to foo@acme.com and bar@gov.in ASAP!
+Match = bar@gov.in,bar,gov,in
+RegExp.lastMatch = bar@gov.in
+RegExp.$1 = bar
+RegExp.$2 = gov
+RegExp.$3 = in
+RegExp.lastParen = in
+RegExp.input = Please send mail to foo@acme.com and bar@gov.in ASAP!