Merge
This commit is contained in:
commit
f0dc68f1aa
@ -29,14 +29,16 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import javax.script.*;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public class EvalFile {
|
||||
public static void main(String[] args) throws Exception {
|
||||
public static void main(final String[] args) throws Exception {
|
||||
// create a script engine manager
|
||||
ScriptEngineManager factory = new ScriptEngineManager();
|
||||
final ScriptEngineManager factory = new ScriptEngineManager();
|
||||
// create JavaScript engine
|
||||
ScriptEngine engine = factory.getEngineByName("nashorn");
|
||||
final ScriptEngine engine = factory.getEngineByName("nashorn");
|
||||
// evaluate JavaScript code from given file - specified by first argument
|
||||
engine.eval(new java.io.FileReader(args[0]));
|
||||
}
|
||||
|
@ -29,14 +29,16 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import javax.script.*;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public class EvalScript {
|
||||
public static void main(String[] args) throws Exception {
|
||||
public static void main(final String[] args) throws Exception {
|
||||
// create a script engine manager
|
||||
ScriptEngineManager factory = new ScriptEngineManager();
|
||||
final ScriptEngineManager factory = new ScriptEngineManager();
|
||||
// create a JavaScript engine
|
||||
ScriptEngine engine = factory.getEngineByName("nashorn");
|
||||
final ScriptEngine engine = factory.getEngineByName("nashorn");
|
||||
// evaluate JavaScript code from String
|
||||
engine.eval("print('Hello, World')");
|
||||
}
|
||||
|
@ -29,22 +29,25 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import javax.script.*;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public class InvokeScriptFunction {
|
||||
public static void main(String[] args) throws Exception {
|
||||
ScriptEngineManager manager = new ScriptEngineManager();
|
||||
ScriptEngine engine = manager.getEngineByName("nashorn");
|
||||
public static void main(final String[] args) throws Exception {
|
||||
final ScriptEngineManager manager = new ScriptEngineManager();
|
||||
final ScriptEngine engine = manager.getEngineByName("nashorn");
|
||||
|
||||
// JavaScript code in a String
|
||||
String script = "function hello(name) { print('Hello, ' + name); }";
|
||||
final String script = "function hello(name) { print('Hello, ' + name); }";
|
||||
// evaluate script
|
||||
engine.eval(script);
|
||||
|
||||
// javax.script.Invocable is an optional interface.
|
||||
// Check whether your script engine implements or not!
|
||||
// Note that the JavaScript engine implements Invocable interface.
|
||||
Invocable inv = (Invocable) engine;
|
||||
final Invocable inv = (Invocable) engine;
|
||||
|
||||
// invoke the global function named "hello"
|
||||
inv.invokeFunction("hello", "Scripting!!" );
|
||||
|
@ -29,26 +29,29 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import javax.script.*;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public class InvokeScriptMethod {
|
||||
public static void main(String[] args) throws Exception {
|
||||
ScriptEngineManager manager = new ScriptEngineManager();
|
||||
ScriptEngine engine = manager.getEngineByName("nashorn");
|
||||
public static void main(final String[] args) throws Exception {
|
||||
final ScriptEngineManager manager = new ScriptEngineManager();
|
||||
final ScriptEngine engine = manager.getEngineByName("nashorn");
|
||||
|
||||
// JavaScript code in a String. This code defines a script object 'obj'
|
||||
// with one method called 'hello'.
|
||||
String script = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }";
|
||||
final String script = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }";
|
||||
// evaluate script
|
||||
engine.eval(script);
|
||||
|
||||
// javax.script.Invocable is an optional interface.
|
||||
// Check whether your script engine implements or not!
|
||||
// Note that the JavaScript engine implements Invocable interface.
|
||||
Invocable inv = (Invocable) engine;
|
||||
final Invocable inv = (Invocable) engine;
|
||||
|
||||
// get script object on which we want to call the method
|
||||
Object obj = engine.get("obj");
|
||||
final Object obj = engine.get("obj");
|
||||
|
||||
// invoke the method named "hello" on the script object "obj"
|
||||
inv.invokeMethod(obj, "hello", "Script Method !!" );
|
||||
|
@ -29,12 +29,17 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import javax.script.*;
|
||||
import javax.script.Bindings;
|
||||
import javax.script.ScriptContext;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.SimpleScriptContext;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public class MultiScopes {
|
||||
public static void main(String[] args) throws Exception {
|
||||
ScriptEngineManager manager = new ScriptEngineManager();
|
||||
ScriptEngine engine = manager.getEngineByName("nashorn");
|
||||
public static void main(final String[] args) throws Exception {
|
||||
final ScriptEngineManager manager = new ScriptEngineManager();
|
||||
final ScriptEngine engine = manager.getEngineByName("nashorn");
|
||||
|
||||
engine.put("x", "hello");
|
||||
// print global variable "x"
|
||||
@ -42,9 +47,9 @@ public class MultiScopes {
|
||||
// the above line prints "hello"
|
||||
|
||||
// Now, pass a different script context
|
||||
ScriptContext newContext = new SimpleScriptContext();
|
||||
final ScriptContext newContext = new SimpleScriptContext();
|
||||
newContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
|
||||
Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
|
||||
final Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
|
||||
|
||||
// add new variable "x" to the new engineScope
|
||||
engineScope.put("x", "world");
|
||||
|
@ -29,28 +29,31 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import javax.script.*;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public class RunnableImpl {
|
||||
public static void main(String[] args) throws Exception {
|
||||
ScriptEngineManager manager = new ScriptEngineManager();
|
||||
ScriptEngine engine = manager.getEngineByName("nashorn");
|
||||
public static void main(final String[] args) throws Exception {
|
||||
final ScriptEngineManager manager = new ScriptEngineManager();
|
||||
final ScriptEngine engine = manager.getEngineByName("nashorn");
|
||||
|
||||
// JavaScript code in a String
|
||||
String script = "function run() { print('run called'); }";
|
||||
final String script = "function run() { print('run called'); }";
|
||||
|
||||
// evaluate script
|
||||
engine.eval(script);
|
||||
|
||||
Invocable inv = (Invocable) engine;
|
||||
final Invocable inv = (Invocable) engine;
|
||||
|
||||
// get Runnable interface object from engine. This interface methods
|
||||
// are implemented by script functions with the matching name.
|
||||
Runnable r = inv.getInterface(Runnable.class);
|
||||
final Runnable r = inv.getInterface(Runnable.class);
|
||||
|
||||
// start a new thread that runs the script implemented
|
||||
// runnable interface
|
||||
Thread th = new Thread(r);
|
||||
final Thread th = new Thread(r);
|
||||
th.start();
|
||||
th.join();
|
||||
}
|
||||
|
@ -29,31 +29,34 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import javax.script.*;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public class RunnableImplObject {
|
||||
public static void main(String[] args) throws Exception {
|
||||
ScriptEngineManager manager = new ScriptEngineManager();
|
||||
ScriptEngine engine = manager.getEngineByName("nashorn");
|
||||
public static void main(final String[] args) throws Exception {
|
||||
final ScriptEngineManager manager = new ScriptEngineManager();
|
||||
final ScriptEngine engine = manager.getEngineByName("nashorn");
|
||||
|
||||
// JavaScript code in a String
|
||||
String script = "var obj = new Object(); obj.run = function() { print('run method called'); }";
|
||||
final String script = "var obj = new Object(); obj.run = function() { print('run method called'); }";
|
||||
|
||||
// evaluate script
|
||||
engine.eval(script);
|
||||
|
||||
// get script object on which we want to implement the interface with
|
||||
Object obj = engine.get("obj");
|
||||
final Object obj = engine.get("obj");
|
||||
|
||||
Invocable inv = (Invocable) engine;
|
||||
final Invocable inv = (Invocable) engine;
|
||||
|
||||
// get Runnable interface object from engine. This interface methods
|
||||
// are implemented by script methods of object 'obj'
|
||||
Runnable r = inv.getInterface(obj, Runnable.class);
|
||||
final Runnable r = inv.getInterface(obj, Runnable.class);
|
||||
|
||||
// start a new thread that runs the script implemented
|
||||
// runnable interface
|
||||
Thread th = new Thread(r);
|
||||
final Thread th = new Thread(r);
|
||||
th.start();
|
||||
th.join();
|
||||
}
|
||||
|
@ -29,15 +29,17 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import javax.script.*;
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public class ScriptVars {
|
||||
public static void main(String[] args) throws Exception {
|
||||
ScriptEngineManager manager = new ScriptEngineManager();
|
||||
ScriptEngine engine = manager.getEngineByName("nashorn");
|
||||
public static void main(final String[] args) throws Exception {
|
||||
final ScriptEngineManager manager = new ScriptEngineManager();
|
||||
final ScriptEngine engine = manager.getEngineByName("nashorn");
|
||||
|
||||
File f = new File("test.txt");
|
||||
final File f = new File("test.txt");
|
||||
// expose File object as variable to script
|
||||
engine.put("file", f);
|
||||
|
||||
|
@ -97,6 +97,7 @@ import jdk.internal.dynalink.beans.BeansLinker;
|
||||
import jdk.internal.dynalink.linker.GuardingDynamicLinker;
|
||||
import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
|
||||
import jdk.internal.dynalink.support.AutoDiscovery;
|
||||
import jdk.internal.dynalink.support.BottomGuardingDynamicLinker;
|
||||
import jdk.internal.dynalink.support.ClassLoaderGetterContextProvider;
|
||||
@ -105,6 +106,7 @@ import jdk.internal.dynalink.support.CompositeTypeBasedGuardingDynamicLinker;
|
||||
import jdk.internal.dynalink.support.DefaultPrelinkFilter;
|
||||
import jdk.internal.dynalink.support.LinkerServicesImpl;
|
||||
import jdk.internal.dynalink.support.TypeConverterFactory;
|
||||
import jdk.internal.dynalink.support.TypeUtilities;
|
||||
|
||||
/**
|
||||
* A factory class for creating {@link DynamicLinker}s. The most usual dynamic linker is a linker that is a composition
|
||||
@ -115,7 +117,6 @@ import jdk.internal.dynalink.support.TypeConverterFactory;
|
||||
* @author Attila Szegedi
|
||||
*/
|
||||
public class DynamicLinkerFactory {
|
||||
|
||||
/**
|
||||
* Default value for {@link #setUnstableRelinkThreshold(int) unstable relink threshold}.
|
||||
*/
|
||||
@ -130,6 +131,7 @@ public class DynamicLinkerFactory {
|
||||
private boolean syncOnRelink = false;
|
||||
private int unstableRelinkThreshold = DEFAULT_UNSTABLE_RELINK_THRESHOLD;
|
||||
private GuardedInvocationFilter prelinkFilter;
|
||||
private MethodTypeConversionStrategy autoConversionStrategy;
|
||||
|
||||
/**
|
||||
* Sets the class loader for automatic discovery of available linkers. If not set explicitly, then the thread
|
||||
@ -258,6 +260,29 @@ public class DynamicLinkerFactory {
|
||||
this.prelinkFilter = prelinkFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an object representing the conversion strategy for automatic type conversions. After
|
||||
* {@link TypeConverterFactory#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)} has
|
||||
* applied all custom conversions to a method handle, it still needs to effect
|
||||
* {@link TypeUtilities#isMethodInvocationConvertible(Class, Class) method invocation conversions} that
|
||||
* can usually be automatically applied as per
|
||||
* {@link java.lang.invoke.MethodHandle#asType(java.lang.invoke.MethodType)}.
|
||||
* However, sometimes language runtimes will want to customize even those conversions for their own call
|
||||
* sites. A typical example is allowing unboxing of null return values, which is by default prohibited by
|
||||
* ordinary {@code MethodHandles.asType}. In this case, a language runtime can install its own custom
|
||||
* automatic conversion strategy, that can deal with null values. Note that when the strategy's
|
||||
* {@link MethodTypeConversionStrategy#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)}
|
||||
* is invoked, the custom language conversions will already have been applied to the method handle, so by
|
||||
* design the difference between the handle's current method type and the desired final type will always
|
||||
* only be ones that can be subjected to method invocation conversions. The strategy also doesn't need to
|
||||
* invoke a final {@code MethodHandle.asType()} as the converter factory will do that as the final step.
|
||||
* @param autoConversionStrategy the strategy for applying method invocation conversions for the linker
|
||||
* created by this factory.
|
||||
*/
|
||||
public void setAutoConversionStrategy(final MethodTypeConversionStrategy autoConversionStrategy) {
|
||||
this.autoConversionStrategy = autoConversionStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers as well as
|
||||
* the pre-link filter.
|
||||
@ -324,8 +349,9 @@ public class DynamicLinkerFactory {
|
||||
prelinkFilter = new DefaultPrelinkFilter();
|
||||
}
|
||||
|
||||
return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters), composite),
|
||||
prelinkFilter, runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
|
||||
return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters,
|
||||
autoConversionStrategy), composite), prelinkFilter, runtimeContextArgCount, syncOnRelink,
|
||||
unstableRelinkThreshold);
|
||||
}
|
||||
|
||||
private static ClassLoader getThreadContextClassLoader() {
|
||||
|
@ -287,10 +287,21 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
*/
|
||||
private static SingleDynamicMethod createDynamicMethod(final AccessibleObject m) {
|
||||
if(CallerSensitiveDetector.isCallerSensitive(m)) {
|
||||
// Method has @CallerSensitive annotation
|
||||
return new CallerSensitiveDynamicMethod(m);
|
||||
}
|
||||
// Method has no @CallerSensitive annotation
|
||||
final MethodHandle mh;
|
||||
try {
|
||||
mh = unreflectSafely(m);
|
||||
} catch (final IllegalAccessError e) {
|
||||
// java.lang.invoke can in some case conservatively treat as caller sensitive methods that aren't
|
||||
// marked with the annotation. In this case, we'll fall back to treating it as caller sensitive.
|
||||
return new CallerSensitiveDynamicMethod(m);
|
||||
}
|
||||
// Proceed with non-caller sensitive
|
||||
final Member member = (Member)m;
|
||||
return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName(), m instanceof Constructor);
|
||||
return new SimpleDynamicMethod(mh, member.getDeclaringClass(), member.getName(), m instanceof Constructor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file, and Oracle licenses the original version of this file under the BSD
|
||||
* license:
|
||||
*/
|
||||
/*
|
||||
Copyright 2014 Attila Szegedi
|
||||
|
||||
Licensed under both the Apache License, Version 2.0 (the "Apache License")
|
||||
and the BSD License (the "BSD License"), with licensee being free to
|
||||
choose either of the two at their discretion.
|
||||
|
||||
You may not use this file except in compliance with either the Apache
|
||||
License or the BSD License.
|
||||
|
||||
If you choose to use this file in compliance with the Apache License, the
|
||||
following notice applies to you:
|
||||
|
||||
You may obtain a copy of the Apache License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied. See the License for the specific language governing
|
||||
permissions and limitations under the License.
|
||||
|
||||
If you choose to use this file in compliance with the BSD License, the
|
||||
following notice applies to you:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the names of
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package jdk.internal.dynalink.linker;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
/**
|
||||
* Interface for objects representing a strategy for converting a method handle to a new type.
|
||||
*/
|
||||
public interface MethodTypeConversionStrategy {
|
||||
/**
|
||||
* Converts a method handle to a new type.
|
||||
* @param target target method handle
|
||||
* @param newType new type
|
||||
* @return target converted to the new type.
|
||||
*/
|
||||
public MethodHandle asType(final MethodHandle target, final MethodType newType);
|
||||
}
|
@ -97,6 +97,7 @@ import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.GuardedTypeConversion;
|
||||
import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
|
||||
import jdk.internal.dynalink.linker.LinkerServices;
|
||||
import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
|
||||
|
||||
/**
|
||||
* A factory for type converters. This class is the main implementation behind the
|
||||
@ -109,6 +110,7 @@ public class TypeConverterFactory {
|
||||
|
||||
private final GuardingTypeConverterFactory[] factories;
|
||||
private final ConversionComparator[] comparators;
|
||||
private final MethodTypeConversionStrategy autoConversionStrategy;
|
||||
|
||||
private final ClassValue<ClassMap<MethodHandle>> converterMap = new ClassValue<ClassMap<MethodHandle>>() {
|
||||
@Override
|
||||
@ -177,8 +179,24 @@ public class TypeConverterFactory {
|
||||
* Creates a new type converter factory from the available {@link GuardingTypeConverterFactory} instances.
|
||||
*
|
||||
* @param factories the {@link GuardingTypeConverterFactory} instances to compose.
|
||||
* @param autoConversionStrategy conversion strategy for automatic type conversions. After
|
||||
* {@link #asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)} has applied all custom
|
||||
* conversions to a method handle, it still needs to effect
|
||||
* {@link TypeUtilities#isMethodInvocationConvertible(Class, Class) method invocation conversions} that
|
||||
* can usually be automatically applied as per
|
||||
* {@link java.lang.invoke.MethodHandle#asType(java.lang.invoke.MethodType)}.
|
||||
* However, sometimes language runtimes will want to customize even those conversions for their own call
|
||||
* sites. A typical example is allowing unboxing of null return values, which is by default prohibited by
|
||||
* ordinary {@code MethodHandles.asType}. In this case, a language runtime can install its own custom
|
||||
* automatic conversion strategy, that can deal with null values. Note that when the strategy's
|
||||
* {@link MethodTypeConversionStrategy#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)}
|
||||
* is invoked, the custom language conversions will already have been applied to the method handle, so by
|
||||
* design the difference between the handle's current method type and the desired final type will always
|
||||
* only be ones that can be subjected to method invocation conversions. Can be null, in which case no
|
||||
* custom strategy is employed.
|
||||
*/
|
||||
public TypeConverterFactory(final Iterable<? extends GuardingTypeConverterFactory> factories) {
|
||||
public TypeConverterFactory(final Iterable<? extends GuardingTypeConverterFactory> factories,
|
||||
final MethodTypeConversionStrategy autoConversionStrategy) {
|
||||
final List<GuardingTypeConverterFactory> l = new LinkedList<>();
|
||||
final List<ConversionComparator> c = new LinkedList<>();
|
||||
for(final GuardingTypeConverterFactory factory: factories) {
|
||||
@ -189,20 +207,24 @@ public class TypeConverterFactory {
|
||||
}
|
||||
this.factories = l.toArray(new GuardingTypeConverterFactory[l.size()]);
|
||||
this.comparators = c.toArray(new ConversionComparator[c.size()]);
|
||||
|
||||
this.autoConversionStrategy = autoConversionStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link MethodHandle#asType(MethodType)} except it also hooks in method handles produced by
|
||||
* {@link GuardingTypeConverterFactory} implementations, providing for language-specific type coercing of
|
||||
* parameters. It will apply {@link MethodHandle#asType(MethodType)} for all primitive-to-primitive,
|
||||
* wrapper-to-primitive, primitive-to-wrapper conversions as well as for all upcasts. For all other conversions,
|
||||
* it'll insert {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
|
||||
* provided by {@link GuardingTypeConverterFactory} implementations.
|
||||
* parameters. For all conversions that are not a JLS method invocation conversion it'll insert
|
||||
* {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
|
||||
* provided by {@link GuardingTypeConverterFactory} implementations. For the remaining JLS method invocation
|
||||
* conversions, it will invoke {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)} first
|
||||
* if an automatic conversion strategy was specified in the
|
||||
* {@link #TypeConverterFactory(Iterable, MethodTypeConversionStrategy) constructor}, and finally apply
|
||||
* {@link MethodHandle#asType(MethodType)} for any remaining conversions.
|
||||
*
|
||||
* @param handle target method handle
|
||||
* @param fromType the types of source arguments
|
||||
* @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)} and
|
||||
* @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)},
|
||||
* {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)}, and
|
||||
* {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
|
||||
* {@link GuardingTypeConverterFactory} produced type converters as filters.
|
||||
*/
|
||||
@ -246,8 +268,12 @@ public class TypeConverterFactory {
|
||||
}
|
||||
}
|
||||
|
||||
// Take care of automatic conversions
|
||||
return newHandle.asType(fromType);
|
||||
// Give change to automatic conversion strategy, if one is present.
|
||||
final MethodHandle autoConvertedHandle =
|
||||
autoConversionStrategy != null ? autoConversionStrategy.asType(newHandle, fromType) : newHandle;
|
||||
|
||||
// Do a final asType for any conversions that remain.
|
||||
return autoConvertedHandle.asType(fromType);
|
||||
}
|
||||
|
||||
private static MethodHandle applyConverters(final MethodHandle handle, final int pos, final List<MethodHandle> converters) {
|
||||
|
@ -520,4 +520,13 @@ public class TypeUtilities {
|
||||
public static Class<?> getWrapperType(final Class<?> primitiveType) {
|
||||
return WRAPPER_TYPES.get(primitiveType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the passed type is a wrapper for a primitive type.
|
||||
* @param type the examined type
|
||||
* @return true if the passed type is a wrapper for a primitive type.
|
||||
*/
|
||||
public static boolean isWrapperType(final Class<?> type) {
|
||||
return PRIMITIVE_TYPES.containsKey(type);
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,8 @@ package jdk.nashorn.internal;
|
||||
|
||||
/**
|
||||
* Class that exposes the current state of asserts.
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
public final class AssertsEnabled {
|
||||
private static boolean assertsEnabled = false;
|
||||
static {
|
||||
|
@ -29,6 +29,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.EXPLODED_ARGUMENT_PREFIX;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
@ -93,6 +94,8 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
|
||||
|
||||
private final Deque<List<IdentNode>> explodedArguments = new ArrayDeque<>();
|
||||
|
||||
private final Deque<MethodType> callSiteTypes = new ArrayDeque<>();
|
||||
|
||||
private static final String ARGUMENTS = ARGUMENTS_VAR.symbolName();
|
||||
|
||||
/**
|
||||
@ -118,86 +121,108 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
|
||||
return context.getLogger(this.getClass());
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class TransformFailedException extends RuntimeException {
|
||||
TransformFailedException(final FunctionNode fn, final String message) {
|
||||
super(massageURL(fn.getSource().getURL()) + '.' + fn.getName() + " => " + message, null, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class AppliesFoundException extends RuntimeException {
|
||||
AppliesFoundException() {
|
||||
super("applies_found", null, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static final AppliesFoundException HAS_APPLIES = new AppliesFoundException();
|
||||
|
||||
private boolean hasApplies(final FunctionNode functionNode) {
|
||||
try {
|
||||
functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public boolean enterCallNode(final CallNode callNode) {
|
||||
if (isApply(callNode)) {
|
||||
throw HAS_APPLIES;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} catch (final AppliesFoundException e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
log.fine("There are no applies in ", DebugLogger.quote(functionNode.getName()), " - nothing to do.");
|
||||
return false; // no applies
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments may only be used as args to the apply. Everything else is disqualified
|
||||
* We cannot control arguments if they escape from the method and go into an unknown
|
||||
* scope, thus we are conservative and treat any access to arguments outside the
|
||||
* apply call as a case of "we cannot apply the optimization".
|
||||
*
|
||||
* @return true if arguments escape
|
||||
*/
|
||||
private boolean argumentsEscape(final FunctionNode functionNode) {
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
final UnsupportedOperationException uoe = new UnsupportedOperationException() {
|
||||
@Override
|
||||
public synchronized Throwable fillInStackTrace() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
private void checkValidTransform(final FunctionNode functionNode) {
|
||||
|
||||
final Set<Expression> argumentsFound = new HashSet<>();
|
||||
final Deque<Set<Expression>> stack = new ArrayDeque<>();
|
||||
//ensure that arguments is only passed as arg to apply
|
||||
try {
|
||||
functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
private boolean isCurrentArg(final Expression expr) {
|
||||
return !stack.isEmpty() && stack.peek().contains(expr); //args to current apply call
|
||||
}
|
||||
|
||||
private boolean isArguments(final Expression expr) {
|
||||
if (expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName())) {
|
||||
argumentsFound.add(expr);
|
||||
//ensure that arguments is only passed as arg to apply
|
||||
functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
|
||||
private boolean isCurrentArg(final Expression expr) {
|
||||
return !stack.isEmpty() && stack.peek().contains(expr); //args to current apply call
|
||||
}
|
||||
|
||||
private boolean isArguments(final Expression expr) {
|
||||
if (expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName())) {
|
||||
argumentsFound.add(expr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isParam(final String name) {
|
||||
for (final IdentNode param : functionNode.getParameters()) {
|
||||
if (param.getName().equals(name)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isParam(final String name) {
|
||||
for (final IdentNode param : functionNode.getParameters()) {
|
||||
if (param.getName().equals(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveIdentNode(final IdentNode identNode) {
|
||||
if (isParam(identNode.getName()) || isArguments(identNode) && !isCurrentArg(identNode)) {
|
||||
throw uoe; //avoid filling in stack trace
|
||||
}
|
||||
return identNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterCallNode(final CallNode callNode) {
|
||||
final Set<Expression> callArgs = new HashSet<>();
|
||||
if (isApply(callNode)) {
|
||||
final List<Expression> argList = callNode.getArgs();
|
||||
if (argList.size() != 2 || !isArguments(argList.get(argList.size() - 1))) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
callArgs.addAll(callNode.getArgs());
|
||||
}
|
||||
stack.push(callArgs);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveCallNode(final CallNode callNode) {
|
||||
stack.pop();
|
||||
return callNode;
|
||||
}
|
||||
});
|
||||
} catch (final UnsupportedOperationException e) {
|
||||
if (!argumentsFound.isEmpty()) {
|
||||
log.fine("'arguments' is used but escapes, or is reassigned in '" + functionNode.getName() + "'. Aborting");
|
||||
return false;
|
||||
}
|
||||
return true; //bad
|
||||
}
|
||||
|
||||
return false;
|
||||
@Override
|
||||
public Node leaveIdentNode(final IdentNode identNode) {
|
||||
if (isParam(identNode.getName())) {
|
||||
throw new TransformFailedException(lc.getCurrentFunction(), "parameter: " + identNode.getName());
|
||||
}
|
||||
// it's OK if 'argument' occurs as the current argument of an apply
|
||||
if (isArguments(identNode) && !isCurrentArg(identNode)) {
|
||||
throw new TransformFailedException(lc.getCurrentFunction(), "is 'arguments': " + identNode.getName());
|
||||
}
|
||||
return identNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterCallNode(final CallNode callNode) {
|
||||
final Set<Expression> callArgs = new HashSet<>();
|
||||
if (isApply(callNode)) {
|
||||
final List<Expression> argList = callNode.getArgs();
|
||||
if (argList.size() != 2 || !isArguments(argList.get(argList.size() - 1))) {
|
||||
throw new TransformFailedException(lc.getCurrentFunction(), "argument pattern not matched: " + argList);
|
||||
}
|
||||
callArgs.addAll(callNode.getArgs());
|
||||
}
|
||||
stack.push(callArgs);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveCallNode(final CallNode callNode) {
|
||||
stack.pop();
|
||||
return callNode;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -224,12 +249,14 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
|
||||
|
||||
final CallNode newCallNode = callNode.setArgs(newArgs).setIsApplyToCall();
|
||||
|
||||
log.fine("Transformed ",
|
||||
callNode,
|
||||
" from apply to call => ",
|
||||
newCallNode,
|
||||
" in ",
|
||||
DebugLogger.quote(lc.getCurrentFunction().getName()));
|
||||
if (log.isEnabled()) {
|
||||
log.fine("Transformed ",
|
||||
callNode,
|
||||
" from apply to call => ",
|
||||
newCallNode,
|
||||
" in ",
|
||||
DebugLogger.quote(lc.getCurrentFunction().getName()));
|
||||
}
|
||||
|
||||
return newCallNode;
|
||||
}
|
||||
@ -237,12 +264,12 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
|
||||
return callNode;
|
||||
}
|
||||
|
||||
private boolean pushExplodedArgs(final FunctionNode functionNode) {
|
||||
private void pushExplodedArgs(final FunctionNode functionNode) {
|
||||
int start = 0;
|
||||
|
||||
final MethodType actualCallSiteType = compiler.getCallSiteType(functionNode);
|
||||
if (actualCallSiteType == null) {
|
||||
return false;
|
||||
throw new TransformFailedException(lc.getCurrentFunction(), "No callsite type");
|
||||
}
|
||||
assert actualCallSiteType.parameterType(actualCallSiteType.parameterCount() - 1) != Object[].class : "error vararg callsite passed to apply2call " + functionNode.getName() + " " + actualCallSiteType;
|
||||
|
||||
@ -264,8 +291,8 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
|
||||
}
|
||||
}
|
||||
|
||||
callSiteTypes.push(actualCallSiteType);
|
||||
explodedArguments.push(newParams);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -288,11 +315,30 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
|
||||
return false;
|
||||
}
|
||||
|
||||
if (argumentsEscape(functionNode)) {
|
||||
if (!hasApplies(functionNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return pushExplodedArgs(functionNode);
|
||||
if (log.isEnabled()) {
|
||||
log.info("Trying to specialize apply to call in '",
|
||||
functionNode.getName(),
|
||||
"' params=",
|
||||
functionNode.getParameters(),
|
||||
" id=",
|
||||
functionNode.getId(),
|
||||
" source=",
|
||||
massageURL(functionNode.getSource().getURL()));
|
||||
}
|
||||
|
||||
try {
|
||||
checkValidTransform(functionNode);
|
||||
pushExplodedArgs(functionNode);
|
||||
} catch (final TransformFailedException e) {
|
||||
log.info("Failure: ", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -300,8 +346,8 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode0) {
|
||||
FunctionNode newFunctionNode = functionNode0;
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
FunctionNode newFunctionNode = functionNode;
|
||||
final String functionName = newFunctionNode.getName();
|
||||
|
||||
if (changed.contains(newFunctionNode.getId())) {
|
||||
@ -310,17 +356,18 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
|
||||
setParameters(lc, explodedArguments.peek());
|
||||
|
||||
if (log.isEnabled()) {
|
||||
log.info("Successfully specialized apply to call in '",
|
||||
log.info("Success: ",
|
||||
massageURL(newFunctionNode.getSource().getURL()),
|
||||
'.',
|
||||
functionName,
|
||||
" params=",
|
||||
explodedArguments.peek(),
|
||||
"' id=",
|
||||
newFunctionNode.getId(),
|
||||
" source=",
|
||||
newFunctionNode.getSource().getURL());
|
||||
" params=",
|
||||
callSiteTypes.peek());
|
||||
}
|
||||
}
|
||||
|
||||
callSiteTypes.pop();
|
||||
explodedArguments.pop();
|
||||
|
||||
return newFunctionNode.setState(lc, CompilationState.BUILTINS_TRANSFORMED);
|
||||
@ -331,4 +378,15 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
|
||||
return f instanceof AccessNode && "apply".equals(((AccessNode)f).getProperty());
|
||||
}
|
||||
|
||||
private static String massageURL(final URL url) {
|
||||
if (url == null) {
|
||||
return "<null>";
|
||||
}
|
||||
final String str = url.toString();
|
||||
final int slash = str.lastIndexOf('/');
|
||||
if (slash == -1) {
|
||||
return str;
|
||||
}
|
||||
return str.substring(slash + 1);
|
||||
}
|
||||
}
|
||||
|
@ -615,7 +615,6 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
static final TypeBounds UNBOUNDED = new TypeBounds(Type.UNKNOWN, Type.OBJECT);
|
||||
static final TypeBounds INT = exact(Type.INT);
|
||||
static final TypeBounds NUMBER = exact(Type.NUMBER);
|
||||
static final TypeBounds OBJECT = exact(Type.OBJECT);
|
||||
static final TypeBounds BOOLEAN = exact(Type.BOOLEAN);
|
||||
|
||||
@ -3569,7 +3568,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
operandBounds = new TypeBounds(binaryNode.getType(), Type.OBJECT);
|
||||
} else {
|
||||
// Non-optimistic, non-FP +. Allow it to overflow.
|
||||
operandBounds = new TypeBounds(binaryNode.getWidestOperandType(), Type.OBJECT);
|
||||
operandBounds = new TypeBounds(Type.narrowest(binaryNode.getWidestOperandType(), resultBounds.widest),
|
||||
Type.OBJECT);
|
||||
forceConversionSeparation = binaryNode.getWidestOperationType().narrowerThan(resultBounds.widest);
|
||||
}
|
||||
loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), operandBounds, false, forceConversionSeparation);
|
||||
@ -3856,12 +3856,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
operandBounds = numericBounds;
|
||||
} else {
|
||||
final boolean isOptimistic = isValid(getProgramPoint());
|
||||
if(isOptimistic) {
|
||||
if(isOptimistic || node.isTokenType(TokenType.DIV) || node.isTokenType(TokenType.MOD)) {
|
||||
operandBounds = new TypeBounds(node.getType(), Type.NUMBER);
|
||||
} else if(node.isTokenType(TokenType.DIV) || node.isTokenType(TokenType.MOD)) {
|
||||
// Non-optimistic division must always take double arguments as its result must also be
|
||||
// double.
|
||||
operandBounds = TypeBounds.NUMBER;
|
||||
} else {
|
||||
// Non-optimistic, non-FP subtraction or multiplication. Allow them to overflow.
|
||||
operandBounds = new TypeBounds(Type.narrowest(node.getWidestOperandType(),
|
||||
@ -3897,7 +3893,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
private static boolean isRhsZero(final BinaryNode binaryNode) {
|
||||
final Expression rhs = binaryNode.rhs();
|
||||
return rhs instanceof LiteralNode && INT_ZERO.equals(((LiteralNode)rhs).getValue());
|
||||
return rhs instanceof LiteralNode && INT_ZERO.equals(((LiteralNode<?>)rhs).getValue());
|
||||
}
|
||||
|
||||
private void loadBIT_XOR(final BinaryNode binaryNode) {
|
||||
|
@ -31,7 +31,6 @@ import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.nashorn.internal.IntDeque;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
@ -250,7 +249,7 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
||||
static Type getTypeForSlotDescriptor(final char typeDesc) {
|
||||
// Recognizing both lowercase and uppercase as we're using both to signify symbol boundaries; see
|
||||
// MethodEmitter.markSymbolBoundariesInLvarTypesDescriptor().
|
||||
switch(typeDesc) {
|
||||
switch (typeDesc) {
|
||||
case 'I':
|
||||
case 'i':
|
||||
return Type.INT;
|
||||
|
@ -389,6 +389,7 @@ public final class Compiler implements Loggable {
|
||||
* @param continuationEntryPoints continuation entry points for restof method
|
||||
* @param runtimeScope runtime scope for recompilation type lookup in {@code TypeEvaluator}
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public Compiler(
|
||||
final Context context,
|
||||
final ScriptEnvironment env,
|
||||
|
@ -28,7 +28,6 @@ package jdk.nashorn.internal.codegen;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
|
||||
import static jdk.nashorn.internal.ir.Expression.isAlwaysFalse;
|
||||
import static jdk.nashorn.internal.ir.Expression.isAlwaysTrue;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -236,12 +235,12 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
private byte conversions;
|
||||
|
||||
void recordConversion(final LvarType from, final LvarType to) {
|
||||
switch(from) {
|
||||
switch (from) {
|
||||
case UNDEFINED:
|
||||
return;
|
||||
case INT:
|
||||
case BOOLEAN:
|
||||
switch(to) {
|
||||
switch (to) {
|
||||
case LONG:
|
||||
recordConversion(I2L);
|
||||
return;
|
||||
@ -256,7 +255,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
return;
|
||||
}
|
||||
case LONG:
|
||||
switch(to) {
|
||||
switch (to) {
|
||||
case DOUBLE:
|
||||
recordConversion(L2D);
|
||||
return;
|
||||
@ -1425,6 +1424,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
* @param symbol the symbol representing the variable
|
||||
* @param type the type
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void setType(final Symbol symbol, final LvarType type) {
|
||||
if(getLocalVariableTypeOrNull(symbol) == type) {
|
||||
return;
|
||||
|
@ -1591,7 +1591,7 @@ public class MethodEmitter implements Emitter {
|
||||
/**
|
||||
* Abstraction for performing a conditional jump of any type
|
||||
*
|
||||
* @see MethodEmitter.Condition
|
||||
* @see Condition
|
||||
*
|
||||
* @param cond the condition to test
|
||||
* @param trueLabel the destination label is condition is true
|
||||
@ -2217,6 +2217,10 @@ public class MethodEmitter implements Emitter {
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod) {
|
||||
if (name.length() > LARGE_STRING_THRESHOLD) { // use getIndex for extremely long names
|
||||
return load(name).dynamicGetIndex(valueType, flags, isMethod);
|
||||
}
|
||||
|
||||
debug("dynamic_get", name, valueType, getProgramPoint(flags));
|
||||
|
||||
Type type = valueType;
|
||||
@ -2240,9 +2244,14 @@ public class MethodEmitter implements Emitter {
|
||||
* @param name name of property
|
||||
* @param flags call site flags
|
||||
*/
|
||||
void dynamicSet(final String name, final int flags) {
|
||||
assert !isOptimistic(flags);
|
||||
debug("dynamic_set", name, peekType());
|
||||
void dynamicSet(final String name, final int flags) {
|
||||
if (name.length() > LARGE_STRING_THRESHOLD) { // use setIndex for extremely long names
|
||||
load(name).swap().dynamicSetIndex(flags);
|
||||
return;
|
||||
}
|
||||
|
||||
assert !isOptimistic(flags);
|
||||
debug("dynamic_set", name, peekType());
|
||||
|
||||
Type type = peekType();
|
||||
if (type.isObject() || type.isBoolean()) { //promote strings to objects etc
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package jdk.nashorn.internal.codegen;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.MethodEmitter.LARGE_STRING_THRESHOLD;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
@ -66,27 +68,28 @@ public class Namespace {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a uniqueName name in the namespace in the form base$n where n varies
|
||||
* .
|
||||
* @param base Base of name. Base will be returned if uniqueName.
|
||||
* Create a uniqueName name in the namespace in the form base$n where n varies.
|
||||
* Also truncates very long names that would otherwise break ASM.
|
||||
*
|
||||
* @param base Base of name. Base will be returned if uniqueName.
|
||||
* @return Generated uniqueName name.
|
||||
*/
|
||||
public String uniqueName(final String base) {
|
||||
final String truncatedBase = base.length() > LARGE_STRING_THRESHOLD ? base.substring(0, LARGE_STRING_THRESHOLD) : base;
|
||||
for (Namespace namespace = this; namespace != null; namespace = namespace.getParent()) {
|
||||
final HashMap<String, Integer> namespaceDirectory = namespace.directory;
|
||||
final Integer counter = namespaceDirectory.get(base);
|
||||
final Integer counter = namespaceDirectory.get(truncatedBase);
|
||||
|
||||
if (counter != null) {
|
||||
final int count = counter + 1;
|
||||
namespaceDirectory.put(base, count);
|
||||
return base + '-' + count;
|
||||
namespaceDirectory.put(truncatedBase, count);
|
||||
return truncatedBase + '-' + count;
|
||||
}
|
||||
}
|
||||
|
||||
directory.put(base, 0);
|
||||
directory.put(truncatedBase, 0);
|
||||
|
||||
return base;
|
||||
return truncatedBase;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -323,9 +323,11 @@ public final class OptimisticTypesPersistence {
|
||||
* per-code-version directory. Normally, this will create the SHA-1 digest of the nashorn.jar. In case the classpath
|
||||
* for nashorn is local directory (e.g. during development), this will create the string "dev-" followed by the
|
||||
* timestamp of the most recent .class file.
|
||||
* @return
|
||||
*
|
||||
* @return digest of currently running nashorn
|
||||
* @throws Exception if digest could not be created
|
||||
*/
|
||||
private static String getVersionDirName() throws Exception {
|
||||
public static String getVersionDirName() throws Exception {
|
||||
final URL url = OptimisticTypesPersistence.class.getResource("");
|
||||
final String protocol = url.getProtocol();
|
||||
if (protocol.equals("jar")) {
|
||||
|
@ -55,6 +55,7 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_
|
||||
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.nashorn.internal.codegen.CompilerConstants;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
|
||||
/**
|
||||
* Type class: INT
|
||||
@ -230,19 +231,21 @@ class IntType extends BitwiseType {
|
||||
|
||||
@Override
|
||||
public Type div(final MethodVisitor method, final int programPoint) {
|
||||
// Never perform non-optimistic integer division in JavaScript.
|
||||
assert programPoint != INVALID_PROGRAM_POINT;
|
||||
|
||||
method.visitInvokeDynamicInsn("idiv", "(II)I", MATHBOOTSTRAP, programPoint);
|
||||
if (programPoint == INVALID_PROGRAM_POINT) {
|
||||
JSType.DIV_ZERO.invoke(method);
|
||||
} else {
|
||||
method.visitInvokeDynamicInsn("idiv", "(II)I", MATHBOOTSTRAP, programPoint);
|
||||
}
|
||||
return INT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type rem(final MethodVisitor method, final int programPoint) {
|
||||
// Never perform non-optimistic integer remainder in JavaScript.
|
||||
assert programPoint != INVALID_PROGRAM_POINT;
|
||||
|
||||
method.visitInvokeDynamicInsn("irem", "(II)I", MATHBOOTSTRAP, programPoint);
|
||||
if (programPoint == INVALID_PROGRAM_POINT) {
|
||||
JSType.REM_ZERO.invoke(method);
|
||||
} else {
|
||||
method.visitInvokeDynamicInsn("irem", "(II)I", MATHBOOTSTRAP, programPoint);
|
||||
}
|
||||
return INT;
|
||||
}
|
||||
|
||||
|
@ -170,19 +170,21 @@ class LongType extends BitwiseType {
|
||||
|
||||
@Override
|
||||
public Type div(final MethodVisitor method, final int programPoint) {
|
||||
// Never perform non-optimistic integer division in JavaScript.
|
||||
assert programPoint != INVALID_PROGRAM_POINT;
|
||||
|
||||
method.visitInvokeDynamicInsn("ldiv", "(JJ)J", MATHBOOTSTRAP, programPoint);
|
||||
if (programPoint == INVALID_PROGRAM_POINT) {
|
||||
JSType.DIV_ZERO_LONG.invoke(method);
|
||||
} else {
|
||||
method.visitInvokeDynamicInsn("ldiv", "(JJ)J", MATHBOOTSTRAP, programPoint);
|
||||
}
|
||||
return LONG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type rem(final MethodVisitor method, final int programPoint) {
|
||||
// Never perform non-optimistic integer remainder in JavaScript.
|
||||
assert programPoint != INVALID_PROGRAM_POINT;
|
||||
|
||||
method.visitInvokeDynamicInsn("lrem", "(JJ)J", MATHBOOTSTRAP, programPoint);
|
||||
if (programPoint == INVALID_PROGRAM_POINT) {
|
||||
JSType.REM_ZERO_LONG.invoke(method);
|
||||
} else {
|
||||
method.visitInvokeDynamicInsn("lrem", "(JJ)J", MATHBOOTSTRAP, programPoint);
|
||||
}
|
||||
return LONG;
|
||||
}
|
||||
|
||||
|
@ -356,7 +356,7 @@ public abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
|
||||
final int pp = input.readInt();
|
||||
final int typeChar = input.readByte();
|
||||
final Type type;
|
||||
switch(typeChar) {
|
||||
switch (typeChar) {
|
||||
case 'L': type = Type.OBJECT; break;
|
||||
case 'D': type = Type.NUMBER; break;
|
||||
case 'J': type = Type.LONG; break;
|
||||
@ -376,13 +376,13 @@ public abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
|
||||
}
|
||||
|
||||
private static jdk.internal.org.objectweb.asm.Type lookupInternalType(final Class<?> type) {
|
||||
final Map<Class<?>, jdk.internal.org.objectweb.asm.Type> cache = INTERNAL_TYPE_CACHE;
|
||||
jdk.internal.org.objectweb.asm.Type itype = cache.get(type);
|
||||
final Map<Class<?>, jdk.internal.org.objectweb.asm.Type> c = INTERNAL_TYPE_CACHE;
|
||||
jdk.internal.org.objectweb.asm.Type itype = c.get(type);
|
||||
if (itype != null) {
|
||||
return itype;
|
||||
}
|
||||
itype = jdk.internal.org.objectweb.asm.Type.getType(type);
|
||||
cache.put(type, itype);
|
||||
c.put(type, itype);
|
||||
return itype;
|
||||
}
|
||||
|
||||
@ -1155,6 +1155,10 @@ public abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read resolve
|
||||
* @return resolved type
|
||||
*/
|
||||
protected final Object readResolve() {
|
||||
return Type.typeFor(clazz);
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ package jdk.nashorn.internal.objects;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
@ -44,6 +43,9 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
|
||||
|
||||
/**
|
||||
* ArrayBufferView, es6 class or TypedArray implementation
|
||||
*/
|
||||
@ScriptClass("ArrayBufferView")
|
||||
public abstract class ArrayBufferView extends ScriptObject {
|
||||
private final NativeArrayBuffer buffer;
|
||||
@ -71,6 +73,13 @@ public abstract class ArrayBufferView extends ScriptObject {
|
||||
setArray(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param buffer underlying NativeArrayBuffer
|
||||
* @param byteOffset byte offset for buffer
|
||||
* @param elementLength element length in bytes
|
||||
*/
|
||||
protected ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
|
||||
this(buffer, byteOffset, elementLength, Global.instance());
|
||||
}
|
||||
@ -89,22 +98,42 @@ public abstract class ArrayBufferView extends ScriptObject {
|
||||
return factory().bytesPerElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer getter as per spec
|
||||
* @param self ArrayBufferView instance
|
||||
* @return buffer
|
||||
*/
|
||||
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
|
||||
public static Object buffer(final Object self) {
|
||||
return ((ArrayBufferView)self).buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer offset getter as per spec
|
||||
* @param self ArrayBufferView instance
|
||||
* @return buffer offset
|
||||
*/
|
||||
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
|
||||
public static int byteOffset(final Object self) {
|
||||
return ((ArrayBufferView)self).byteOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Byte length getter as per spec
|
||||
* @param self ArrayBufferView instance
|
||||
* @return array buffer view length in bytes
|
||||
*/
|
||||
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
|
||||
public static int byteLength(final Object self) {
|
||||
final ArrayBufferView view = (ArrayBufferView)self;
|
||||
return ((TypedArrayData<?>)view.getArray()).getElementLength() * view.bytesPerElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Length getter as per spec
|
||||
* @param self ArrayBufferView instance
|
||||
* @return length in elements
|
||||
*/
|
||||
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
|
||||
public static int length(final Object self) {
|
||||
return ((ArrayBufferView)self).elementLength();
|
||||
@ -119,15 +148,29 @@ public abstract class ArrayBufferView extends ScriptObject {
|
||||
return ((TypedArrayData<?>)getArray()).getElementLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory class for byte ArrayBufferViews
|
||||
*/
|
||||
protected static abstract class Factory {
|
||||
final int bytesPerElement;
|
||||
final int maxElementLength;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param bytesPerElement number of bytes per element for this buffer
|
||||
*/
|
||||
public Factory(final int bytesPerElement) {
|
||||
this.bytesPerElement = bytesPerElement;
|
||||
this.maxElementLength = Integer.MAX_VALUE / bytesPerElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method
|
||||
*
|
||||
* @param elementLength number of elements
|
||||
* @return new ArrayBufferView
|
||||
*/
|
||||
public final ArrayBufferView construct(final int elementLength) {
|
||||
if (elementLength > maxElementLength) {
|
||||
throw rangeError("inappropriate.array.buffer.length", JSType.toString(elementLength));
|
||||
@ -135,15 +178,47 @@ public abstract class ArrayBufferView extends ScriptObject {
|
||||
return construct(new NativeArrayBuffer(elementLength * bytesPerElement), 0, elementLength);
|
||||
}
|
||||
|
||||
public abstract ArrayBufferView construct(NativeArrayBuffer buffer, int byteOffset, int elementLength);
|
||||
/**
|
||||
* Factory method
|
||||
*
|
||||
* @param buffer underlying buffer
|
||||
* @param byteOffset byte offset
|
||||
* @param elementLength number of elements
|
||||
*
|
||||
* @return new ArrayBufferView
|
||||
*/
|
||||
public abstract ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength);
|
||||
|
||||
public abstract TypedArrayData<?> createArrayData(ByteBuffer nb, int start, int end);
|
||||
/**
|
||||
* Factory method for array data
|
||||
*
|
||||
* @param nb underlying nativebuffer
|
||||
* @param start start element
|
||||
* @param end end element
|
||||
*
|
||||
* @return new array data
|
||||
*/
|
||||
public abstract TypedArrayData<?> createArrayData(final ByteBuffer nb, final int start, final int end);
|
||||
|
||||
/**
|
||||
* Get the class name for this type of buffer
|
||||
*
|
||||
* @return class name
|
||||
*/
|
||||
public abstract String getClassName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the factor for this kind of buffer
|
||||
* @return Factory
|
||||
*/
|
||||
protected abstract Factory factory();
|
||||
|
||||
/**
|
||||
* Get the prototype for this ArrayBufferView
|
||||
* @param global global instance
|
||||
* @return prototype
|
||||
*/
|
||||
protected abstract ScriptObject getPrototype(final Global global);
|
||||
|
||||
@Override
|
||||
@ -151,10 +226,23 @@ public abstract class ArrayBufferView extends ScriptObject {
|
||||
return factory().getClassName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this array contains floats
|
||||
* @return true if float array (or double)
|
||||
*/
|
||||
protected boolean isFloatArray() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inheritable constructor implementation
|
||||
*
|
||||
* @param newObj is this a new constructor
|
||||
* @param args arguments
|
||||
* @param factory factory
|
||||
*
|
||||
* @return new ArrayBufferView
|
||||
*/
|
||||
protected static ArrayBufferView constructorImpl(final boolean newObj, final Object[] args, final Factory factory) {
|
||||
final Object arg0 = args.length != 0 ? args[0] : 0;
|
||||
final ArrayBufferView dest;
|
||||
@ -200,6 +288,15 @@ public abstract class ArrayBufferView extends ScriptObject {
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inheritable implementation of set, if no efficient implementation is available
|
||||
*
|
||||
* @param self ArrayBufferView instance
|
||||
* @param array array
|
||||
* @param offset0 array offset
|
||||
*
|
||||
* @return result of setter
|
||||
*/
|
||||
protected static Object setImpl(final Object self, final Object array, final Object offset0) {
|
||||
final ArrayBufferView dest = (ArrayBufferView)self;
|
||||
final int length;
|
||||
@ -244,6 +341,15 @@ public abstract class ArrayBufferView extends ScriptObject {
|
||||
return (int)(length & Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of subarray if no efficient override exists
|
||||
*
|
||||
* @param self ArrayBufferView instance
|
||||
* @param begin0 begin index
|
||||
* @param end0 end index
|
||||
*
|
||||
* @return sub array
|
||||
*/
|
||||
protected static ScriptObject subarrayImpl(final Object self, final Object begin0, final Object end0) {
|
||||
final ArrayBufferView arrayView = (ArrayBufferView)self;
|
||||
final int byteOffset = arrayView.byteOffset;
|
||||
|
@ -29,6 +29,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
@ -41,7 +42,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.script.ScriptContext;
|
||||
import javax.script.ScriptEngine;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
@ -54,7 +54,6 @@ import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.runtime.ConsString;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.GlobalConstants;
|
||||
import jdk.nashorn.internal.runtime.GlobalFunctions;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.NativeJavaPackage;
|
||||
@ -438,9 +437,6 @@ public final class Global extends ScriptObject implements Scope {
|
||||
this.scontext = scontext;
|
||||
}
|
||||
|
||||
// global constants for this global - they can be replaced with MethodHandle.constant until invalidated
|
||||
private static AtomicReference<GlobalConstants> gcsInstance = new AtomicReference<>();
|
||||
|
||||
@Override
|
||||
protected Context getContext() {
|
||||
return context;
|
||||
@ -470,11 +466,6 @@ public final class Global extends ScriptObject implements Scope {
|
||||
super(checkAndGetMap(context));
|
||||
this.context = context;
|
||||
this.setIsScope();
|
||||
//we can only share one instance of Global constants between globals, or we consume way too much
|
||||
//memory - this is good enough for most programs
|
||||
while (gcsInstance.get() == null) {
|
||||
gcsInstance.compareAndSet(null, new GlobalConstants(context.getLogger(GlobalConstants.class)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -492,15 +483,6 @@ public final class Global extends ScriptObject implements Scope {
|
||||
return self instanceof Global? (Global)self : instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the global constants map for fields that
|
||||
* can be accessed as MethodHandle.constant
|
||||
* @return constant map
|
||||
*/
|
||||
public static GlobalConstants getConstants() {
|
||||
return gcsInstance.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we have a Global instance
|
||||
* @return true if one exists
|
||||
@ -559,16 +541,16 @@ public final class Global extends ScriptObject implements Scope {
|
||||
* as well as our extension builtin objects like "Java", "JSAdapter" as properties
|
||||
* of the global scope object.
|
||||
*
|
||||
* @param engine ScriptEngine to initialize
|
||||
* @param eng ScriptEngine to initialize
|
||||
*/
|
||||
public void initBuiltinObjects(final ScriptEngine engine) {
|
||||
public void initBuiltinObjects(final ScriptEngine eng) {
|
||||
if (this.builtinObject != null) {
|
||||
// already initialized, just return
|
||||
return;
|
||||
}
|
||||
|
||||
this.engine = engine;
|
||||
init(engine);
|
||||
this.engine = eng;
|
||||
init(eng);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1717,7 +1699,7 @@ public final class Global extends ScriptObject implements Scope {
|
||||
return func;
|
||||
}
|
||||
|
||||
private void init(final ScriptEngine engine) {
|
||||
private void init(final ScriptEngine eng) {
|
||||
assert Context.getGlobal() == this : "this global is not set as current";
|
||||
|
||||
final ScriptEnvironment env = getContext().getEnv();
|
||||
@ -1835,7 +1817,7 @@ public final class Global extends ScriptObject implements Scope {
|
||||
addOwnProperty("$ARG", Attribute.NOT_ENUMERABLE, arguments);
|
||||
}
|
||||
|
||||
if (engine != null) {
|
||||
if (eng != null) {
|
||||
// default file name
|
||||
addOwnProperty(ScriptEngine.FILENAME, Attribute.NOT_ENUMERABLE, null);
|
||||
// __noSuchProperty__ hook for ScriptContext search of missing variables
|
||||
|
@ -26,7 +26,6 @@
|
||||
package jdk.nashorn.internal.objects;
|
||||
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
@ -34,6 +33,7 @@ import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Getter;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
@ -137,6 +137,19 @@ public final class NativeArrayBuffer extends ScriptObject {
|
||||
return ((NativeArrayBuffer)self).getByteLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an object is an ArrayBufferView
|
||||
*
|
||||
* @param self self
|
||||
* @param obj object to check
|
||||
*
|
||||
* @return true if obj is an ArrayBufferView
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
|
||||
public static boolean isView(final Object self, final Object obj) {
|
||||
return obj instanceof ArrayBufferView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Slice function
|
||||
* @param self native array buffer
|
||||
|
@ -29,6 +29,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
|
||||
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
@ -572,7 +573,7 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
|
||||
try {
|
||||
return ((CharSequence)self).charAt(pos);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
throw new ClassCastException();
|
||||
throw new ClassCastException(); //invalid char, out of bounds, force relink
|
||||
}
|
||||
}
|
||||
|
||||
@ -1380,7 +1381,6 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
|
||||
* sequence and that we are in range
|
||||
*/
|
||||
private static final class CharCodeAtLinkLogic extends SpecializedFunction.LinkLogic {
|
||||
|
||||
private static final CharCodeAtLinkLogic INSTANCE = new CharCodeAtLinkLogic();
|
||||
|
||||
@Override
|
||||
@ -1389,7 +1389,7 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
|
||||
//check that it's a char sequence or throw cce
|
||||
final CharSequence cs = (CharSequence)self;
|
||||
//check that the index, representable as an int, is inside the array
|
||||
final int intIndex = JSType.toInteger(request.getArguments()[1]);
|
||||
final int intIndex = JSType.toInteger(request.getArguments()[2]);
|
||||
return intIndex >= 0 && intIndex < cs.length(); //can link
|
||||
} catch (final ClassCastException | IndexOutOfBoundsException e) {
|
||||
//fallthru
|
||||
|
@ -141,9 +141,8 @@ class ParserContext {
|
||||
return breakable;
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
return getBreakable();
|
||||
}
|
||||
return getBreakable();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,7 +56,7 @@ abstract class ParserContextBaseNode implements ParserContextNode {
|
||||
|
||||
/**
|
||||
* Returns a single flag
|
||||
* @param flag
|
||||
* @param flag flag
|
||||
* @return A single flag
|
||||
*/
|
||||
protected int getFlag(final int flag) {
|
||||
@ -64,7 +64,7 @@ abstract class ParserContextBaseNode implements ParserContextNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param flag
|
||||
* @param flag flag
|
||||
* @return the new flags
|
||||
*/
|
||||
@Override
|
||||
@ -82,7 +82,7 @@ abstract class ParserContextBaseNode implements ParserContextNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param statements
|
||||
* @param statements statements
|
||||
*/
|
||||
@Override
|
||||
public void setStatements(final List<Statement> statements) {
|
||||
|
@ -41,6 +41,7 @@ import java.security.PrivilegedExceptionAction;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import jdk.nashorn.internal.codegen.OptimisticTypesPersistence;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.runtime.logging.DebugLogger;
|
||||
import jdk.nashorn.internal.runtime.logging.Loggable;
|
||||
@ -102,7 +103,7 @@ public abstract class CodeStore implements Loggable {
|
||||
} catch (final AccessControlException e) {
|
||||
context.getLogger(CodeStore.class).warning("failed to load code store provider ", e);
|
||||
}
|
||||
final CodeStore store = new DirectoryCodeStore();
|
||||
final CodeStore store = new DirectoryCodeStore(context);
|
||||
store.initLogger(context);
|
||||
return store;
|
||||
}
|
||||
@ -210,32 +211,34 @@ public abstract class CodeStore implements Loggable {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param context the current context
|
||||
* @throws IOException if there are read/write problems with the cache and cache directory
|
||||
*/
|
||||
public DirectoryCodeStore() throws IOException {
|
||||
this(Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"), false, DEFAULT_MIN_SIZE);
|
||||
public DirectoryCodeStore(final Context context) throws IOException {
|
||||
this(context, Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"), false, DEFAULT_MIN_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param context the current context
|
||||
* @param path directory to store code in
|
||||
* @param readOnly is this a read only code store
|
||||
* @param minSize minimum file size for caching scripts
|
||||
* @throws IOException if there are read/write problems with the cache and cache directory
|
||||
*/
|
||||
public DirectoryCodeStore(final String path, final boolean readOnly, final int minSize) throws IOException {
|
||||
this.dir = checkDirectory(path, readOnly);
|
||||
public DirectoryCodeStore(final Context context, final String path, final boolean readOnly, final int minSize) throws IOException {
|
||||
this.dir = checkDirectory(path, context.getEnv(), readOnly);
|
||||
this.readOnly = readOnly;
|
||||
this.minSize = minSize;
|
||||
}
|
||||
|
||||
private static File checkDirectory(final String path, final boolean readOnly) throws IOException {
|
||||
private static File checkDirectory(final String path, final ScriptEnvironment env, final boolean readOnly) throws IOException {
|
||||
try {
|
||||
return AccessController.doPrivileged(new PrivilegedExceptionAction<File>() {
|
||||
@Override
|
||||
public File run() throws IOException {
|
||||
final File dir = new File(path).getAbsoluteFile();
|
||||
final File dir = new File(path, getVersionDir(env)).getAbsoluteFile();
|
||||
if (readOnly) {
|
||||
if (!dir.exists() || !dir.isDirectory()) {
|
||||
throw new IOException("Not a directory: " + dir.getPath());
|
||||
@ -257,6 +260,15 @@ public abstract class CodeStore implements Loggable {
|
||||
}
|
||||
}
|
||||
|
||||
private static String getVersionDir(final ScriptEnvironment env) throws IOException {
|
||||
try {
|
||||
final String versionDir = OptimisticTypesPersistence.getVersionDirName();
|
||||
return env._optimistic_types ? versionDir + "_opt" : versionDir;
|
||||
} catch (final Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredScript load(final Source source, final String functionKey) {
|
||||
if (source.getLength() < minSize) {
|
||||
|
@ -27,7 +27,6 @@ package jdk.nashorn.internal.runtime;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
|
||||
|
||||
import java.lang.invoke.CallSite;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@ -777,7 +776,7 @@ final class CompiledFunction {
|
||||
|
||||
// Compiler needs a call site type as its input, which always has a callee parameter, so we must add it if
|
||||
// this function doesn't have a callee parameter.
|
||||
final MethodType callSiteType = type.parameterType(0) == ScriptFunction.class ?
|
||||
final MethodType ct = type.parameterType(0) == ScriptFunction.class ?
|
||||
type :
|
||||
type.insertParameterTypes(0, ScriptFunction.class);
|
||||
final OptimismInfo currentOptInfo = optimismInfo;
|
||||
@ -788,29 +787,29 @@ final class CompiledFunction {
|
||||
final OptimismInfo effectiveOptInfo = currentOptInfo != null ? currentOptInfo : oldOptInfo;
|
||||
FunctionNode fn = effectiveOptInfo.reparse();
|
||||
final boolean serialized = effectiveOptInfo.isSerialized();
|
||||
final Compiler compiler = effectiveOptInfo.getCompiler(fn, callSiteType, re); //set to non rest-of
|
||||
final Compiler compiler = effectiveOptInfo.getCompiler(fn, ct, re); //set to non rest-of
|
||||
|
||||
if (!shouldRecompile) {
|
||||
// It didn't necessarily recompile, e.g. for an outer invocation of a recursive function if we already
|
||||
// recompiled a deoptimized version for an inner invocation.
|
||||
// We still need to do the rest of from the beginning
|
||||
logRecompile("Rest-of compilation [STANDALONE] ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints);
|
||||
logRecompile("Rest-of compilation [STANDALONE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints);
|
||||
return restOfHandle(effectiveOptInfo, compiler.compile(fn, serialized ? CompilationPhases.COMPILE_SERIALIZED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null);
|
||||
}
|
||||
|
||||
logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints);
|
||||
logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, ct, effectiveOptInfo.invalidatedProgramPoints);
|
||||
fn = compiler.compile(fn, serialized ? CompilationPhases.RECOMPILE_SERIALIZED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE);
|
||||
log.info("Reusable IR generated");
|
||||
|
||||
// compile the rest of the function, and install it
|
||||
log.info("Generating and installing bytecode from reusable IR...");
|
||||
logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints);
|
||||
logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints);
|
||||
final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.GENERATE_BYTECODE_AND_INSTALL);
|
||||
|
||||
if (effectiveOptInfo.data.usePersistentCodeCache()) {
|
||||
final RecompilableScriptFunctionData data = effectiveOptInfo.data;
|
||||
final int functionNodeId = data.getFunctionNodeId();
|
||||
final TypeMap typeMap = data.typeMap(callSiteType);
|
||||
final TypeMap typeMap = data.typeMap(ct);
|
||||
final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
|
||||
final String cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes);
|
||||
compiler.persistClassInfo(cacheKey, normalFn);
|
||||
@ -871,6 +870,7 @@ final class CompiledFunction {
|
||||
private SwitchPoint optimisticAssumptions;
|
||||
private final DebugLogger log;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
OptimismInfo(final RecompilableScriptFunctionData data, final Map<Integer, Type> invalidatedProgramPoints) {
|
||||
this.data = data;
|
||||
this.log = data.getLogger();
|
||||
|
@ -33,6 +33,7 @@ import static jdk.nashorn.internal.runtime.CodeStore.newCodeStore;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
|
||||
import static jdk.nashorn.internal.runtime.Source.sourceFor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
@ -60,6 +61,7 @@ import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
@ -262,6 +264,10 @@ public final class Context {
|
||||
// persistent code store
|
||||
private CodeStore codeStore;
|
||||
|
||||
// A factory for linking global properties as constant method handles. It is created when the first Global
|
||||
// is created, and invalidated forever once the second global is created.
|
||||
private final AtomicReference<GlobalConstants> globalConstantsRef = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* Get the current global scope
|
||||
* @return the current global scope
|
||||
@ -293,7 +299,10 @@ public final class Context {
|
||||
assert getGlobal() != global;
|
||||
//same code can be cached between globals, then we need to invalidate method handle constants
|
||||
if (global != null) {
|
||||
Global.getConstants().invalidateAll();
|
||||
final GlobalConstants globalConstants = getContext(global).getGlobalConstants();
|
||||
if (globalConstants != null) {
|
||||
globalConstants.invalidateAll();
|
||||
}
|
||||
}
|
||||
currentGlobal.set(global);
|
||||
}
|
||||
@ -528,6 +537,15 @@ public final class Context {
|
||||
return classFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the factory for constant method handles for global properties. The returned factory can be
|
||||
* invalidated if this Context has more than one Global.
|
||||
* @return the factory for constant method handles for global properties.
|
||||
*/
|
||||
GlobalConstants getGlobalConstants() {
|
||||
return globalConstantsRef.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error manager for this context
|
||||
* @return error manger
|
||||
@ -1016,9 +1034,32 @@ public final class Context {
|
||||
* @return the global script object
|
||||
*/
|
||||
public Global newGlobal() {
|
||||
createOrInvalidateGlobalConstants();
|
||||
return new Global(this);
|
||||
}
|
||||
|
||||
private void createOrInvalidateGlobalConstants() {
|
||||
for (;;) {
|
||||
final GlobalConstants currentGlobalConstants = getGlobalConstants();
|
||||
if (currentGlobalConstants != null) {
|
||||
// Subsequent invocation; we're creating our second or later Global. GlobalConstants is not safe to use
|
||||
// with more than one Global, as the constant method handle linkages it creates create a coupling
|
||||
// between the Global and the call sites in the compiled code.
|
||||
currentGlobalConstants.invalidateForever();
|
||||
return;
|
||||
}
|
||||
final GlobalConstants newGlobalConstants = new GlobalConstants(getLogger(GlobalConstants.class));
|
||||
if (globalConstantsRef.compareAndSet(null, newGlobalConstants)) {
|
||||
// First invocation; we're creating the first Global in this Context. Create the GlobalConstants object
|
||||
// for this Context.
|
||||
return;
|
||||
}
|
||||
|
||||
// If we reach here, then we started out as the first invocation, but another concurrent invocation won the
|
||||
// CAS race. We'll just let the loop repeat and invalidate the CAS race winner.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize given global scope object.
|
||||
*
|
||||
@ -1057,12 +1098,19 @@ public final class Context {
|
||||
* @return current global's context
|
||||
*/
|
||||
static Context getContextTrusted() {
|
||||
return ((ScriptObject)Context.getGlobal()).getContext();
|
||||
return getContext(getGlobal());
|
||||
}
|
||||
|
||||
static Context getContextTrustedOrNull() {
|
||||
final Global global = Context.getGlobal();
|
||||
return global == null ? null : ((ScriptObject)global).getContext();
|
||||
return global == null ? null : getContext(global);
|
||||
}
|
||||
|
||||
private static Context getContext(final Global global) {
|
||||
// We can't invoke Global.getContext() directly, as it's a protected override, and Global isn't in our package.
|
||||
// In order to access the method, we must cast it to ScriptObject first (which is in our package) and then let
|
||||
// virtual invocation do its thing.
|
||||
return ((ScriptObject)global).getContext();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1150,9 +1198,8 @@ public final class Context {
|
||||
|
||||
StoredScript storedScript = null;
|
||||
FunctionNode functionNode = null;
|
||||
// We only use the code store here if optimistic types are disabled. With optimistic types,
|
||||
// code is stored per function in RecompilableScriptFunctionData.
|
||||
// TODO: This should really be triggered by lazy compilation, not optimistic types.
|
||||
// We only use the code store here if optimistic types are disabled. With optimistic types, initial compilation
|
||||
// just creates a thin wrapper, and actual code is stored per function in RecompilableScriptFunctionData.
|
||||
final boolean useCodeStore = env._persistent_cache && !env._parse_only && !env._optimistic_types;
|
||||
final String cacheKey = useCodeStore ? CodeStore.getCacheKey(0, null) : null;
|
||||
|
||||
|
@ -96,15 +96,17 @@ public final class ECMAException extends NashornException {
|
||||
// If thrown object is an Error or sub-object like TypeError, then
|
||||
// an ECMAException object has been already initialized at constructor.
|
||||
if (thrown instanceof ScriptObject) {
|
||||
final ScriptObject sobj = (ScriptObject)thrown;
|
||||
final Object exception = getException(sobj);
|
||||
final Object exception = getException((ScriptObject)thrown);
|
||||
if (exception instanceof ECMAException) {
|
||||
// copy over file name, line number and column number.
|
||||
final ECMAException ee = (ECMAException)exception;
|
||||
ee.setFileName(fileName);
|
||||
ee.setLineNumber(line);
|
||||
ee.setColumnNumber(column);
|
||||
return ee;
|
||||
// Make sure exception has correct thrown reference because that's what will end up getting caught.
|
||||
if (ee.getThrown() == thrown) {
|
||||
// copy over file name, line number and column number.
|
||||
ee.setFileName(fileName);
|
||||
ee.setLineNumber(line);
|
||||
ee.setColumnNumber(column);
|
||||
return ee;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,7 +156,11 @@ public final class ECMAException extends NashornException {
|
||||
* @return a {@link ECMAException}
|
||||
*/
|
||||
public static Object getException(final ScriptObject errObj) {
|
||||
return errObj.get(ECMAException.EXCEPTION_PROPERTY);
|
||||
// Exclude inherited properties that may belong to errors in the prototype chain.
|
||||
if (errObj.hasOwnProperty(ECMAException.EXCEPTION_PROPERTY)) {
|
||||
return errObj.get(ECMAException.EXCEPTION_PROPERTY);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,12 +31,14 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.getProgramPoint;
|
||||
import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.SwitchPoint;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.DynamicLinker;
|
||||
@ -50,7 +52,7 @@ import jdk.nashorn.internal.runtime.logging.Loggable;
|
||||
import jdk.nashorn.internal.runtime.logging.Logger;
|
||||
|
||||
/**
|
||||
* Each global owns one of these. This is basically table of accessors
|
||||
* Each context owns one of these. This is basically table of accessors
|
||||
* for global properties. A global constant is evaluated to a MethodHandle.constant
|
||||
* for faster access and to avoid walking to proto chain looking for it.
|
||||
*
|
||||
@ -67,12 +69,19 @@ import jdk.nashorn.internal.runtime.logging.Logger;
|
||||
* reregister the switchpoint. Set twice or more - don't try again forever, or we'd
|
||||
* just end up relinking our way into megamorphisism.
|
||||
*
|
||||
* Also it has to be noted that this kind of linking creates a coupling between a Global
|
||||
* and the call sites in compiled code belonging to the Context. For this reason, the
|
||||
* linkage becomes incorrect as soon as the Context has more than one Global. The
|
||||
* {@link #invalidateForever()} is invoked by the Context to invalidate all linkages and
|
||||
* turn off the functionality of this object as soon as the Context's {@link Context#newGlobal()} is invoked
|
||||
* for second time.
|
||||
*
|
||||
* We can extend this to ScriptObjects in general (GLOBAL_ONLY=false), which requires
|
||||
* a receiver guard on the constant getter, but it currently leaks memory and its benefits
|
||||
* have not yet been investigated property.
|
||||
*
|
||||
* As long as all Globals share the same constant instance, we need synchronization
|
||||
* whenever we access the instance.
|
||||
* As long as all Globals in a Context share the same GlobalConstants instance, we need synchronization
|
||||
* whenever we access it.
|
||||
*/
|
||||
@Logger(name="const")
|
||||
public final class GlobalConstants implements Loggable {
|
||||
@ -82,7 +91,7 @@ public final class GlobalConstants implements Loggable {
|
||||
* Script objects require a receiver guard, which is memory intensive, so this is currently
|
||||
* disabled. We might implement a weak reference based approach to this later.
|
||||
*/
|
||||
private static final boolean GLOBAL_ONLY = true;
|
||||
public static final boolean GLOBAL_ONLY = true;
|
||||
|
||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
|
||||
@ -98,6 +107,8 @@ public final class GlobalConstants implements Loggable {
|
||||
*/
|
||||
private final Map<String, Access> map = new HashMap<>();
|
||||
|
||||
private final AtomicBoolean invalidatedForever = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
* Constructor - used only by global
|
||||
* @param log logger, or null if none
|
||||
@ -216,10 +227,34 @@ public final class GlobalConstants implements Loggable {
|
||||
* the same class for a new global, but the builtins and global scoped variables
|
||||
* will have changed.
|
||||
*/
|
||||
public synchronized void invalidateAll() {
|
||||
log.info("New global created - invalidating all constant callsites without increasing invocation count.");
|
||||
for (final Access acc : map.values()) {
|
||||
acc.invalidateUncounted();
|
||||
public void invalidateAll() {
|
||||
if (!invalidatedForever.get()) {
|
||||
log.info("New global created - invalidating all constant callsites without increasing invocation count.");
|
||||
synchronized (this) {
|
||||
for (final Access acc : map.values()) {
|
||||
acc.invalidateUncounted();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To avoid an expensive global guard "is this the same global", similar to the
|
||||
* receiver guard on the ScriptObject level, we invalidate all getters when the
|
||||
* second Global is created by the Context owning this instance. After this
|
||||
* method is invoked, this GlobalConstants instance will both invalidate all the
|
||||
* switch points it produced, and it will stop handing out new method handles
|
||||
* altogether.
|
||||
*/
|
||||
public void invalidateForever() {
|
||||
if (invalidatedForever.compareAndSet(false, true)) {
|
||||
log.info("New global created - invalidating all constant callsites.");
|
||||
synchronized (this) {
|
||||
for (final Access acc : map.values()) {
|
||||
acc.invalidateForever();
|
||||
}
|
||||
map.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,7 +286,7 @@ public final class GlobalConstants implements Loggable {
|
||||
return obj;
|
||||
}
|
||||
|
||||
private synchronized Access getOrCreateSwitchPoint(final String name) {
|
||||
private Access getOrCreateSwitchPoint(final String name) {
|
||||
Access acc = map.get(name);
|
||||
if (acc != null) {
|
||||
return acc;
|
||||
@ -267,9 +302,13 @@ public final class GlobalConstants implements Loggable {
|
||||
* @param name name of property
|
||||
*/
|
||||
void delete(final String name) {
|
||||
final Access acc = map.get(name);
|
||||
if (acc != null) {
|
||||
acc.invalidateForever();
|
||||
if (!invalidatedForever.get()) {
|
||||
synchronized (this) {
|
||||
final Access acc = map.get(name);
|
||||
if (acc != null) {
|
||||
acc.invalidateForever();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,45 +352,45 @@ public final class GlobalConstants implements Loggable {
|
||||
*
|
||||
* @return null if failed to set up constant linkage
|
||||
*/
|
||||
synchronized GuardedInvocation findSetMethod(final FindProperty find, final ScriptObject receiver, final GuardedInvocation inv, final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
if (GLOBAL_ONLY && !isGlobalSetter(receiver, find)) {
|
||||
GuardedInvocation findSetMethod(final FindProperty find, final ScriptObject receiver, final GuardedInvocation inv, final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
if (invalidatedForever.get() || (GLOBAL_ONLY && !isGlobalSetter(receiver, find))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
|
||||
final Access acc = getOrCreateSwitchPoint(name);
|
||||
synchronized (this) {
|
||||
final Access acc = getOrCreateSwitchPoint(name);
|
||||
|
||||
if (log.isEnabled()) {
|
||||
log.fine("Trying to link constant SETTER ", acc);
|
||||
}
|
||||
|
||||
if (!acc.mayRetry()) {
|
||||
if (log.isEnabled()) {
|
||||
log.fine("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
|
||||
log.fine("Trying to link constant SETTER ", acc);
|
||||
}
|
||||
return null;
|
||||
|
||||
if (!acc.mayRetry() || invalidatedForever.get()) {
|
||||
if (log.isEnabled()) {
|
||||
log.fine("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (acc.hasBeenInvalidated()) {
|
||||
log.info("New chance for " + acc);
|
||||
acc.newSwitchPoint();
|
||||
}
|
||||
|
||||
assert !acc.hasBeenInvalidated();
|
||||
|
||||
// if we haven't given up on this symbol, add a switchpoint invalidation filter to the receiver parameter
|
||||
final MethodHandle target = inv.getInvocation();
|
||||
final Class<?> receiverType = target.type().parameterType(0);
|
||||
final MethodHandle boundInvalidator = MH.bindTo(INVALIDATE_SP, this);
|
||||
final MethodHandle invalidator = MH.asType(boundInvalidator, boundInvalidator.type().changeParameterType(0, receiverType).changeReturnType(receiverType));
|
||||
final MethodHandle mh = MH.filterArguments(inv.getInvocation(), 0, MH.insertArguments(invalidator, 1, acc));
|
||||
|
||||
assert inv.getSwitchPoints() == null : Arrays.asList(inv.getSwitchPoints());
|
||||
log.info("Linked setter " + quote(name) + " " + acc.getSwitchPoint());
|
||||
return new GuardedInvocation(mh, inv.getGuard(), acc.getSwitchPoint(), inv.getException());
|
||||
}
|
||||
|
||||
assert acc.mayRetry();
|
||||
|
||||
if (acc.hasBeenInvalidated()) {
|
||||
log.info("New chance for " + acc);
|
||||
acc.newSwitchPoint();
|
||||
}
|
||||
|
||||
assert !acc.hasBeenInvalidated();
|
||||
|
||||
// if we haven't given up on this symbol, add a switchpoint invalidation filter to the receiver parameter
|
||||
final MethodHandle target = inv.getInvocation();
|
||||
final Class<?> receiverType = target.type().parameterType(0);
|
||||
final MethodHandle boundInvalidator = MH.bindTo(INVALIDATE_SP, this);
|
||||
final MethodHandle invalidator = MH.asType(boundInvalidator, boundInvalidator.type().changeParameterType(0, receiverType).changeReturnType(receiverType));
|
||||
final MethodHandle mh = MH.filterArguments(inv.getInvocation(), 0, MH.insertArguments(invalidator, 1, acc));
|
||||
|
||||
assert inv.getSwitchPoints() == null : Arrays.asList(inv.getSwitchPoints());
|
||||
log.info("Linked setter " + quote(name) + " " + acc.getSwitchPoint());
|
||||
return new GuardedInvocation(mh, inv.getGuard(), acc.getSwitchPoint(), inv.getException());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -380,11 +419,11 @@ public final class GlobalConstants implements Loggable {
|
||||
*
|
||||
* @return resulting getter, or null if failed to create constant
|
||||
*/
|
||||
synchronized GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc) {
|
||||
GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc) {
|
||||
// Only use constant getter for fast scope access, because the receiver may change between invocations
|
||||
// for slow-scope and non-scope callsites.
|
||||
// Also return null for user accessor properties as they may have side effects.
|
||||
if (!NashornCallSiteDescriptor.isFastScope(desc)
|
||||
if (invalidatedForever.get() || !NashornCallSiteDescriptor.isFastScope(desc)
|
||||
|| (GLOBAL_ONLY && !find.getOwner().isGlobal())
|
||||
|| find.getProperty() instanceof UserAccessorProperty) {
|
||||
return null;
|
||||
@ -395,51 +434,53 @@ public final class GlobalConstants implements Loggable {
|
||||
final Class<?> retType = desc.getMethodType().returnType();
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
|
||||
final Access acc = getOrCreateSwitchPoint(name);
|
||||
synchronized (this) {
|
||||
final Access acc = getOrCreateSwitchPoint(name);
|
||||
|
||||
log.fine("Starting to look up object value " + name);
|
||||
final Object c = find.getObjectValue();
|
||||
log.fine("Starting to look up object value " + name);
|
||||
final Object c = find.getObjectValue();
|
||||
|
||||
if (log.isEnabled()) {
|
||||
log.fine("Trying to link constant GETTER " + acc + " value = " + c);
|
||||
}
|
||||
|
||||
if (acc.hasBeenInvalidated() || acc.guardFailed()) {
|
||||
if (log.isEnabled()) {
|
||||
log.info("*** GET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
|
||||
log.fine("Trying to link constant GETTER " + acc + " value = " + c);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
final MethodHandle cmh = constantGetter(c);
|
||||
if (acc.hasBeenInvalidated() || acc.guardFailed() || invalidatedForever.get()) {
|
||||
if (log.isEnabled()) {
|
||||
log.info("*** GET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
MethodHandle mh;
|
||||
MethodHandle guard;
|
||||
final MethodHandle cmh = constantGetter(c);
|
||||
|
||||
if (isOptimistic) {
|
||||
if (JSType.getAccessorTypeIndex(cmh.type().returnType()) <= JSType.getAccessorTypeIndex(retType)) {
|
||||
//widen return type - this is pessimistic, so it will always work
|
||||
mh = MH.asType(cmh, cmh.type().changeReturnType(retType));
|
||||
MethodHandle mh;
|
||||
MethodHandle guard;
|
||||
|
||||
if (isOptimistic) {
|
||||
if (JSType.getAccessorTypeIndex(cmh.type().returnType()) <= JSType.getAccessorTypeIndex(retType)) {
|
||||
//widen return type - this is pessimistic, so it will always work
|
||||
mh = MH.asType(cmh, cmh.type().changeReturnType(retType));
|
||||
} else {
|
||||
//immediately invalidate - we asked for a too wide constant as a narrower one
|
||||
mh = MH.dropArguments(MH.insertArguments(JSType.THROW_UNWARRANTED.methodHandle(), 0, c, programPoint), 0, Object.class);
|
||||
}
|
||||
} else {
|
||||
//immediately invalidate - we asked for a too wide constant as a narrower one
|
||||
mh = MH.dropArguments(MH.insertArguments(JSType.THROW_UNWARRANTED.methodHandle(), 0, c, programPoint), 0, Object.class);
|
||||
//pessimistic return type filter
|
||||
mh = Lookup.filterReturnType(cmh, retType);
|
||||
}
|
||||
} else {
|
||||
//pessimistic return type filter
|
||||
mh = Lookup.filterReturnType(cmh, retType);
|
||||
}
|
||||
|
||||
if (find.getOwner().isGlobal()) {
|
||||
guard = null;
|
||||
} else {
|
||||
guard = MH.insertArguments(RECEIVER_GUARD, 0, acc, receiver);
|
||||
}
|
||||
if (find.getOwner().isGlobal()) {
|
||||
guard = null;
|
||||
} else {
|
||||
guard = MH.insertArguments(RECEIVER_GUARD, 0, acc, receiver);
|
||||
}
|
||||
|
||||
if (log.isEnabled()) {
|
||||
log.info("Linked getter " + quote(name) + " as MethodHandle.constant() -> " + c + " " + acc.getSwitchPoint());
|
||||
mh = MethodHandleFactory.addDebugPrintout(log, Level.FINE, mh, "get const " + acc);
|
||||
}
|
||||
if (log.isEnabled()) {
|
||||
log.info("Linked getter " + quote(name) + " as MethodHandle.constant() -> " + c + " " + acc.getSwitchPoint());
|
||||
mh = MethodHandleFactory.addDebugPrintout(log, Level.FINE, mh, "get const " + acc);
|
||||
}
|
||||
|
||||
return new GuardedInvocation(mh, guard, acc.getSwitchPoint(), null);
|
||||
return new GuardedInvocation(mh, guard, acc.getSwitchPoint(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,6 +150,12 @@ public enum JSType {
|
||||
/** Div exact wrapper for potentially integer division that turns into float point */
|
||||
public static final Call DIV_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", int.class, int.class, int.class, int.class);
|
||||
|
||||
/** Div zero wrapper for integer division that handles (0/0)|0 == 0 */
|
||||
public static final Call DIV_ZERO = staticCall(JSTYPE_LOOKUP, JSType.class, "divZero", int.class, int.class, int.class);
|
||||
|
||||
/** Mod zero wrapper for integer division that handles (0%0)|0 == 0 */
|
||||
public static final Call REM_ZERO = staticCall(JSTYPE_LOOKUP, JSType.class, "remZero", int.class, int.class, int.class);
|
||||
|
||||
/** Mod exact wrapper for potentially integer remainders that turns into float point */
|
||||
public static final Call REM_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "remExact", int.class, int.class, int.class, int.class);
|
||||
|
||||
@ -174,6 +180,12 @@ public enum JSType {
|
||||
/** Div exact wrapper for potentially integer division that turns into float point */
|
||||
public static final Call DIV_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", long.class, long.class, long.class, int.class);
|
||||
|
||||
/** Div zero wrapper for long division that handles (0/0) >>> 0 == 0 */
|
||||
public static final Call DIV_ZERO_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "divZero", long.class, long.class, long.class);
|
||||
|
||||
/** Mod zero wrapper for long division that handles (0%0) >>> 0 == 0 */
|
||||
public static final Call REM_ZERO_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "remZero", long.class, long.class, long.class);
|
||||
|
||||
/** Mod exact wrapper for potentially integer remainders that turns into float point */
|
||||
public static final Call REM_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "remExact", long.class, long.class, long.class, int.class);
|
||||
|
||||
@ -1485,6 +1497,28 @@ public enum JSType {
|
||||
throw new UnwarrantedOptimismException((double)x / (double)y, programPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements int division but allows {@code x / 0} to be represented as 0. Basically equivalent to
|
||||
* {@code (x / y)|0} JavaScript expression (division of two ints coerced to int).
|
||||
* @param x the dividend
|
||||
* @param y the divisor
|
||||
* @return the result
|
||||
*/
|
||||
public static int divZero(final int x, final int y) {
|
||||
return y == 0 ? 0 : x / y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements int remainder but allows {@code x % 0} to be represented as 0. Basically equivalent to
|
||||
* {@code (x % y)|0} JavaScript expression (remainder of two ints coerced to int).
|
||||
* @param x the dividend
|
||||
* @param y the divisor
|
||||
* @return the remainder
|
||||
*/
|
||||
public static int remZero(final int x, final int y) {
|
||||
return y == 0 ? 0 : x % y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for modExact. Throws UnwarrantedOptimismException if the modulo can't be represented as int.
|
||||
*
|
||||
@ -1528,6 +1562,28 @@ public enum JSType {
|
||||
throw new UnwarrantedOptimismException((double)x / (double)y, programPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements long division but allows {@code x / 0} to be represented as 0. Useful when division of two longs
|
||||
* is coerced to long.
|
||||
* @param x the dividend
|
||||
* @param y the divisor
|
||||
* @return the result
|
||||
*/
|
||||
public static long divZero(final long x, final long y) {
|
||||
return y == 0L ? 0L : x / y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements long remainder but allows {@code x % 0} to be represented as 0. Useful when remainder of two longs
|
||||
* is coerced to long.
|
||||
* @param x the dividend
|
||||
* @param y the divisor
|
||||
* @return the remainder
|
||||
*/
|
||||
public static long remZero(final long x, final long y) {
|
||||
return y == 0L ? 0L : x % y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for modExact. Throws UnwarrantedOptimismException if the modulo can't be represented as int.
|
||||
*
|
||||
|
@ -84,7 +84,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
|
||||
private transient WeakHashMap<Property, SoftReference<PropertyMap>> history;
|
||||
|
||||
/** History of prototypes, used to limit map duplication. */
|
||||
private transient WeakHashMap<PropertyMap, SoftReference<PropertyMap>> protoHistory;
|
||||
private transient WeakHashMap<ScriptObject, SoftReference<PropertyMap>> protoHistory;
|
||||
|
||||
/** property listeners */
|
||||
private transient PropertyListeners listeners;
|
||||
@ -677,14 +677,14 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
|
||||
/**
|
||||
* Check prototype history for an existing property map with specified prototype.
|
||||
*
|
||||
* @param parentMap New prototype object.
|
||||
* @param proto New prototype object.
|
||||
*
|
||||
* @return Existing {@link PropertyMap} or {@code null} if not found.
|
||||
*/
|
||||
private PropertyMap checkProtoHistory(final PropertyMap parentMap) {
|
||||
private PropertyMap checkProtoHistory(final ScriptObject proto) {
|
||||
final PropertyMap cachedMap;
|
||||
if (protoHistory != null) {
|
||||
final SoftReference<PropertyMap> weakMap = protoHistory.get(parentMap);
|
||||
final SoftReference<PropertyMap> weakMap = protoHistory.get(proto);
|
||||
cachedMap = (weakMap != null ? weakMap.get() : null);
|
||||
} else {
|
||||
cachedMap = null;
|
||||
@ -700,15 +700,15 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
|
||||
/**
|
||||
* Add a map to the prototype history.
|
||||
*
|
||||
* @param parentMap Prototype to add (key.)
|
||||
* @param newProto Prototype to add (key.)
|
||||
* @param newMap {@link PropertyMap} associated with prototype.
|
||||
*/
|
||||
private void addToProtoHistory(final PropertyMap parentMap, final PropertyMap newMap) {
|
||||
private void addToProtoHistory(final ScriptObject newProto, final PropertyMap newMap) {
|
||||
if (protoHistory == null) {
|
||||
protoHistory = new WeakHashMap<>();
|
||||
}
|
||||
|
||||
protoHistory.put(parentMap, new SoftReference<>(newMap));
|
||||
protoHistory.put(newProto, new SoftReference<>(newMap));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -883,8 +883,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
|
||||
*/
|
||||
public PropertyMap changeProto(final ScriptObject newProto) {
|
||||
|
||||
final PropertyMap parentMap = newProto == null ? null : newProto.getMap();
|
||||
final PropertyMap nextMap = checkProtoHistory(parentMap);
|
||||
final PropertyMap nextMap = checkProtoHistory(newProto);
|
||||
if (nextMap != null) {
|
||||
return nextMap;
|
||||
}
|
||||
@ -894,7 +893,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
|
||||
}
|
||||
|
||||
final PropertyMap newMap = new PropertyMap(this);
|
||||
addToProtoHistory(parentMap, newMap);
|
||||
addToProtoHistory(newProto, newMap);
|
||||
|
||||
return newMap;
|
||||
}
|
||||
|
@ -475,6 +475,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
* @return either the existing map, or a loaded map from the persistent type info cache, or a new empty map if
|
||||
* neither an existing map or a persistent cached type info is available.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private static Map<Integer, Type> getEffectiveInvalidatedProgramPoints(
|
||||
final Map<Integer, Type> invalidatedProgramPoints, final Object typeInformationFile) {
|
||||
if(invalidatedProgramPoints != null) {
|
||||
@ -727,7 +728,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
|
||||
assert existingBest != null;
|
||||
//we are calling a vararg method with real args
|
||||
boolean applyToCall = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
|
||||
boolean varArgWithRealArgs = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
|
||||
|
||||
//if the best one is an apply to call, it has to match the callsite exactly
|
||||
//or we need to regenerate
|
||||
@ -736,14 +737,16 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
if (best != null) {
|
||||
return best;
|
||||
}
|
||||
applyToCall = true;
|
||||
varArgWithRealArgs = true;
|
||||
}
|
||||
|
||||
if (applyToCall) {
|
||||
if (varArgWithRealArgs) {
|
||||
// special case: we had an apply to call, but we failed to make it fit.
|
||||
// Try to generate a specialized one for this callsite. It may
|
||||
// be another apply to call specialization, or it may not, but whatever
|
||||
// it is, it is a specialization that is guaranteed to fit
|
||||
final FunctionInitializer fnInit = compileTypeSpecialization(callSiteType, runtimeScope, false);
|
||||
if ((fnInit.getFlags() & FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION) != 0) { //did the specialization work
|
||||
existingBest = addCode(fnInit, callSiteType);
|
||||
}
|
||||
existingBest = addCode(fnInit, callSiteType);
|
||||
}
|
||||
|
||||
return existingBest;
|
||||
|
@ -212,6 +212,7 @@ public final class ScriptEnvironment {
|
||||
* @param out output print writer
|
||||
* @param err error print writer
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public ScriptEnvironment(final Options options, final PrintWriter out, final PrintWriter err) {
|
||||
this.out = out;
|
||||
this.err = err;
|
||||
|
@ -47,6 +47,7 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
|
||||
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
|
||||
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
|
||||
import static jdk.nashorn.internal.runtime.linker.NashornGuards.explicitInstanceOfCheck;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
@ -922,7 +923,10 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
if (property instanceof UserAccessorProperty) {
|
||||
((UserAccessorProperty)property).setAccessors(this, getMap(), null);
|
||||
}
|
||||
Global.getConstants().delete(property.getKey());
|
||||
final GlobalConstants globalConstants = getGlobalConstants();
|
||||
if (globalConstants != null) {
|
||||
globalConstants.delete(property.getKey());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1983,9 +1987,12 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
}
|
||||
}
|
||||
|
||||
final GuardedInvocation cinv = Global.getConstants().findGetMethod(find, this, desc);
|
||||
if (cinv != null) {
|
||||
return cinv;
|
||||
final GlobalConstants globalConstants = getGlobalConstants();
|
||||
if (globalConstants != null) {
|
||||
final GuardedInvocation cinv = globalConstants.findGetMethod(find, this, desc);
|
||||
if (cinv != null) {
|
||||
return cinv;
|
||||
}
|
||||
}
|
||||
|
||||
final Class<?> returnType = desc.getMethodType().returnType();
|
||||
@ -2183,14 +2190,22 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
|
||||
final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(findBuiltinSwitchPoint(name));
|
||||
|
||||
final GuardedInvocation cinv = Global.getConstants().findSetMethod(find, this, inv, desc, request);
|
||||
if (cinv != null) {
|
||||
return cinv;
|
||||
final GlobalConstants globalConstants = getGlobalConstants();
|
||||
if (globalConstants != null) {
|
||||
final GuardedInvocation cinv = globalConstants.findSetMethod(find, this, inv, desc, request);
|
||||
if (cinv != null) {
|
||||
return cinv;
|
||||
}
|
||||
}
|
||||
|
||||
return inv;
|
||||
}
|
||||
|
||||
private GlobalConstants getGlobalConstants() {
|
||||
// Avoid hitting getContext() which might be costly for a non-Global unless needed.
|
||||
return GlobalConstants.GLOBAL_ONLY && !isGlobal() ? null : getContext().getGlobalConstants();
|
||||
}
|
||||
|
||||
private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String strictErrorMessage, final boolean canBeFastScope) {
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
if (NashornCallSiteDescriptor.isStrict(desc)) {
|
||||
|
@ -98,6 +98,10 @@ public abstract class ArrayData {
|
||||
@Override
|
||||
public ArrayData ensure(final long safeIndex) {
|
||||
if (safeIndex > 0L) {
|
||||
if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) {
|
||||
return new SparseArrayData(this, safeIndex + 1);
|
||||
}
|
||||
//known to fit in int
|
||||
return toRealArrayData((int)safeIndex).ensure(safeIndex);
|
||||
}
|
||||
return this;
|
||||
|
@ -36,7 +36,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
* Handle arrays where the index is very large.
|
||||
*/
|
||||
class SparseArrayData extends ArrayData {
|
||||
static final long MAX_DENSE_LENGTH = 16 * 512 * 1024;
|
||||
static final int MAX_DENSE_LENGTH = 8 * 1024 * 1024;
|
||||
|
||||
/** Underlying array. */
|
||||
private ArrayData underlying;
|
||||
|
@ -47,10 +47,9 @@ public final class RecompilationEvent extends RuntimeEvent<RewriteException> {
|
||||
* @param level logging level
|
||||
* @param rewriteException rewriteException wrapped by this RuntimEvent
|
||||
* @param returnValue rewriteException return value - as we don't want to make
|
||||
* {@link RewriteException#getReturnValueNonDestructive()} public, we pass it as
|
||||
* {@code RewriteException.getReturnValueNonDestructive()} public, we pass it as
|
||||
* an extra parameter, rather than querying the getter from another package.
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public RecompilationEvent(final Level level, final RewriteException rewriteException, final Object returnValue) {
|
||||
super(level, rewriteException);
|
||||
assert Context.getContext().getLogger(RecompilableScriptFunctionData.class).isEnabled() :
|
||||
|
@ -42,6 +42,8 @@ import jdk.internal.dynalink.beans.StaticClass;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
import jdk.internal.dynalink.linker.LinkerServices;
|
||||
import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
|
||||
import jdk.internal.dynalink.support.TypeUtilities;
|
||||
import jdk.nashorn.api.scripting.JSObject;
|
||||
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||
@ -106,6 +108,12 @@ public final class Bootstrap {
|
||||
return OptimisticReturnFilters.filterOptimisticReturnValue(inv, desc).asType(linkerServices, desc.getMethodType());
|
||||
}
|
||||
});
|
||||
factory.setAutoConversionStrategy(new MethodTypeConversionStrategy() {
|
||||
@Override
|
||||
public MethodHandle asType(final MethodHandle target, final MethodType newType) {
|
||||
return unboxReturnType(target, newType);
|
||||
}
|
||||
});
|
||||
final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD);
|
||||
if (relinkThreshold > -1) {
|
||||
factory.setUnstableRelinkThreshold(relinkThreshold);
|
||||
@ -420,4 +428,29 @@ public final class Bootstrap {
|
||||
static GuardedInvocation asTypeSafeReturn(final GuardedInvocation inv, final LinkerServices linkerServices, final CallSiteDescriptor desc) {
|
||||
return inv == null ? null : inv.asTypeSafeReturn(linkerServices, desc.getMethodType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts the return type of the method handle with {@code explicitCastArguments} when it is an unboxing
|
||||
* conversion. This will ensure that nulls are unwrapped to false or 0.
|
||||
* @param target the target method handle
|
||||
* @param newType the desired new type. Note that this method does not adapt the method handle completely to the
|
||||
* new type, it only adapts the return type; this is allowed as per
|
||||
* {@link DynamicLinkerFactory#setAutoConversionStrategy(MethodTypeConversionStrategy)}, which is what this method
|
||||
* is used for.
|
||||
* @return the method handle with adapted return type, if it required an unboxing conversion.
|
||||
*/
|
||||
private static MethodHandle unboxReturnType(final MethodHandle target, final MethodType newType) {
|
||||
final MethodType targetType = target.type();
|
||||
final Class<?> oldReturnType = targetType.returnType();
|
||||
if (TypeUtilities.isWrapperType(oldReturnType)) {
|
||||
final Class<?> newReturnType = newType.returnType();
|
||||
if (newReturnType.isPrimitive()) {
|
||||
// The contract of setAutoConversionStrategy is such that the difference between newType and targetType
|
||||
// can only be JLS method invocation conversions.
|
||||
assert TypeUtilities.isMethodInvocationConvertible(oldReturnType, newReturnType);
|
||||
return MethodHandles.explicitCastArguments(target, targetType.changeReturnType(newReturnType));
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,10 @@
|
||||
|
||||
package jdk.nashorn.internal.runtime.linker;
|
||||
|
||||
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.*;
|
||||
|
||||
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_GETMEMBER;
|
||||
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_GETSLOT;
|
||||
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_SETMEMBER;
|
||||
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_SETSLOT;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
@ -114,12 +116,10 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
|
||||
case "getMethod":
|
||||
if (c > 2) {
|
||||
return findGetMethod(desc);
|
||||
} else {
|
||||
// For indexed get, we want GuardedInvocation from beans linker and pass it.
|
||||
// BrowserJSObjectLinker.get uses this fallback getter for explicit signature method access.
|
||||
final GuardedInvocation beanInv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
|
||||
return findGetIndexMethod(beanInv);
|
||||
}
|
||||
// For indexed get, we want GuardedInvocation from beans linker and pass it.
|
||||
// BrowserJSObjectLinker.get uses this fallback getter for explicit signature method access.
|
||||
return findGetIndexMethod(nashornBeansLinker.getGuardedInvocation(request, linkerServices));
|
||||
case "setProp":
|
||||
case "setElem":
|
||||
return c > 2 ? findSetMethod(desc) : findSetIndexMethod();
|
||||
@ -166,9 +166,8 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
|
||||
final String name = (String)key;
|
||||
if (name.indexOf('(') != -1) {
|
||||
return fallback.invokeExact(jsobj, key);
|
||||
} else {
|
||||
return JSOBJECT_GETMEMBER.invokeExact(jsobj, (String)key);
|
||||
}
|
||||
return JSOBJECT_GETMEMBER.invokeExact(jsobj, (String)key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -120,12 +120,10 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTy
|
||||
case "getMethod":
|
||||
if (c > 2) {
|
||||
return findGetMethod(desc);
|
||||
} else {
|
||||
// For indexed get, we want get GuardedInvocation beans linker and pass it.
|
||||
// JSObjectLinker.get uses this fallback getter for explicit signature method access.
|
||||
final GuardedInvocation beanInv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
|
||||
return findGetIndexMethod(beanInv);
|
||||
}
|
||||
// For indexed get, we want get GuardedInvocation beans linker and pass it.
|
||||
// JSObjectLinker.get uses this fallback getter for explicit signature method access.
|
||||
return findGetIndexMethod(nashornBeansLinker.getGuardedInvocation(request, linkerServices));
|
||||
case "setProp":
|
||||
case "setElem":
|
||||
return c > 2 ? findSetMethod(desc) : findSetIndexMethod();
|
||||
@ -192,9 +190,8 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTy
|
||||
// get with method name and signature. delegate it to beans linker!
|
||||
if (name.indexOf('(') != -1) {
|
||||
return fallback.invokeExact(jsobj, key);
|
||||
} else {
|
||||
return ((JSObject)jsobj).getMember(name);
|
||||
}
|
||||
return ((JSObject)jsobj).getMember(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -53,15 +53,34 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
|
||||
// Object type arguments of Java method calls, field set and array set.
|
||||
private static final boolean MIRROR_ALWAYS = Options.getBooleanProperty("nashorn.mirror.always", true);
|
||||
|
||||
private static final MethodHandle EXPORT_ARGUMENT = new Lookup(MethodHandles.lookup()).findOwnStatic("exportArgument", Object.class, Object.class);
|
||||
private static final MethodHandle EXPORT_NATIVE_ARRAY = new Lookup(MethodHandles.lookup()).findOwnStatic("exportNativeArray", Object.class, NativeArray.class);
|
||||
private static final MethodHandle EXPORT_SCRIPT_OBJECT = new Lookup(MethodHandles.lookup()).findOwnStatic("exportScriptObject", Object.class, ScriptObject.class);
|
||||
private static final MethodHandle IMPORT_RESULT = new Lookup(MethodHandles.lookup()).findOwnStatic("importResult", Object.class, Object.class);
|
||||
private static final MethodHandle EXPORT_ARGUMENT;
|
||||
private static final MethodHandle EXPORT_NATIVE_ARRAY;
|
||||
private static final MethodHandle EXPORT_SCRIPT_OBJECT;
|
||||
private static final MethodHandle IMPORT_RESULT;
|
||||
private static final MethodHandle FILTER_CONSSTRING;
|
||||
|
||||
static {
|
||||
final Lookup lookup = new Lookup(MethodHandles.lookup());
|
||||
EXPORT_ARGUMENT = lookup.findOwnStatic("exportArgument", Object.class, Object.class);
|
||||
EXPORT_NATIVE_ARRAY = lookup.findOwnStatic("exportNativeArray", Object.class, NativeArray.class);
|
||||
EXPORT_SCRIPT_OBJECT = lookup.findOwnStatic("exportScriptObject", Object.class, ScriptObject.class);
|
||||
IMPORT_RESULT = lookup.findOwnStatic("importResult", Object.class, Object.class);
|
||||
FILTER_CONSSTRING = lookup.findOwnStatic("consStringFilter", Object.class, Object.class);
|
||||
}
|
||||
|
||||
private final BeansLinker beansLinker = new BeansLinker();
|
||||
|
||||
@Override
|
||||
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
|
||||
if (linkRequest.getReceiver() instanceof ConsString) {
|
||||
// In order to treat ConsString like a java.lang.String we need a link request with a string receiver.
|
||||
final Object[] arguments = linkRequest.getArguments();
|
||||
arguments[0] = "";
|
||||
final LinkRequest forgedLinkRequest = linkRequest.replaceArguments(linkRequest.getCallSiteDescriptor(), arguments);
|
||||
final GuardedInvocation invocation = getGuardedInvocation(beansLinker, forgedLinkRequest, linkerServices);
|
||||
// If an invocation is found we add a filter that makes it work for both Strings and ConsStrings.
|
||||
return invocation == null ? null : invocation.filterArguments(0, FILTER_CONSSTRING);
|
||||
}
|
||||
return getGuardedInvocation(beansLinker, linkRequest, linkerServices);
|
||||
}
|
||||
|
||||
@ -113,6 +132,11 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
|
||||
return ScriptUtils.unwrap(arg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static Object consStringFilter(final Object arg) {
|
||||
return arg instanceof ConsString ? arg.toString() : arg;
|
||||
}
|
||||
|
||||
private static class NashornBeansLinkerServices implements LinkerServices {
|
||||
private final LinkerServices linkerServices;
|
||||
|
||||
|
@ -27,7 +27,6 @@ import static jdk.nashorn.internal.runtime.regexp.joni.Option.isIgnoreCase;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isMultiline;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.ast.ConsAltNode.newAltNode;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.isRepeatInfinite;
|
||||
|
||||
import java.util.HashSet;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ast.AnchorNode;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ast.BackRefNode;
|
||||
@ -53,6 +52,7 @@ final class Analyser extends Parser {
|
||||
super(env, chars, p, end);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
protected final void compile() {
|
||||
if (Config.DEBUG) {
|
||||
Config.log.println(new String(chars, getBegin(), getEnd()));
|
||||
@ -76,7 +76,9 @@ final class Analyser extends Parser {
|
||||
|
||||
root = setupTree(root, 0);
|
||||
if (Config.DEBUG_PARSE_TREE) {
|
||||
if (Config.DEBUG_PARSE_TREE_RAW) Config.log.println("<TREE>");
|
||||
if (Config.DEBUG_PARSE_TREE_RAW) {
|
||||
Config.log.println("<TREE>");
|
||||
}
|
||||
root.verifyTree(new HashSet<Node>(), env.reg.warnings);
|
||||
Config.log.println(root + "\n");
|
||||
}
|
||||
@ -94,7 +96,9 @@ final class Analyser extends Parser {
|
||||
|
||||
regex.clearOptimizeInfo();
|
||||
|
||||
if (!Config.DONT_OPTIMIZE) setOptimizedInfoFromTree(root);
|
||||
if (!Config.DONT_OPTIMIZE) {
|
||||
setOptimizedInfoFromTree(root);
|
||||
}
|
||||
|
||||
env.memNodes = null;
|
||||
|
||||
@ -110,7 +114,9 @@ final class Analyser extends Parser {
|
||||
|
||||
if (Config.DEBUG_COMPILE) {
|
||||
Config.log.println("stack used: " + regex.stackNeeded);
|
||||
if (Config.USE_STRING_TEMPLATES) Config.log.print("templates: " + regex.templateNum + "\n");
|
||||
if (Config.USE_STRING_TEMPLATES) {
|
||||
Config.log.print("templates: " + regex.templateNum + "\n");
|
||||
}
|
||||
Config.log.println(new ByteCodePrinter(regex).byteCodeListToString());
|
||||
|
||||
} // DEBUG_COMPILE
|
||||
@ -136,7 +142,9 @@ final class Analyser extends Parser {
|
||||
ConsAltNode can = (ConsAltNode)node;
|
||||
do {
|
||||
final int v = quantifiersMemoryInfo(can.car);
|
||||
if (v > info) info = v;
|
||||
if (v > info) {
|
||||
info = v;
|
||||
}
|
||||
} while ((can = can.cdr) != null);
|
||||
break;
|
||||
|
||||
@ -182,7 +190,9 @@ final class Analyser extends Parser {
|
||||
switch (node.getType()) {
|
||||
case NodeType.BREF:
|
||||
final BackRefNode br = (BackRefNode)node;
|
||||
if (br.isRecursion()) break;
|
||||
if (br.isRecursion()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (br.backRef > env.numMem) {
|
||||
throw new ValueException(ERR_INVALID_BACKREF);
|
||||
@ -249,6 +259,9 @@ final class Analyser extends Parser {
|
||||
case EncloseType.STOP_BACKTRACK:
|
||||
min = getMinMatchLength(en.target);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
} // inner switch
|
||||
break;
|
||||
|
||||
@ -276,7 +289,9 @@ final class Analyser extends Parser {
|
||||
ConsAltNode an = (ConsAltNode)node;
|
||||
do {
|
||||
final int tmax = getMaxMatchLength(an.car);
|
||||
if (max < tmax) max = tmax;
|
||||
if (max < tmax) {
|
||||
max = tmax;
|
||||
}
|
||||
} while ((an = an.cdr) != null);
|
||||
break;
|
||||
|
||||
@ -304,7 +319,9 @@ final class Analyser extends Parser {
|
||||
throw new ValueException(ERR_INVALID_BACKREF);
|
||||
}
|
||||
final int tmax = getMaxMatchLength(env.memNodes[br.backRef]);
|
||||
if (max < tmax) max = tmax;
|
||||
if (max < tmax) {
|
||||
max = tmax;
|
||||
}
|
||||
break;
|
||||
|
||||
case NodeType.QTFR:
|
||||
@ -338,6 +355,9 @@ final class Analyser extends Parser {
|
||||
case EncloseType.STOP_BACKTRACK:
|
||||
max = getMaxMatchLength(en.target);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
} // inner switch
|
||||
break;
|
||||
|
||||
@ -355,8 +375,8 @@ final class Analyser extends Parser {
|
||||
return getCharLengthTree(node, 0);
|
||||
}
|
||||
|
||||
private int getCharLengthTree(final Node node, int level) {
|
||||
level++;
|
||||
private int getCharLengthTree(final Node node, final int levelp) {
|
||||
final int level = levelp + 1;
|
||||
|
||||
int len = 0;
|
||||
returnCode = 0;
|
||||
@ -366,7 +386,9 @@ final class Analyser extends Parser {
|
||||
ConsAltNode ln = (ConsAltNode)node;
|
||||
do {
|
||||
final int tlen = getCharLengthTree(ln.car, level);
|
||||
if (returnCode == 0) len = MinMaxLen.distanceAdd(len, tlen);
|
||||
if (returnCode == 0) {
|
||||
len = MinMaxLen.distanceAdd(len, tlen);
|
||||
}
|
||||
} while (returnCode == 0 && (ln = ln.cdr) != null);
|
||||
break;
|
||||
|
||||
@ -378,7 +400,9 @@ final class Analyser extends Parser {
|
||||
while (returnCode == 0 && (an = an.cdr) != null) {
|
||||
final int tlen2 = getCharLengthTree(an.car, level);
|
||||
if (returnCode == 0) {
|
||||
if (tlen != tlen2) varLen = true;
|
||||
if (tlen != tlen2) {
|
||||
varLen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,7 +428,9 @@ final class Analyser extends Parser {
|
||||
final QuantifierNode qn = (QuantifierNode)node;
|
||||
if (qn.lower == qn.upper) {
|
||||
tlen = getCharLengthTree(qn.target, level);
|
||||
if (returnCode == 0) len = MinMaxLen.distanceMultiply(tlen, qn.lower);
|
||||
if (returnCode == 0) {
|
||||
len = MinMaxLen.distanceMultiply(tlen, qn.lower);
|
||||
}
|
||||
} else {
|
||||
returnCode = GET_CHAR_LEN_VARLEN;
|
||||
}
|
||||
@ -435,6 +461,9 @@ final class Analyser extends Parser {
|
||||
case EncloseType.STOP_BACKTRACK:
|
||||
len = getCharLengthTree(en.target, level);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
} // inner switch
|
||||
break;
|
||||
|
||||
@ -448,7 +477,9 @@ final class Analyser extends Parser {
|
||||
}
|
||||
|
||||
/* x is not included y ==> 1 : 0 */
|
||||
private boolean isNotIncluded(Node x, Node y) {
|
||||
private static boolean isNotIncluded(final Node xn, final Node yn) {
|
||||
Node x = xn;
|
||||
Node y = yn;
|
||||
Node tmp;
|
||||
|
||||
// !retry:!
|
||||
@ -492,10 +523,14 @@ final class Analyser extends Parser {
|
||||
boolean v = xc.bs.at(i);
|
||||
if ((v && !xc.isNot()) || (!v && xc.isNot())) {
|
||||
v = yc.bs.at(i);
|
||||
if ((v && !yc.isNot()) || (!v && yc.isNot())) return false;
|
||||
if ((v && !yc.isNot()) || (!v && yc.isNot())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((xc.mbuf == null && !xc.isNot()) || yc.mbuf == null && !yc.isNot()) return true;
|
||||
if ((xc.mbuf == null && !xc.isNot()) || yc.mbuf == null && !yc.isNot()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
// break; not reached
|
||||
|
||||
@ -514,7 +549,9 @@ final class Analyser extends Parser {
|
||||
|
||||
case NodeType.STR:
|
||||
final StringNode xs = (StringNode)x;
|
||||
if (xs.length() == 0) break;
|
||||
if (xs.length() == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (yType) {
|
||||
|
||||
@ -526,13 +563,16 @@ final class Analyser extends Parser {
|
||||
case NodeType.STR:
|
||||
final StringNode ys = (StringNode)y;
|
||||
int len = xs.length();
|
||||
if (len > ys.length()) len = ys.length();
|
||||
if (len > ys.length()) {
|
||||
len = ys.length();
|
||||
}
|
||||
if (xs.isAmbig() || ys.isAmbig()) {
|
||||
/* tiny version */
|
||||
return false;
|
||||
} else {
|
||||
for (int i=0, p=ys.p, q=xs.p; i<len; i++, p++, q++) {
|
||||
if (ys.chars[p] != xs.chars[q]) return true;
|
||||
}
|
||||
for (int i=0, pt=ys.p, q=xs.p; i<len; i++, pt++, q++) {
|
||||
if (ys.chars[pt] != xs.chars[q]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -542,6 +582,8 @@ final class Analyser extends Parser {
|
||||
} // inner switch
|
||||
|
||||
break; // case NodeType.STR
|
||||
default:
|
||||
break;
|
||||
|
||||
} // switch
|
||||
|
||||
@ -561,7 +603,9 @@ final class Analyser extends Parser {
|
||||
|
||||
case NodeType.CTYPE:
|
||||
case NodeType.CCLASS:
|
||||
if (!exact) n = node;
|
||||
if (!exact) {
|
||||
n = node;
|
||||
}
|
||||
break;
|
||||
|
||||
case NodeType.LIST:
|
||||
@ -570,7 +614,10 @@ final class Analyser extends Parser {
|
||||
|
||||
case NodeType.STR:
|
||||
final StringNode sn = (StringNode)node;
|
||||
if (sn.end <= sn.p) break; // ???
|
||||
if (sn.end <= sn.p)
|
||||
{
|
||||
break; // ???
|
||||
}
|
||||
|
||||
if (exact && !sn.isRaw() && isIgnoreCase(regex.options)){
|
||||
// nothing
|
||||
@ -605,12 +652,17 @@ final class Analyser extends Parser {
|
||||
case EncloseType.STOP_BACKTRACK:
|
||||
n = getHeadValueNode(en.target, exact);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
} // inner switch
|
||||
break;
|
||||
|
||||
case NodeType.ANCHOR:
|
||||
final AnchorNode an = (AnchorNode)node;
|
||||
if (an.type == AnchorType.PREC_READ) n = getHeadValueNode(an.target, exact);
|
||||
if (an.type == AnchorType.PREC_READ) {
|
||||
n = getHeadValueNode(an.target, exact);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -622,7 +674,9 @@ final class Analyser extends Parser {
|
||||
|
||||
// true: invalid
|
||||
private boolean checkTypeTree(final Node node, final int typeMask, final int encloseMask, final int anchorMask) {
|
||||
if ((node.getType2Bit() & typeMask) == 0) return true;
|
||||
if ((node.getType2Bit() & typeMask) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean invalid = false;
|
||||
|
||||
@ -641,15 +695,21 @@ final class Analyser extends Parser {
|
||||
|
||||
case NodeType.ENCLOSE:
|
||||
final EncloseNode en = (EncloseNode)node;
|
||||
if ((en.type & encloseMask) == 0) return true;
|
||||
if ((en.type & encloseMask) == 0) {
|
||||
return true;
|
||||
}
|
||||
invalid = checkTypeTree(en.target, typeMask, encloseMask, anchorMask);
|
||||
break;
|
||||
|
||||
case NodeType.ANCHOR:
|
||||
final AnchorNode an = (AnchorNode)node;
|
||||
if ((an.type & anchorMask) == 0) return true;
|
||||
if ((an.type & anchorMask) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (an.target != null) invalid = checkTypeTree(an.target, typeMask, encloseMask, anchorMask);
|
||||
if (an.target != null) {
|
||||
invalid = checkTypeTree(an.target, typeMask, encloseMask, anchorMask);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -664,7 +724,8 @@ final class Analyser extends Parser {
|
||||
(?<=A|B) ==> (?<=A)|(?<=B)
|
||||
(?<!A|B) ==> (?<!A)(?<!B)
|
||||
*/
|
||||
private Node divideLookBehindAlternatives(Node node) {
|
||||
private Node divideLookBehindAlternatives(final Node nodep) {
|
||||
Node node = nodep;
|
||||
final AnchorNode an = (AnchorNode)node;
|
||||
final int anchorType = an.type;
|
||||
Node head = an.target;
|
||||
@ -699,7 +760,7 @@ final class Analyser extends Parser {
|
||||
private Node setupLookBehind(final Node node) {
|
||||
final AnchorNode an = (AnchorNode)node;
|
||||
final int len = getCharLengthTree(an.target);
|
||||
switch(returnCode) {
|
||||
switch (returnCode) {
|
||||
case 0:
|
||||
an.charLength = len;
|
||||
break;
|
||||
@ -708,14 +769,17 @@ final class Analyser extends Parser {
|
||||
case GET_CHAR_LEN_TOP_ALT_VARLEN:
|
||||
if (syntax.differentLengthAltLookBehind()) {
|
||||
return divideLookBehindAlternatives(node);
|
||||
} else {
|
||||
throw new SyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
|
||||
}
|
||||
throw new SyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private void nextSetup(Node node, final Node nextNode) {
|
||||
private void nextSetup(final Node nodep, final Node nextNode) {
|
||||
Node node = nodep;
|
||||
|
||||
// retry:
|
||||
retry: while(true) {
|
||||
|
||||
@ -762,7 +826,7 @@ final class Analyser extends Parser {
|
||||
}
|
||||
|
||||
private void updateStringNodeCaseFoldMultiByte(final StringNode sn) {
|
||||
final char[] chars = sn.chars;
|
||||
final char[] ch = sn.chars;
|
||||
final int end = sn.end;
|
||||
value = sn.p;
|
||||
int sp = 0;
|
||||
@ -770,15 +834,15 @@ final class Analyser extends Parser {
|
||||
|
||||
while (value < end) {
|
||||
final int ovalue = value;
|
||||
buf = EncodingHelper.toLowerCase(chars[value++]);
|
||||
buf = EncodingHelper.toLowerCase(ch[value++]);
|
||||
|
||||
if (chars[ovalue] != buf) {
|
||||
if (ch[ovalue] != buf) {
|
||||
|
||||
char[] sbuf = new char[sn.length() << 1];
|
||||
System.arraycopy(chars, sn.p, sbuf, 0, ovalue - sn.p);
|
||||
System.arraycopy(ch, sn.p, sbuf, 0, ovalue - sn.p);
|
||||
value = ovalue;
|
||||
while (value < end) {
|
||||
buf = EncodingHelper.toLowerCase(chars[value++]);
|
||||
buf = EncodingHelper.toLowerCase(ch[value++]);
|
||||
if (sp >= sbuf.length) {
|
||||
final char[]tmp = new char[sbuf.length << 1];
|
||||
System.arraycopy(sbuf, 0, tmp, 0, sbuf.length);
|
||||
@ -798,8 +862,8 @@ final class Analyser extends Parser {
|
||||
updateStringNodeCaseFoldMultiByte(sn);
|
||||
}
|
||||
|
||||
private Node expandCaseFoldMakeRemString(final char[] chars, final int p, final int end) {
|
||||
final StringNode node = new StringNode(chars, p, end);
|
||||
private Node expandCaseFoldMakeRemString(final char[] ch, final int pp, final int end) {
|
||||
final StringNode node = new StringNode(ch, pp, end);
|
||||
|
||||
updateStringNodeCaseFold(node);
|
||||
node.setAmbig();
|
||||
@ -807,7 +871,7 @@ final class Analyser extends Parser {
|
||||
return node;
|
||||
}
|
||||
|
||||
private boolean expandCaseFoldStringAlt(final int itemNum, final char[] items,
|
||||
private static boolean expandCaseFoldStringAlt(final int itemNum, final char[] items,
|
||||
final char[] chars, final int p, final int slen, final int end, final ObjPtr<Node> node) {
|
||||
|
||||
ConsAltNode altNode;
|
||||
@ -833,59 +897,68 @@ final class Analyser extends Parser {
|
||||
private Node expandCaseFoldString(final Node node) {
|
||||
final StringNode sn = (StringNode)node;
|
||||
|
||||
if (sn.isAmbig() || sn.length() <= 0) return node;
|
||||
if (sn.isAmbig() || sn.length() <= 0) {
|
||||
return node;
|
||||
}
|
||||
|
||||
final char[] chars = sn.chars;
|
||||
int p = sn.p;
|
||||
final char[] chars1 = sn.chars;
|
||||
int pt = sn.p;
|
||||
final int end = sn.end;
|
||||
int altNum = 1;
|
||||
|
||||
ConsAltNode topRoot = null, root = null;
|
||||
ConsAltNode topRoot = null, r = null;
|
||||
@SuppressWarnings("unused")
|
||||
final ObjPtr<Node> prevNode = new ObjPtr<Node>();
|
||||
StringNode stringNode = null;
|
||||
|
||||
while (p < end) {
|
||||
final char[] items = EncodingHelper.caseFoldCodesByString(regex.caseFoldFlag, chars[p]);
|
||||
while (pt < end) {
|
||||
final char[] items = EncodingHelper.caseFoldCodesByString(regex.caseFoldFlag, chars1[pt]);
|
||||
|
||||
if (items.length == 0) {
|
||||
if (stringNode == null) {
|
||||
if (root == null && prevNode.p != null) {
|
||||
topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
|
||||
if (r == null && prevNode.p != null) {
|
||||
topRoot = r = ConsAltNode.listAdd(null, prevNode.p);
|
||||
}
|
||||
|
||||
prevNode.p = stringNode = new StringNode(); // onig_node_new_str(NULL, NULL);
|
||||
|
||||
if (root != null) ConsAltNode.listAdd(root, stringNode);
|
||||
if (r != null) {
|
||||
ConsAltNode.listAdd(r, stringNode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
stringNode.cat(chars, p, p + 1);
|
||||
stringNode.cat(chars1, pt, pt + 1);
|
||||
} else {
|
||||
altNum *= (items.length + 1);
|
||||
if (altNum > THRESHOLD_CASE_FOLD_ALT_FOR_EXPANSION) break;
|
||||
|
||||
if (root == null && prevNode.p != null) {
|
||||
topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
|
||||
if (altNum > THRESHOLD_CASE_FOLD_ALT_FOR_EXPANSION) {
|
||||
break;
|
||||
}
|
||||
|
||||
expandCaseFoldStringAlt(items.length, items, chars, p, 1, end, prevNode);
|
||||
if (root != null) ConsAltNode.listAdd(root, prevNode.p);
|
||||
if (r == null && prevNode.p != null) {
|
||||
topRoot = r = ConsAltNode.listAdd(null, prevNode.p);
|
||||
}
|
||||
|
||||
expandCaseFoldStringAlt(items.length, items, chars1, pt, 1, end, prevNode);
|
||||
if (r != null) {
|
||||
ConsAltNode.listAdd(r, prevNode.p);
|
||||
}
|
||||
stringNode = null;
|
||||
}
|
||||
p++;
|
||||
pt++;
|
||||
}
|
||||
|
||||
if (p < end) {
|
||||
final Node srem = expandCaseFoldMakeRemString(chars, p, end);
|
||||
if (pt < end) {
|
||||
final Node srem = expandCaseFoldMakeRemString(chars1, pt, end);
|
||||
|
||||
if (prevNode.p != null && root == null) {
|
||||
topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
|
||||
if (prevNode.p != null && r == null) {
|
||||
topRoot = r = ConsAltNode.listAdd(null, prevNode.p);
|
||||
}
|
||||
|
||||
if (root == null) {
|
||||
if (r == null) {
|
||||
prevNode.p = srem;
|
||||
} else {
|
||||
ConsAltNode.listAdd(root, srem);
|
||||
ConsAltNode.listAdd(r, srem);
|
||||
}
|
||||
}
|
||||
/* ending */
|
||||
@ -909,7 +982,10 @@ final class Analyser extends Parser {
|
||||
5. find invalid patterns in look-behind.
|
||||
6. expand repeated string.
|
||||
*/
|
||||
protected final Node setupTree(Node node, int state) {
|
||||
protected final Node setupTree(final Node nodep, final int statep) {
|
||||
Node node = nodep;
|
||||
int state = statep;
|
||||
|
||||
restart: while (true) {
|
||||
switch (node.getType()) {
|
||||
case NodeType.LIST:
|
||||
@ -958,7 +1034,9 @@ final class Analyser extends Parser {
|
||||
final QuantifierNode qn = (QuantifierNode)node;
|
||||
Node target = qn.target;
|
||||
|
||||
if ((state & IN_REPEAT) != 0) qn.setInRepeat();
|
||||
if ((state & IN_REPEAT) != 0) {
|
||||
qn.setInRepeat();
|
||||
}
|
||||
|
||||
if (isRepeatInfinite(qn.upper) || qn.lower >= 1) {
|
||||
final int d = getMinMatchLength(target);
|
||||
@ -966,14 +1044,18 @@ final class Analyser extends Parser {
|
||||
qn.targetEmptyInfo = TargetInfo.IS_EMPTY;
|
||||
if (Config.USE_MONOMANIAC_CHECK_CAPTURES_IN_ENDLESS_REPEAT) {
|
||||
final int info = quantifiersMemoryInfo(target);
|
||||
if (info > 0) qn.targetEmptyInfo = info;
|
||||
if (info > 0) {
|
||||
qn.targetEmptyInfo = info;
|
||||
}
|
||||
} // USE_INFINITE_REPEAT_MONOMANIAC_MEM_STATUS_CHECK
|
||||
// strange stuff here (turned off)
|
||||
}
|
||||
}
|
||||
|
||||
state |= IN_REPEAT;
|
||||
if (qn.lower != qn.upper) state |= IN_VAR_REPEAT;
|
||||
if (qn.lower != qn.upper) {
|
||||
state |= IN_VAR_REPEAT;
|
||||
}
|
||||
|
||||
target = setupTree(target, state);
|
||||
|
||||
@ -1035,11 +1117,16 @@ final class Analyser extends Parser {
|
||||
final QuantifierNode tqn = (QuantifierNode)en.target;
|
||||
if (isRepeatInfinite(tqn.upper) && tqn.lower <= 1 && tqn.greedy) {
|
||||
/* (?>a*), a*+ etc... */
|
||||
if (tqn.target.isSimple()) en.setStopBtSimpleRepeat();
|
||||
if (tqn.target.isSimple()) {
|
||||
en.setStopBtSimpleRepeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
} // inner switch
|
||||
break;
|
||||
|
||||
@ -1059,7 +1146,9 @@ final class Analyser extends Parser {
|
||||
throw new SyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
|
||||
}
|
||||
node = setupLookBehind(node);
|
||||
if (node.getType() != NodeType.ANCHOR) continue restart;
|
||||
if (node.getType() != NodeType.ANCHOR) {
|
||||
continue restart;
|
||||
}
|
||||
setupTree(((AnchorNode)node).target, state);
|
||||
break;
|
||||
|
||||
@ -1068,12 +1157,19 @@ final class Analyser extends Parser {
|
||||
throw new SyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
|
||||
}
|
||||
node = setupLookBehind(node);
|
||||
if (node.getType() != NodeType.ANCHOR) continue restart;
|
||||
if (node.getType() != NodeType.ANCHOR) {
|
||||
continue restart;
|
||||
}
|
||||
setupTree(((AnchorNode)node).target, (state | IN_NOT));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
} // inner switch
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
return node;
|
||||
} // restart: while
|
||||
@ -1191,7 +1287,9 @@ final class Analyser extends Parser {
|
||||
opt.expr.copy(nopt.exm);
|
||||
}
|
||||
opt.expr.reachEnd = false;
|
||||
if (nopt.map.value > 0) opt.map.copy(nopt.map);
|
||||
if (nopt.map.value > 0) {
|
||||
opt.map.copy(nopt.map);
|
||||
}
|
||||
break;
|
||||
|
||||
case AnchorType.PREC_READ_NOT:
|
||||
@ -1199,6 +1297,9 @@ final class Analyser extends Parser {
|
||||
case AnchorType.LOOK_BEHIND_NOT:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
} // inner switch
|
||||
break;
|
||||
}
|
||||
@ -1282,8 +1383,12 @@ final class Analyser extends Parser {
|
||||
if (++en.optCount > MAX_NODE_OPT_INFO_REF_COUNT) {
|
||||
int min = 0;
|
||||
int max = MinMaxLen.INFINITE_DISTANCE;
|
||||
if (en.isMinFixed()) min = en.minLength;
|
||||
if (en.isMaxFixed()) max = en.maxLength;
|
||||
if (en.isMinFixed()) {
|
||||
min = en.minLength;
|
||||
}
|
||||
if (en.isMaxFixed()) {
|
||||
max = en.maxLength;
|
||||
}
|
||||
opt.length.set(min, max);
|
||||
} else { // USE_SUBEXP_CALL
|
||||
optimizeNodeLeft(en.target, opt, oenv);
|
||||
@ -1298,6 +1403,9 @@ final class Analyser extends Parser {
|
||||
case EncloseType.STOP_BACKTRACK:
|
||||
optimizeNodeLeft(en.target, opt, oenv);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
} // inner switch
|
||||
break;
|
||||
}
|
||||
@ -1307,6 +1415,7 @@ final class Analyser extends Parser {
|
||||
} // switch
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
protected final void setOptimizedInfoFromTree(final Node node) {
|
||||
final NodeOptInfo opt = new NodeOptInfo();
|
||||
final OptEnvironment oenv = new OptEnvironment();
|
||||
@ -1347,7 +1456,9 @@ final class Analyser extends Parser {
|
||||
regex.setSubAnchor(opt.map.anchor);
|
||||
} else {
|
||||
regex.subAnchor |= opt.anchor.leftAnchor & AnchorType.BEGIN_LINE;
|
||||
if (opt.length.max == 0) regex.subAnchor |= opt.anchor.rightAnchor & AnchorType.END_LINE;
|
||||
if (opt.length.max == 0) {
|
||||
regex.subAnchor |= opt.anchor.rightAnchor & AnchorType.END_LINE;
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.DEBUG_COMPILE || Config.DEBUG_MATCH) {
|
||||
|
@ -24,7 +24,7 @@ import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
|
||||
final class ApplyCaseFold {
|
||||
|
||||
// i_apply_case_fold
|
||||
public void apply(final int from, final int to, final Object o) {
|
||||
public static void apply(final int from, final int to, final Object o) {
|
||||
final ApplyCaseFoldArg arg = (ApplyCaseFoldArg)o;
|
||||
|
||||
final ScanEnvironment env = arg.env;
|
||||
@ -45,7 +45,9 @@ final class ApplyCaseFold {
|
||||
} else {
|
||||
if (inCC) {
|
||||
if (to >= BitSet.SINGLE_BYTE_SIZE) {
|
||||
if (cc.isNot()) cc.clearNotFlag();
|
||||
if (cc.isNot()) {
|
||||
cc.clearNotFlag();
|
||||
}
|
||||
cc.addCodeRange(env, to, to);
|
||||
} else {
|
||||
if (cc.isNot()) {
|
||||
|
@ -22,6 +22,7 @@ package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ast.ConsAltNode;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class ApplyCaseFoldArg {
|
||||
final ScanEnvironment env;
|
||||
final CClassNode cc;
|
||||
|
@ -24,7 +24,6 @@ import static jdk.nashorn.internal.runtime.regexp.joni.Option.isDynamic;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isIgnoreCase;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isMultiline;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.isRepeatInfinite;
|
||||
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ast.AnchorNode;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ast.BackRefNode;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
|
||||
@ -98,15 +97,15 @@ final class ArrayCompiler extends Compiler {
|
||||
} while ((aln = aln.cdr) != null);
|
||||
}
|
||||
|
||||
private boolean isNeedStrLenOpExact(final int op) {
|
||||
private static boolean isNeedStrLenOpExact(final int op) {
|
||||
return op == OPCode.EXACTN || op == OPCode.EXACTN_IC;
|
||||
}
|
||||
|
||||
private boolean opTemplated(final int op) {
|
||||
private static boolean opTemplated(final int op) {
|
||||
return isNeedStrLenOpExact(op);
|
||||
}
|
||||
|
||||
private int selectStrOpcode(final int strLength, final boolean ignoreCase) {
|
||||
private static int selectStrOpcode(final int strLength, final boolean ignoreCase) {
|
||||
int op;
|
||||
|
||||
if (ignoreCase) {
|
||||
@ -139,7 +138,7 @@ final class ArrayCompiler extends Compiler {
|
||||
compileTree(node);
|
||||
|
||||
if (emptyInfo != 0) {
|
||||
switch(emptyInfo) {
|
||||
switch (emptyInfo) {
|
||||
case TargetInfo.IS_EMPTY:
|
||||
addOpcode(OPCode.NULL_CHECK_END);
|
||||
break;
|
||||
@ -149,13 +148,15 @@ final class ArrayCompiler extends Compiler {
|
||||
case TargetInfo.IS_EMPTY_REC:
|
||||
addOpcode(OPCode.NULL_CHECK_END_MEMST_PUSH);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
|
||||
addMemNum(savedNumNullCheck); /* NULL CHECK ID */
|
||||
}
|
||||
}
|
||||
|
||||
private int addCompileStringlength(final char[] chars, final int p, final int strLength, final boolean ignoreCase) {
|
||||
private static int addCompileStringlength(final char[] chars, final int p, final int strLength, final boolean ignoreCase) {
|
||||
final int op = selectStrOpcode(strLength, ignoreCase);
|
||||
int len = OPSize.OPCODE;
|
||||
|
||||
@ -163,7 +164,9 @@ final class ArrayCompiler extends Compiler {
|
||||
// string length, template index, template string pointer
|
||||
len += OPSize.LENGTH + OPSize.INDEX + OPSize.INDEX;
|
||||
} else {
|
||||
if (isNeedStrLenOpExact(op)) len += OPSize.LENGTH;
|
||||
if (isNeedStrLenOpExact(op)) {
|
||||
len += OPSize.LENGTH;
|
||||
}
|
||||
len += strLength;
|
||||
}
|
||||
return len;
|
||||
@ -187,9 +190,11 @@ final class ArrayCompiler extends Compiler {
|
||||
}
|
||||
}
|
||||
|
||||
private int compileLengthStringNode(final Node node) {
|
||||
private static int compileLengthStringNode(final Node node) {
|
||||
final StringNode sn = (StringNode)node;
|
||||
if (sn.length() <= 0) return 0;
|
||||
if (sn.length() <= 0) {
|
||||
return 0;
|
||||
}
|
||||
final boolean ambig = sn.isAmbig();
|
||||
|
||||
int p, prev;
|
||||
@ -210,8 +215,10 @@ final class ArrayCompiler extends Compiler {
|
||||
return rlen;
|
||||
}
|
||||
|
||||
private int compileLengthStringRawNode(final StringNode sn) {
|
||||
if (sn.length() <= 0) return 0;
|
||||
private static int compileLengthStringRawNode(final StringNode sn) {
|
||||
if (sn.length() <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return addCompileStringlength(sn.chars, sn.p, sn.length(), false);
|
||||
}
|
||||
|
||||
@ -220,8 +227,10 @@ final class ArrayCompiler extends Compiler {
|
||||
addInts(mbuf.p, mbuf.used);
|
||||
}
|
||||
|
||||
private int compileLengthCClassNode(final CClassNode cc) {
|
||||
if (cc.isShare()) return OPSize.OPCODE + OPSize.POINTER;
|
||||
private static int compileLengthCClassNode(final CClassNode cc) {
|
||||
if (cc.isShare()) {
|
||||
return OPSize.OPCODE + OPSize.POINTER;
|
||||
}
|
||||
|
||||
int len;
|
||||
if (cc.mbuf == null) {
|
||||
@ -360,9 +369,8 @@ final class ArrayCompiler extends Compiler {
|
||||
if (qn.greedy && infinite) {
|
||||
if (qn.nextHeadExact != null) {
|
||||
return OPSize.ANYCHAR_STAR_PEEK_NEXT + tlen * qn.lower;
|
||||
} else {
|
||||
return OPSize.ANYCHAR_STAR + tlen * qn.lower;
|
||||
}
|
||||
return OPSize.ANYCHAR_STAR + tlen * qn.lower;
|
||||
}
|
||||
}
|
||||
|
||||
@ -425,14 +433,13 @@ final class ArrayCompiler extends Compiler {
|
||||
final StringNode sn = (StringNode)qn.nextHeadExact;
|
||||
addChars(sn.chars, sn.p, 1);
|
||||
return;
|
||||
} else {
|
||||
if (isMultiline(regex.options)) {
|
||||
addOpcode(OPCode.ANYCHAR_ML_STAR);
|
||||
} else {
|
||||
addOpcode(OPCode.ANYCHAR_STAR);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isMultiline(regex.options)) {
|
||||
addOpcode(OPCode.ANYCHAR_ML_STAR);
|
||||
} else {
|
||||
addOpcode(OPCode.ANYCHAR_STAR);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int modTLen;
|
||||
@ -510,9 +517,8 @@ final class ArrayCompiler extends Compiler {
|
||||
|
||||
if (isDynamic(prev ^ node.option)) {
|
||||
return OPSize.SET_OPTION_PUSH + OPSize.SET_OPTION + OPSize.FAIL + tlen + OPSize.SET_OPTION;
|
||||
} else {
|
||||
return tlen;
|
||||
}
|
||||
return tlen;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -675,13 +681,15 @@ final class ArrayCompiler extends Compiler {
|
||||
break;
|
||||
|
||||
case AnchorType.WORD_BEGIN:
|
||||
if (Config.USE_WORD_BEGIN_END)
|
||||
if (Config.USE_WORD_BEGIN_END) {
|
||||
addOpcode(OPCode.WORD_BEGIN);
|
||||
}
|
||||
break;
|
||||
|
||||
case AnchorType.WORD_END:
|
||||
if (Config.USE_WORD_BEGIN_END)
|
||||
if (Config.USE_WORD_BEGIN_END) {
|
||||
addOpcode(OPCode.WORD_END);
|
||||
}
|
||||
break;
|
||||
|
||||
case AnchorType.PREC_READ:
|
||||
@ -701,7 +709,9 @@ final class ArrayCompiler extends Compiler {
|
||||
addOpcode(OPCode.LOOK_BEHIND);
|
||||
if (node.charLength < 0) {
|
||||
n = analyser.getCharLengthTree(node.target);
|
||||
if (analyser.returnCode != 0) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
|
||||
if (analyser.returnCode != 0) {
|
||||
newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
|
||||
}
|
||||
} else {
|
||||
n = node.charLength;
|
||||
}
|
||||
@ -714,7 +724,9 @@ final class ArrayCompiler extends Compiler {
|
||||
addOpcodeRelAddr(OPCode.PUSH_LOOK_BEHIND_NOT, len + OPSize.FAIL_LOOK_BEHIND_NOT);
|
||||
if (node.charLength < 0) {
|
||||
n = analyser.getCharLengthTree(node.target);
|
||||
if (analyser.returnCode != 0) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
|
||||
if (analyser.returnCode != 0) {
|
||||
newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
|
||||
}
|
||||
} else {
|
||||
n = node.charLength;
|
||||
}
|
||||
@ -796,7 +808,9 @@ final class ArrayCompiler extends Compiler {
|
||||
private void ensure(final int size) {
|
||||
if (size >= code.length) {
|
||||
int length = code.length << 1;
|
||||
while (length <= size) length <<= 1;
|
||||
while (length <= size) {
|
||||
length <<= 1;
|
||||
}
|
||||
final int[]tmp = new int[length];
|
||||
System.arraycopy(code, 0, tmp, 0, code.length);
|
||||
code = tmp;
|
||||
@ -829,11 +843,14 @@ final class ArrayCompiler extends Compiler {
|
||||
regex.operands[regex.operandLength++] = o;
|
||||
}
|
||||
|
||||
private void addChars(final char[] chars, int p ,final int length) {
|
||||
private void addChars(final char[] chars, final int pp ,final int length) {
|
||||
ensure(codeLength + length);
|
||||
int p = pp;
|
||||
final int end = p + length;
|
||||
|
||||
while (p < end) code[codeLength++] = chars[p++];
|
||||
while (p < end) {
|
||||
code[codeLength++] = chars[p++];
|
||||
}
|
||||
}
|
||||
|
||||
private void addInts(final int[]ints, final int length) {
|
||||
@ -876,6 +893,9 @@ final class ArrayCompiler extends Compiler {
|
||||
case OPCode.CALL:
|
||||
case OPCode.RETURN: // it will appear only with CALL though
|
||||
regex.stackNeeded = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class BitSet {
|
||||
static final int BITS_PER_BYTE = 8;
|
||||
public static final int SINGLE_BYTE_SIZE = (1 << BITS_PER_BYTE);
|
||||
@ -34,7 +35,9 @@ public final class BitSet {
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("BitSet");
|
||||
for (int i=0; i<SINGLE_BYTE_SIZE; i++) {
|
||||
if ((i % (SINGLE_BYTE_SIZE / BITS_TO_STRING_WRAP)) == 0) buffer.append("\n ");
|
||||
if ((i % (SINGLE_BYTE_SIZE / BITS_TO_STRING_WRAP)) == 0) {
|
||||
buffer.append("\n ");
|
||||
}
|
||||
buffer.append(at(i) ? "1" : "0");
|
||||
}
|
||||
return buffer.toString();
|
||||
@ -53,44 +56,62 @@ public final class BitSet {
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
for (int i=0; i<BITSET_SIZE; i++) bits[i]=0;
|
||||
for (int i=0; i<BITSET_SIZE; i++) {
|
||||
bits[i]=0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
for (int i=0; i<BITSET_SIZE; i++) {
|
||||
if (bits[i] != 0) return false;
|
||||
if (bits[i] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setRange(final int from, final int to) {
|
||||
for (int i=from; i<=to && i < SINGLE_BYTE_SIZE; i++) set(i);
|
||||
for (int i=from; i<=to && i < SINGLE_BYTE_SIZE; i++) {
|
||||
set(i);
|
||||
}
|
||||
}
|
||||
|
||||
public void invert() {
|
||||
for (int i=0; i<BITSET_SIZE; i++) bits[i] = ~bits[i];
|
||||
for (int i=0; i<BITSET_SIZE; i++) {
|
||||
bits[i] = ~bits[i];
|
||||
}
|
||||
}
|
||||
|
||||
public void invertTo(final BitSet to) {
|
||||
for (int i=0; i<BITSET_SIZE; i++) to.bits[i] = ~bits[i];
|
||||
for (int i=0; i<BITSET_SIZE; i++) {
|
||||
to.bits[i] = ~bits[i];
|
||||
}
|
||||
}
|
||||
|
||||
public void and(final BitSet other) {
|
||||
for (int i=0; i<BITSET_SIZE; i++) bits[i] &= other.bits[i];
|
||||
for (int i=0; i<BITSET_SIZE; i++) {
|
||||
bits[i] &= other.bits[i];
|
||||
}
|
||||
}
|
||||
|
||||
public void or(final BitSet other) {
|
||||
for (int i=0; i<BITSET_SIZE; i++) bits[i] |= other.bits[i];
|
||||
for (int i=0; i<BITSET_SIZE; i++) {
|
||||
bits[i] |= other.bits[i];
|
||||
}
|
||||
}
|
||||
|
||||
public void copy(final BitSet other) {
|
||||
for (int i=0; i<BITSET_SIZE; i++) bits[i] = other.bits[i];
|
||||
for (int i=0; i<BITSET_SIZE; i++) {
|
||||
bits[i] = other.bits[i];
|
||||
}
|
||||
}
|
||||
|
||||
public int numOn() {
|
||||
int num = 0;
|
||||
for (int i=0; i<SINGLE_BYTE_SIZE; i++) {
|
||||
if (at(i)) num++;
|
||||
if (at(i)) {
|
||||
num++;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
@ -99,9 +120,12 @@ public final class BitSet {
|
||||
return 1 << (pos % SINGLE_BYTE_SIZE);
|
||||
}
|
||||
|
||||
private static int log2(int n){
|
||||
private static int log2(final int np) {
|
||||
int log = 0;
|
||||
while ((n >>>= 1) != 0) log++;
|
||||
int n = np;
|
||||
while ((n >>>= 1) != 0) {
|
||||
log++;
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,8 @@ final class BitStatus {
|
||||
return (n < BIT_STATUS_BITS_NUM ? stats & (1 << n) : (stats & 1)) != 0;
|
||||
}
|
||||
|
||||
public static int bsOnAt(int stats, final int n) {
|
||||
public static int bsOnAt(final int statsp, final int n) {
|
||||
int stats = statsp;
|
||||
if (n < BIT_STATUS_BITS_NUM) {
|
||||
stats |= (1 << n);
|
||||
} else {
|
||||
@ -43,12 +44,7 @@ final class BitStatus {
|
||||
return stats;
|
||||
}
|
||||
|
||||
public static int bsOnOff(int v, final int f, final boolean negative) {
|
||||
if (negative) {
|
||||
v &= ~f;
|
||||
} else {
|
||||
v |= f;
|
||||
}
|
||||
return v;
|
||||
public static int bsOnOff(final int v, final int f, final boolean negative) {
|
||||
return negative ? (v & ~f) : (v | f);
|
||||
}
|
||||
}
|
||||
|
@ -27,10 +27,8 @@ import static jdk.nashorn.internal.runtime.regexp.joni.Option.isFindNotEmpty;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isNotBol;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isNotEol;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isPosixRegion;
|
||||
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.constants.OPCode;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.constants.OPSize;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.encoding.IntHolder;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.exception.ErrorMessages;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
|
||||
@ -52,8 +50,8 @@ class ByteCodeMachine extends StackMachine {
|
||||
this.code = regex.code;
|
||||
}
|
||||
|
||||
private boolean stringCmpIC(final int caseFlodFlag, int s1, final IntHolder ps2, final int mbLen, final int textEnd) {
|
||||
|
||||
private boolean stringCmpIC(final int caseFlodFlag, final int s1p, final IntHolder ps2, final int mbLen, final int textEnd) {
|
||||
int s1 = s1p;
|
||||
int s2 = ps2.value;
|
||||
final int end1 = s1 + mbLen;
|
||||
|
||||
@ -83,12 +81,16 @@ class ByteCodeMachine extends StackMachine {
|
||||
Config.log.printf("%4d", (s - str)).print("> \"");
|
||||
int q, i;
|
||||
for (i=0, q=s; i<7 && q<end && s>=0; i++) {
|
||||
if (q < end) Config.log.print(new String(new char[]{chars[q++]}));
|
||||
if (q < end) {
|
||||
Config.log.print(new String(new char[]{chars[q++]}));
|
||||
}
|
||||
}
|
||||
final String string = q < end ? "...\"" : "\"";
|
||||
q += string.length();
|
||||
Config.log.print(string);
|
||||
for (i=0; i<20-(q-s);i++) {
|
||||
Config.log.print(" ");
|
||||
}
|
||||
final String str = q < end ? "...\"" : "\"";
|
||||
q += str.length();
|
||||
Config.log.print(str);
|
||||
for (i=0; i<20-(q-s);i++) Config.log.print(" ");
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
new ByteCodePrinter(regex).compiledByteCodeToString(sb, ip);
|
||||
Config.log.println(sb.toString());
|
||||
@ -96,28 +98,34 @@ class ByteCodeMachine extends StackMachine {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final int matchAt(final int range, final int sstart, final int sprev) {
|
||||
this.range = range;
|
||||
this.sstart = sstart;
|
||||
this.sprev = sprev;
|
||||
protected final int matchAt(final int r, final int ss, final int sp) {
|
||||
this.range = r;
|
||||
this.sstart = ss;
|
||||
this.sprev = sp;
|
||||
|
||||
stk = 0;
|
||||
ip = 0;
|
||||
|
||||
if (Config.DEBUG_MATCH) debugMatchBegin();
|
||||
if (Config.DEBUG_MATCH) {
|
||||
debugMatchBegin();
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
bestLen = -1;
|
||||
s = sstart;
|
||||
s = ss;
|
||||
|
||||
final int[]code = this.code;
|
||||
final int[] c = this.code;
|
||||
while (true) {
|
||||
if (Config.DEBUG_MATCH) debugMatchLoop();
|
||||
if (Config.DEBUG_MATCH) {
|
||||
debugMatchLoop();
|
||||
}
|
||||
|
||||
sbegin = s;
|
||||
switch (code[ip++]) {
|
||||
case OPCode.END: if (opEnd()) return finish(); break;
|
||||
switch (c[ip++]) {
|
||||
case OPCode.END: if (opEnd()) {
|
||||
return finish();
|
||||
} break;
|
||||
case OPCode.EXACT1: opExact1(); break;
|
||||
case OPCode.EXACT2: opExact2(); continue;
|
||||
case OPCode.EXACT3: opExact3(); continue;
|
||||
@ -358,10 +366,14 @@ class ByteCodeMachine extends StackMachine {
|
||||
final char[] bs = regex.templates[code[ip++]];
|
||||
int ps = code[ip++];
|
||||
|
||||
while (tlen-- > 0) if (bs[ps++] != chars[s++]) {opFail(); return;}
|
||||
while (tlen-- > 0) {
|
||||
if (bs[ps++] != chars[s++]) {opFail(); return;}
|
||||
}
|
||||
|
||||
} else {
|
||||
while (tlen-- > 0) if (code[ip++] != chars[s++]) {opFail(); return;}
|
||||
while (tlen-- > 0) {
|
||||
if (code[ip++] != chars[s++]) {opFail(); return;}
|
||||
}
|
||||
}
|
||||
sprev = s - 1;
|
||||
}
|
||||
@ -380,10 +392,14 @@ class ByteCodeMachine extends StackMachine {
|
||||
final char[] bs = regex.templates[code[ip++]];
|
||||
int ps = code[ip++];
|
||||
|
||||
while (tlen-- > 0) if (bs[ps++] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
|
||||
while (tlen-- > 0) {
|
||||
if (bs[ps++] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
|
||||
}
|
||||
} else {
|
||||
|
||||
while (tlen-- > 0) if (code[ip++] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
|
||||
while (tlen-- > 0) {
|
||||
if (code[ip++] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
|
||||
}
|
||||
}
|
||||
sprev = s - 1;
|
||||
}
|
||||
@ -402,11 +418,15 @@ class ByteCodeMachine extends StackMachine {
|
||||
|
||||
private boolean isInClassMB() {
|
||||
final int tlen = code[ip++];
|
||||
if (s >= range) return false;
|
||||
if (s >= range) {
|
||||
return false;
|
||||
}
|
||||
final int ss = s;
|
||||
s++;
|
||||
final int c = chars[ss];
|
||||
if (!EncodingHelper.isInCodeRange(code, ip, c)) return false;
|
||||
if (!EncodingHelper.isInCodeRange(code, ip, c)) {
|
||||
return false;
|
||||
}
|
||||
ip += tlen;
|
||||
return true;
|
||||
}
|
||||
@ -444,7 +464,9 @@ class ByteCodeMachine extends StackMachine {
|
||||
final int tlen = code[ip++];
|
||||
|
||||
if (!(s + 1 <= range)) {
|
||||
if (s >= range) return false;
|
||||
if (s >= range) {
|
||||
return false;
|
||||
}
|
||||
s = end;
|
||||
ip += tlen;
|
||||
return true;
|
||||
@ -454,7 +476,9 @@ class ByteCodeMachine extends StackMachine {
|
||||
s++;
|
||||
final int c = chars[ss];
|
||||
|
||||
if (EncodingHelper.isInCodeRange(code, ip, c)) return false;
|
||||
if (EncodingHelper.isInCodeRange(code, ip, c)) {
|
||||
return false;
|
||||
}
|
||||
ip += tlen;
|
||||
return true;
|
||||
}
|
||||
@ -511,10 +535,10 @@ class ByteCodeMachine extends StackMachine {
|
||||
}
|
||||
|
||||
private void opAnyCharStar() {
|
||||
final char[] chars = this.chars;
|
||||
final char[] ch = this.chars;
|
||||
while (s < range) {
|
||||
pushAlt(ip, s, sprev);
|
||||
if (isNewLine(chars, s, end)) {opFail(); return;}
|
||||
if (isNewLine(ch, s, end)) {opFail(); return;}
|
||||
sprev = s;
|
||||
s++;
|
||||
}
|
||||
@ -532,11 +556,13 @@ class ByteCodeMachine extends StackMachine {
|
||||
|
||||
private void opAnyCharStarPeekNext() {
|
||||
final char c = (char)code[ip];
|
||||
final char[] chars = this.chars;
|
||||
final char[] ch = this.chars;
|
||||
|
||||
while (s < range) {
|
||||
final char b = chars[s];
|
||||
if (c == b) pushAlt(ip + 1, s, sprev);
|
||||
final char b = ch[s];
|
||||
if (c == b) {
|
||||
pushAlt(ip + 1, s, sprev);
|
||||
}
|
||||
if (isNewLine(b)) {opFail(); return;}
|
||||
sprev = s;
|
||||
s++;
|
||||
@ -547,10 +573,12 @@ class ByteCodeMachine extends StackMachine {
|
||||
|
||||
private void opAnyCharMLStarPeekNext() {
|
||||
final char c = (char)code[ip];
|
||||
final char[] chars = this.chars;
|
||||
final char[] ch = this.chars;
|
||||
|
||||
while (s < range) {
|
||||
if (c == chars[s]) pushAlt(ip + 1, s, sprev);
|
||||
if (c == ch[s]) {
|
||||
pushAlt(ip + 1, s, sprev);
|
||||
}
|
||||
sprev = s;
|
||||
s++;
|
||||
}
|
||||
@ -592,29 +620,39 @@ class ByteCodeMachine extends StackMachine {
|
||||
|
||||
private void opWordBegin() {
|
||||
if (s < range && EncodingHelper.isWord(chars[s])) {
|
||||
if (s == str || !EncodingHelper.isWord(chars[sprev])) return;
|
||||
if (s == str || !EncodingHelper.isWord(chars[sprev])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
opFail();
|
||||
}
|
||||
|
||||
private void opWordEnd() {
|
||||
if (s != str && EncodingHelper.isWord(chars[sprev])) {
|
||||
if (s == end || !EncodingHelper.isWord(chars[s])) return;
|
||||
if (s == end || !EncodingHelper.isWord(chars[s])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
opFail();
|
||||
}
|
||||
|
||||
private void opBeginBuf() {
|
||||
if (s != str) opFail();
|
||||
if (s != str) {
|
||||
opFail();
|
||||
}
|
||||
}
|
||||
|
||||
private void opEndBuf() {
|
||||
if (s != end) opFail();
|
||||
if (s != end) {
|
||||
opFail();
|
||||
}
|
||||
}
|
||||
|
||||
private void opBeginLine() {
|
||||
if (s == str) {
|
||||
if (isNotBol(msaOptions)) opFail();
|
||||
if (isNotBol(msaOptions)) {
|
||||
opFail();
|
||||
}
|
||||
return;
|
||||
} else if (isNewLine(chars, sprev, end) && s != end) {
|
||||
return;
|
||||
@ -626,13 +664,16 @@ class ByteCodeMachine extends StackMachine {
|
||||
if (s == end) {
|
||||
if (Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
|
||||
if (str == end || !isNewLine(chars, sprev, end)) {
|
||||
if (isNotEol(msaOptions)) opFail();
|
||||
if (isNotEol(msaOptions)) {
|
||||
opFail();
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (isNotEol(msaOptions)) opFail();
|
||||
return;
|
||||
}
|
||||
if (isNotEol(msaOptions)) {
|
||||
opFail();
|
||||
}
|
||||
return;
|
||||
} else if (isNewLine(chars, s, end)) {
|
||||
return;
|
||||
}
|
||||
@ -643,13 +684,16 @@ class ByteCodeMachine extends StackMachine {
|
||||
if (s == end) {
|
||||
if (Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
|
||||
if (str == end || !isNewLine(chars, sprev, end)) {
|
||||
if (isNotEol(msaOptions)) opFail();
|
||||
if (isNotEol(msaOptions)) {
|
||||
opFail();
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (isNotEol(msaOptions)) opFail();
|
||||
return;
|
||||
}
|
||||
if (isNotEol(msaOptions)) {
|
||||
opFail();
|
||||
}
|
||||
return;
|
||||
} else if (isNewLine(chars, s, end) && s + 1 == end) {
|
||||
return;
|
||||
}
|
||||
@ -657,7 +701,9 @@ class ByteCodeMachine extends StackMachine {
|
||||
}
|
||||
|
||||
private void opBeginPosition() {
|
||||
if (s != msaStart) opFail();
|
||||
if (s != msaStart) {
|
||||
opFail();
|
||||
}
|
||||
}
|
||||
|
||||
private void opMemoryStartPush() {
|
||||
@ -726,11 +772,15 @@ class ByteCodeMachine extends StackMachine {
|
||||
sprev = s;
|
||||
|
||||
// STRING_CMP
|
||||
while(n-- > 0) if (chars[pstart++] != chars[s++]) {opFail(); return;}
|
||||
while(n-- > 0) {
|
||||
if (chars[pstart++] != chars[s++]) {opFail(); return;}
|
||||
}
|
||||
|
||||
// beyond string check
|
||||
if (sprev < range) {
|
||||
while (sprev + 1 < s) sprev++;
|
||||
while (sprev + 1 < s) {
|
||||
sprev++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -764,7 +814,9 @@ class ByteCodeMachine extends StackMachine {
|
||||
s = value;
|
||||
|
||||
// if (sprev < chars.length)
|
||||
while (sprev + 1 < s) sprev++;
|
||||
while (sprev + 1 < s) {
|
||||
sprev++;
|
||||
}
|
||||
}
|
||||
|
||||
private void opBackRefMulti() {
|
||||
@ -773,7 +825,9 @@ class ByteCodeMachine extends StackMachine {
|
||||
int i;
|
||||
loop:for (i=0; i<tlen; i++) {
|
||||
final int mem = code[ip++];
|
||||
if (backrefInvalid(mem)) continue;
|
||||
if (backrefInvalid(mem)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int pstart = backrefStart(mem);
|
||||
final int pend = backrefEnd(mem);
|
||||
@ -785,14 +839,18 @@ class ByteCodeMachine extends StackMachine {
|
||||
int swork = s;
|
||||
|
||||
while (n-- > 0) {
|
||||
if (chars[pstart++] != chars[swork++]) continue loop;
|
||||
if (chars[pstart++] != chars[swork++]) {
|
||||
continue loop;
|
||||
}
|
||||
}
|
||||
|
||||
s = swork;
|
||||
|
||||
// beyond string check
|
||||
if (sprev < range) {
|
||||
while (sprev + 1 < s) sprev++;
|
||||
while (sprev + 1 < s) {
|
||||
sprev++;
|
||||
}
|
||||
}
|
||||
|
||||
ip += tlen - i - 1; // * SIZE_MEMNUM (1)
|
||||
@ -807,7 +865,9 @@ class ByteCodeMachine extends StackMachine {
|
||||
int i;
|
||||
loop:for (i=0; i<tlen; i++) {
|
||||
final int mem = code[ip++];
|
||||
if (backrefInvalid(mem)) continue;
|
||||
if (backrefInvalid(mem)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final int pstart = backrefStart(mem);
|
||||
final int pend = backrefEnd(mem);
|
||||
@ -818,11 +878,16 @@ class ByteCodeMachine extends StackMachine {
|
||||
sprev = s;
|
||||
|
||||
value = s;
|
||||
if (!stringCmpIC(regex.caseFoldFlag, pstart, this, n, end)) continue loop; // STRING_CMP_VALUE_IC
|
||||
if (!stringCmpIC(regex.caseFoldFlag, pstart, this, n, end))
|
||||
{
|
||||
continue loop; // STRING_CMP_VALUE_IC
|
||||
}
|
||||
s = value;
|
||||
|
||||
// if (sprev < chars.length)
|
||||
while (sprev + 1 < s) sprev++;
|
||||
while (sprev + 1 < s) {
|
||||
sprev++;
|
||||
}
|
||||
|
||||
ip += tlen - i - 1; // * SIZE_MEMNUM (1)
|
||||
break; /* success */
|
||||
@ -830,10 +895,12 @@ class ByteCodeMachine extends StackMachine {
|
||||
if (i == tlen) {opFail(); return;}
|
||||
}
|
||||
|
||||
private boolean memIsInMemp(final int mem, final int num, int memp) {
|
||||
for (int i=0; i<num; i++) {
|
||||
private boolean memIsInMemp(final int mem, final int num, final int mempp) {
|
||||
for (int i=0, memp = mempp; i<num; i++) {
|
||||
final int m = code[memp++];
|
||||
if (mem == m) return true;
|
||||
if (mem == m) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -857,7 +924,9 @@ class ByteCodeMachine extends StackMachine {
|
||||
if (memIsInMemp(e.getMemNum(), memNum, memp)) {
|
||||
final int pstart = e.getMemPStr();
|
||||
if (pend != -1) {
|
||||
if (pend - pstart > end - s) return false; /* or goto next_mem; */
|
||||
if (pend - pstart > end - s) {
|
||||
return false; /* or goto next_mem; */
|
||||
}
|
||||
int p = pstart;
|
||||
|
||||
value = s;
|
||||
@ -867,7 +936,9 @@ class ByteCodeMachine extends StackMachine {
|
||||
}
|
||||
} else {
|
||||
while (p < pend) {
|
||||
if (chars[p++] != chars[value++]) return false; /* or goto next_mem; */
|
||||
if (chars[p++] != chars[value++]) {
|
||||
return false; /* or goto next_mem; */
|
||||
}
|
||||
}
|
||||
}
|
||||
s = value;
|
||||
@ -893,24 +964,15 @@ class ByteCodeMachine extends StackMachine {
|
||||
|
||||
sprev = s;
|
||||
if (backrefMatchAtNestedLevel(ic != 0, regex.caseFoldFlag, level, tlen, ip)) { // (s) and (end) implicit
|
||||
while (sprev + 1 < s) sprev++;
|
||||
while (sprev + 1 < s) {
|
||||
sprev++;
|
||||
}
|
||||
ip += tlen; // * SIZE_MEMNUM
|
||||
} else {
|
||||
{opFail(); return;}
|
||||
}
|
||||
}
|
||||
|
||||
/* no need: IS_DYNAMIC_OPTION() == 0 */
|
||||
private void opSetOptionPush() {
|
||||
// option = code[ip++]; // final for now
|
||||
pushAlt(ip, s, sprev);
|
||||
ip += OPSize.SET_OPTION + OPSize.FAIL;
|
||||
}
|
||||
|
||||
private void opSetOption() {
|
||||
// option = code[ip++]; // final for now
|
||||
}
|
||||
|
||||
private void opNullCheckStart() {
|
||||
final int mem = code[ip++];
|
||||
pushNullCheckStart(mem, s);
|
||||
@ -1142,13 +1204,6 @@ class ByteCodeMachine extends StackMachine {
|
||||
sprev = EncodingHelper.prevCharHead(str, s);
|
||||
}
|
||||
|
||||
private void opLookBehindSb() {
|
||||
final int tlen = code[ip++];
|
||||
s -= tlen;
|
||||
if (s < str) {opFail(); return;}
|
||||
sprev = s == str ? -1 : s - 1;
|
||||
}
|
||||
|
||||
private void opPushLookBehindNot() {
|
||||
final int addr = code[ip++];
|
||||
final int tlen = code[ip++];
|
||||
|
@ -236,16 +236,17 @@ class ByteCodePrinter {
|
||||
sb.append(new String(code, s, len));
|
||||
}
|
||||
|
||||
private void pLenStringFromTemplate(final StringBuilder sb, final int len, final char[] tm, final int idx) {
|
||||
private static void pLenStringFromTemplate(final StringBuilder sb, final int len, final char[] tm, final int idx) {
|
||||
sb.append(":T:").append(len).append(":");
|
||||
sb.append(tm, idx, len);
|
||||
}
|
||||
|
||||
public int compiledByteCodeToString(final StringBuilder sb, int bp) {
|
||||
public int compiledByteCodeToString(final StringBuilder sb, final int bptr) {
|
||||
int len, n, mem, addr, scn, cod;
|
||||
BitSet bs;
|
||||
CClassNode cc;
|
||||
int tm, idx;
|
||||
int bp = bptr;
|
||||
|
||||
sb.append("[").append(OpCodeNames[code[bp]]);
|
||||
final int argType = OpCodeArgTypes[code[bp]];
|
||||
@ -253,6 +254,7 @@ class ByteCodePrinter {
|
||||
if (argType != Arguments.SPECIAL) {
|
||||
bp++;
|
||||
switch (argType) {
|
||||
default:
|
||||
case Arguments.NON:
|
||||
break;
|
||||
|
||||
@ -410,7 +412,9 @@ class ByteCodePrinter {
|
||||
for (int i=0; i<len; i++) {
|
||||
mem = code[bp];
|
||||
bp += OPSize.MEMNUM;
|
||||
if (i > 0) sb.append(", ");
|
||||
if (i > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(mem);
|
||||
}
|
||||
break;
|
||||
@ -428,7 +432,9 @@ class ByteCodePrinter {
|
||||
for (int i=0; i<len; i++) {
|
||||
mem = code[bp];
|
||||
bp += OPSize.MEMNUM;
|
||||
if (i > 0) sb.append(", ");
|
||||
if (i > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(mem);
|
||||
}
|
||||
break;
|
||||
@ -501,7 +507,9 @@ class ByteCodePrinter {
|
||||
while (bp < end) {
|
||||
ncode++;
|
||||
|
||||
if (bp > 0) sb.append(ncode % 5 == 0 ? "\n" : " ");
|
||||
if (bp > 0) {
|
||||
sb.append(ncode % 5 == 0 ? "\n" : " ");
|
||||
}
|
||||
|
||||
bp = compiledByteCodeToString(sb, bp);
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.exception.ErrorMessages;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.exception.ValueException;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class CodeRangeBuffer implements Cloneable {
|
||||
private static final int INIT_MULTI_BYTE_RANGE_SIZE = 5;
|
||||
private static final int ALL_MULTI_BYTE_RANGE = 0x7fffffff;
|
||||
@ -68,7 +69,9 @@ public final class CodeRangeBuffer implements Cloneable {
|
||||
|
||||
for (int i=0; i<p[0]; i++) {
|
||||
buf.append("[").append(rangeNumToString(p[i * 2 + 1])).append("..").append(rangeNumToString(p[i * 2 + 2])).append("]");
|
||||
if (i > 0 && i % 6 == 0) buf.append("\n ");
|
||||
if (i > 0 && i % 6 == 0) {
|
||||
buf.append("\n ");
|
||||
}
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
@ -97,9 +100,13 @@ public final class CodeRangeBuffer implements Cloneable {
|
||||
}
|
||||
|
||||
private void moveRight(final int from, final int to, final int n) {
|
||||
if (to + n > p.length) expand(to + n);
|
||||
if (to + n > p.length) {
|
||||
expand(to + n);
|
||||
}
|
||||
System.arraycopy(p, from, p, to, n);
|
||||
if (to + n > used) used = to + n;
|
||||
if (to + n > used) {
|
||||
used = to + n;
|
||||
}
|
||||
}
|
||||
|
||||
protected void moveLeft(final int from, final int to, final int n) {
|
||||
@ -113,9 +120,13 @@ public final class CodeRangeBuffer implements Cloneable {
|
||||
|
||||
public void writeCodePoint(final int pos, final int b) {
|
||||
final int u = pos + 1;
|
||||
if (p.length < u) expand(u);
|
||||
if (p.length < u) {
|
||||
expand(u);
|
||||
}
|
||||
p[pos] = b;
|
||||
if (used < u) used = u;
|
||||
if (used < u) {
|
||||
used = u;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -125,14 +136,19 @@ public final class CodeRangeBuffer implements Cloneable {
|
||||
|
||||
// ugly part: these methods should be made OO
|
||||
// add_code_range_to_buf
|
||||
public static CodeRangeBuffer addCodeRangeToBuff(CodeRangeBuffer pbuf, int from, int to) {
|
||||
public static CodeRangeBuffer addCodeRangeToBuff(final CodeRangeBuffer pbufp, final int fromp, final int top) {
|
||||
int from = fromp, to = top;
|
||||
CodeRangeBuffer pbuf = pbufp;
|
||||
|
||||
if (from > to) {
|
||||
final int n = from;
|
||||
from = to;
|
||||
to = n;
|
||||
}
|
||||
|
||||
if (pbuf == null) pbuf = new CodeRangeBuffer(); // move to CClassNode
|
||||
if (pbuf == null) {
|
||||
pbuf = new CodeRangeBuffer(); // move to CClassNode
|
||||
}
|
||||
|
||||
final int[]p = pbuf.p;
|
||||
int n = p[0];
|
||||
@ -163,11 +179,17 @@ public final class CodeRangeBuffer implements Cloneable {
|
||||
|
||||
final int incN = low + 1 - high;
|
||||
|
||||
if (n + incN > Config.MAX_MULTI_BYTE_RANGES_NUM) throw new ValueException(ErrorMessages.ERR_TOO_MANY_MULTI_BYTE_RANGES);
|
||||
if (n + incN > Config.MAX_MULTI_BYTE_RANGES_NUM) {
|
||||
throw new ValueException(ErrorMessages.ERR_TOO_MANY_MULTI_BYTE_RANGES);
|
||||
}
|
||||
|
||||
if (incN != 1) {
|
||||
if (from > p[low * 2 + 1]) from = p[low * 2 + 1];
|
||||
if (to < p[(high - 1) * 2 + 2]) to = p[(high - 1) * 2 + 2];
|
||||
if (from > p[low * 2 + 1]) {
|
||||
from = p[low * 2 + 1];
|
||||
}
|
||||
if (to < p[(high - 1) * 2 + 2]) {
|
||||
to = p[(high - 1) * 2 + 2];
|
||||
}
|
||||
}
|
||||
|
||||
if (incN != 0 && high < n) {
|
||||
@ -197,9 +219,8 @@ public final class CodeRangeBuffer implements Cloneable {
|
||||
if (from > to) {
|
||||
if (env.syntax.allowEmptyRangeInCC()) {
|
||||
return pbuf;
|
||||
} else {
|
||||
throw new ValueException(ErrorMessages.ERR_EMPTY_RANGE_IN_CHAR_CLASS);
|
||||
}
|
||||
throw new ValueException(ErrorMessages.ERR_EMPTY_RANGE_IN_CHAR_CLASS);
|
||||
}
|
||||
return addCodeRangeToBuff(pbuf, from, to);
|
||||
}
|
||||
@ -218,12 +239,16 @@ public final class CodeRangeBuffer implements Cloneable {
|
||||
public static CodeRangeBuffer notCodeRangeBuff(final CodeRangeBuffer bbuf) {
|
||||
CodeRangeBuffer pbuf = null;
|
||||
|
||||
if (bbuf == null) return setAllMultiByteRange(pbuf);
|
||||
if (bbuf == null) {
|
||||
return setAllMultiByteRange(pbuf);
|
||||
}
|
||||
|
||||
final int[]p = bbuf.p;
|
||||
final int n = p[0];
|
||||
|
||||
if (n <= 0) return setAllMultiByteRange(pbuf);
|
||||
if (n <= 0) {
|
||||
return setAllMultiByteRange(pbuf);
|
||||
}
|
||||
|
||||
int pre = EncodingHelper.mbcodeStartPosition();
|
||||
|
||||
@ -235,18 +260,26 @@ public final class CodeRangeBuffer implements Cloneable {
|
||||
if (pre <= from - 1) {
|
||||
pbuf = addCodeRangeToBuff(pbuf, pre, from - 1);
|
||||
}
|
||||
if (to == ALL_MULTI_BYTE_RANGE) break;
|
||||
if (to == ALL_MULTI_BYTE_RANGE) {
|
||||
break;
|
||||
}
|
||||
pre = to + 1;
|
||||
}
|
||||
|
||||
if (to < ALL_MULTI_BYTE_RANGE) pbuf = addCodeRangeToBuff(pbuf, to + 1, ALL_MULTI_BYTE_RANGE);
|
||||
if (to < ALL_MULTI_BYTE_RANGE) {
|
||||
pbuf = addCodeRangeToBuff(pbuf, to + 1, ALL_MULTI_BYTE_RANGE);
|
||||
}
|
||||
return pbuf;
|
||||
}
|
||||
|
||||
// or_code_range_buf
|
||||
public static CodeRangeBuffer orCodeRangeBuff(CodeRangeBuffer bbuf1, boolean not1,
|
||||
CodeRangeBuffer bbuf2, boolean not2) {
|
||||
public static CodeRangeBuffer orCodeRangeBuff(final CodeRangeBuffer bbuf1p, final boolean not1p,
|
||||
final CodeRangeBuffer bbuf2p, final boolean not2p) {
|
||||
CodeRangeBuffer pbuf = null;
|
||||
CodeRangeBuffer bbuf1 = bbuf1p;
|
||||
CodeRangeBuffer bbuf2 = bbuf2p;
|
||||
boolean not1 = not1p;
|
||||
boolean not2 = not2p;
|
||||
|
||||
if (bbuf1 == null && bbuf2 == null) {
|
||||
if (not1 || not2) {
|
||||
@ -266,13 +299,11 @@ public final class CodeRangeBuffer implements Cloneable {
|
||||
if (bbuf1 == null) {
|
||||
if (not1) {
|
||||
return setAllMultiByteRange(pbuf);
|
||||
} else {
|
||||
if (!not2) {
|
||||
return bbuf2.clone();
|
||||
} else {
|
||||
return notCodeRangeBuff(bbuf2);
|
||||
}
|
||||
}
|
||||
if (!not2) {
|
||||
return bbuf2.clone();
|
||||
}
|
||||
return notCodeRangeBuff(bbuf2);
|
||||
}
|
||||
|
||||
if (not1) {
|
||||
@ -302,16 +333,18 @@ public final class CodeRangeBuffer implements Cloneable {
|
||||
}
|
||||
|
||||
// and_code_range1
|
||||
public static CodeRangeBuffer andCodeRange1(CodeRangeBuffer pbuf, int from1, int to1, final int[]data, final int n) {
|
||||
public static CodeRangeBuffer andCodeRange1(final CodeRangeBuffer pbufp, final int from1p, final int to1p, final int[]data, final int n) {
|
||||
CodeRangeBuffer pbuf = pbufp;
|
||||
int from1 = from1p, to1 = to1p;
|
||||
|
||||
for (int i=0; i<n; i++) {
|
||||
final int from2 = data[i * 2 + 1];
|
||||
final int to2 = data[i * 2 + 2];
|
||||
if (from2 < from1) {
|
||||
if (to2 < from1) {
|
||||
continue;
|
||||
} else {
|
||||
from1 = to2 + 1;
|
||||
}
|
||||
from1 = to2 + 1;
|
||||
} else if (from2 <= to1) {
|
||||
if (to2 < to1) {
|
||||
if (from1 <= from2 - 1) {
|
||||
@ -324,7 +357,9 @@ public final class CodeRangeBuffer implements Cloneable {
|
||||
} else {
|
||||
from1 = from2;
|
||||
}
|
||||
if (from1 > to1) break;
|
||||
if (from1 > to1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (from1 <= to1) {
|
||||
@ -335,15 +370,22 @@ public final class CodeRangeBuffer implements Cloneable {
|
||||
}
|
||||
|
||||
// and_code_range_buf
|
||||
public static CodeRangeBuffer andCodeRangeBuff(CodeRangeBuffer bbuf1, boolean not1,
|
||||
CodeRangeBuffer bbuf2, boolean not2) {
|
||||
public static CodeRangeBuffer andCodeRangeBuff(final CodeRangeBuffer bbuf1p, final boolean not1p,
|
||||
final CodeRangeBuffer bbuf2p, final boolean not2p) {
|
||||
CodeRangeBuffer pbuf = null;
|
||||
CodeRangeBuffer bbuf1 = bbuf1p;
|
||||
CodeRangeBuffer bbuf2 = bbuf2p;
|
||||
boolean not1 = not1p, not2 = not2p;
|
||||
|
||||
if (bbuf1 == null) {
|
||||
if (not1 && bbuf2 != null) return bbuf2.clone(); /* not1 != 0 -> not2 == 0 */
|
||||
if (not1 && bbuf2 != null) {
|
||||
return bbuf2.clone(); /* not1 != 0 -> not2 == 0 */
|
||||
}
|
||||
return null;
|
||||
} else if (bbuf2 == null) {
|
||||
if (not2) return bbuf1.clone();
|
||||
if (not2) {
|
||||
return bbuf1.clone();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -369,8 +411,12 @@ public final class CodeRangeBuffer implements Cloneable {
|
||||
final int from2 = p2[j * 2 + 1];
|
||||
final int to2 = p2[j * 2 + 2];
|
||||
|
||||
if (from2 > to1) break;
|
||||
if (to2 < from1) continue;
|
||||
if (from2 > to1) {
|
||||
break;
|
||||
}
|
||||
if (to2 < from1) {
|
||||
continue;
|
||||
}
|
||||
final int from = from1 > from2 ? from1 : from2;
|
||||
final int to = to1 < to2 ? to1 : to2;
|
||||
pbuf = addCodeRangeToBuff(pbuf, from, to);
|
||||
|
@ -53,13 +53,17 @@ abstract class Compiler implements ErrorMessages {
|
||||
protected abstract void compileAltNode(ConsAltNode node);
|
||||
|
||||
private void compileStringRawNode(final StringNode sn) {
|
||||
if (sn.length() <= 0) return;
|
||||
if (sn.length() <= 0) {
|
||||
return;
|
||||
}
|
||||
addCompileString(sn.chars, sn.p, sn.length(), false);
|
||||
}
|
||||
|
||||
private void compileStringNode(final StringNode node) {
|
||||
final StringNode sn = node;
|
||||
if (sn.length() <= 0) return;
|
||||
if (sn.length() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean ambig = sn.isAmbig();
|
||||
|
||||
@ -145,7 +149,9 @@ abstract class Compiler implements ErrorMessages {
|
||||
}
|
||||
|
||||
protected final void compileTreeNTimes(final Node node, final int n) {
|
||||
for (int i=0; i<n; i++) compileTree(node);
|
||||
for (int i=0; i<n; i++) {
|
||||
compileTree(node);
|
||||
}
|
||||
}
|
||||
|
||||
protected void newSyntaxException(final String message) {
|
||||
|
@ -21,6 +21,7 @@ package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public interface Config {
|
||||
final int CHAR_TABLE_SIZE = 256;
|
||||
|
||||
|
@ -23,6 +23,7 @@ import java.util.Arrays;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.encoding.CharacterType;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.encoding.IntHolder;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class EncodingHelper {
|
||||
|
||||
final static int NEW_LINE = 0x000a;
|
||||
@ -79,14 +80,19 @@ public final class EncodingHelper {
|
||||
|
||||
/* onigenc_get_right_adjust_char_head_with_prev */
|
||||
public static int rightAdjustCharHeadWithPrev(final int s, final IntHolder prev) {
|
||||
if (prev != null) prev.value = -1; /* Sorry */
|
||||
if (prev != null) {
|
||||
prev.value = -1; /* Sorry */
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// Encoding.stepBack
|
||||
public static int stepBack(final int p, int s, int n) {
|
||||
while (s != -1 && n-- > 0) {
|
||||
if (s <= p) return -1;
|
||||
public static int stepBack(final int p, final int sp, final int np) {
|
||||
int s = sp, n = np;
|
||||
while (s != -1 && n-- > 0) {
|
||||
if (s <= p) {
|
||||
return -1;
|
||||
}
|
||||
s--;
|
||||
}
|
||||
return s;
|
||||
@ -122,7 +128,7 @@ public final class EncodingHelper {
|
||||
final int upper = toUpperCase(c);
|
||||
|
||||
if (upper != c) {
|
||||
fun.apply(c, upper, arg);
|
||||
ApplyCaseFold.apply(c, upper, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -133,7 +139,7 @@ public final class EncodingHelper {
|
||||
final int upper = toUpperCase(c);
|
||||
|
||||
if (upper != c) {
|
||||
fun.apply(upper, c, arg);
|
||||
ApplyCaseFold.apply(upper, c, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isSingleline;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.isRepeatInfinite;
|
||||
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.constants.AnchorType;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.constants.MetaChar;
|
||||
@ -53,9 +52,8 @@ class Lexer extends ScannerSupport {
|
||||
if (!left()) {
|
||||
if (synAllow) {
|
||||
return 1; /* "....{" : OK! */
|
||||
} else {
|
||||
throw new SyntaxException(ERR_END_PATTERN_AT_LEFT_BRACE);
|
||||
}
|
||||
throw new SyntaxException(ERR_END_PATTERN_AT_LEFT_BRACE);
|
||||
}
|
||||
|
||||
if (!synAllow) {
|
||||
@ -83,7 +81,9 @@ class Lexer extends ScannerSupport {
|
||||
}
|
||||
}
|
||||
|
||||
if (!left()) return invalidRangeQuantifier(synAllow);
|
||||
if (!left()) {
|
||||
return invalidRangeQuantifier(synAllow);
|
||||
}
|
||||
|
||||
fetch();
|
||||
int up;
|
||||
@ -99,25 +99,35 @@ class Lexer extends ScannerSupport {
|
||||
}
|
||||
|
||||
if (p == prev) {
|
||||
if (nonLow) return invalidRangeQuantifier(synAllow);
|
||||
if (nonLow) {
|
||||
return invalidRangeQuantifier(synAllow);
|
||||
}
|
||||
up = QuantifierNode.REPEAT_INFINITE; /* {n,} : {n,infinite} */
|
||||
}
|
||||
} else {
|
||||
if (nonLow) return invalidRangeQuantifier(synAllow);
|
||||
if (nonLow) {
|
||||
return invalidRangeQuantifier(synAllow);
|
||||
}
|
||||
unfetch();
|
||||
up = low; /* {n} : exact n times */
|
||||
ret = 2; /* fixed */
|
||||
}
|
||||
|
||||
if (!left()) return invalidRangeQuantifier(synAllow);
|
||||
if (!left()) {
|
||||
return invalidRangeQuantifier(synAllow);
|
||||
}
|
||||
fetch();
|
||||
|
||||
if (syntax.opEscBraceInterval()) {
|
||||
if (c != syntax.metaCharTable.esc) return invalidRangeQuantifier(synAllow);
|
||||
if (c != syntax.metaCharTable.esc) {
|
||||
return invalidRangeQuantifier(synAllow);
|
||||
}
|
||||
fetch();
|
||||
}
|
||||
|
||||
if (c != '}') return invalidRangeQuantifier(synAllow);
|
||||
if (c != '}') {
|
||||
return invalidRangeQuantifier(synAllow);
|
||||
}
|
||||
|
||||
if (!isRepeatInfinite(up) && low > up) {
|
||||
throw new ValueException(ERR_UPPER_SMALLER_THAN_LOWER_IN_REPEAT_RANGE);
|
||||
@ -134,9 +144,8 @@ class Lexer extends ScannerSupport {
|
||||
if (synAllow) {
|
||||
restore();
|
||||
return 1;
|
||||
} else {
|
||||
throw new SyntaxException(ERR_INVALID_REPEAT_RANGE_PATTERN);
|
||||
}
|
||||
throw new SyntaxException(ERR_INVALID_REPEAT_RANGE_PATTERN);
|
||||
}
|
||||
|
||||
@SuppressWarnings("fallthrough")
|
||||
@ -218,17 +227,6 @@ class Lexer extends ScannerSupport {
|
||||
}
|
||||
}
|
||||
|
||||
private int nameEndCodePoint(final int start) {
|
||||
switch(start) {
|
||||
case '<':
|
||||
return '>';
|
||||
case '\'':
|
||||
return '\'';
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchTokenInCCFor_charType(final boolean flag, final int type) {
|
||||
token.type = TokenType.CHAR_TYPE;
|
||||
token.setPropCType(type);
|
||||
@ -236,7 +234,9 @@ class Lexer extends ScannerSupport {
|
||||
}
|
||||
|
||||
private void fetchTokenInCCFor_x() {
|
||||
if (!left()) return;
|
||||
if (!left()) {
|
||||
return;
|
||||
}
|
||||
final int last = p;
|
||||
|
||||
if (peekIs('{') && syntax.opEscXBraceHex8()) {
|
||||
@ -274,7 +274,9 @@ class Lexer extends ScannerSupport {
|
||||
}
|
||||
|
||||
private void fetchTokenInCCFor_u() {
|
||||
if (!left()) return;
|
||||
if (!left()) {
|
||||
return;
|
||||
}
|
||||
final int last = p;
|
||||
|
||||
if (syntax.op2EscUHex4()) {
|
||||
@ -329,7 +331,9 @@ class Lexer extends ScannerSupport {
|
||||
} else if (c == '-') {
|
||||
token.type = TokenType.CC_RANGE;
|
||||
} else if (c == syntax.metaCharTable.esc) {
|
||||
if (!syntax.backSlashEscapeInCC()) return token.type;
|
||||
if (!syntax.backSlashEscapeInCC()) {
|
||||
return token.type;
|
||||
}
|
||||
if (!left()) {
|
||||
throw new SyntaxException(ERR_END_PATTERN_AT_ESCAPE);
|
||||
}
|
||||
@ -357,10 +361,14 @@ class Lexer extends ScannerSupport {
|
||||
fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
|
||||
break;
|
||||
case 'h':
|
||||
if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(false, CharacterType.XDIGIT);
|
||||
if (syntax.op2EscHXDigit()) {
|
||||
fetchTokenInCCFor_charType(false, CharacterType.XDIGIT);
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(true, CharacterType.XDIGIT);
|
||||
if (syntax.op2EscHXDigit()) {
|
||||
fetchTokenInCCFor_charType(true, CharacterType.XDIGIT);
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
fetchTokenInCCFor_x();
|
||||
@ -424,7 +432,9 @@ class Lexer extends ScannerSupport {
|
||||
}
|
||||
|
||||
private void fetchTokenFor_xBrace() {
|
||||
if (!left()) return;
|
||||
if (!left()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int last = p;
|
||||
if (peekIs('{') && syntax.opEscXBraceHex8()) {
|
||||
@ -461,7 +471,9 @@ class Lexer extends ScannerSupport {
|
||||
}
|
||||
|
||||
private void fetchTokenFor_uHex() {
|
||||
if (!left()) return;
|
||||
if (!left()) {
|
||||
return;
|
||||
}
|
||||
final int last = p;
|
||||
|
||||
if (syntax.op2EscUHex4()) {
|
||||
@ -562,79 +574,129 @@ class Lexer extends ScannerSupport {
|
||||
switch(c) {
|
||||
|
||||
case '*':
|
||||
if (syntax.opEscAsteriskZeroInf()) fetchTokenFor_repeat(0, QuantifierNode.REPEAT_INFINITE);
|
||||
if (syntax.opEscAsteriskZeroInf()) {
|
||||
fetchTokenFor_repeat(0, QuantifierNode.REPEAT_INFINITE);
|
||||
}
|
||||
break;
|
||||
case '+':
|
||||
if (syntax.opEscPlusOneInf()) fetchTokenFor_repeat(1, QuantifierNode.REPEAT_INFINITE);
|
||||
if (syntax.opEscPlusOneInf()) {
|
||||
fetchTokenFor_repeat(1, QuantifierNode.REPEAT_INFINITE);
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
if (syntax.opEscQMarkZeroOne()) fetchTokenFor_repeat(0, 1);
|
||||
if (syntax.opEscQMarkZeroOne()) {
|
||||
fetchTokenFor_repeat(0, 1);
|
||||
}
|
||||
break;
|
||||
case '{':
|
||||
if (syntax.opEscBraceInterval()) fetchTokenFor_openBrace();
|
||||
if (syntax.opEscBraceInterval()) {
|
||||
fetchTokenFor_openBrace();
|
||||
}
|
||||
break;
|
||||
case '|':
|
||||
if (syntax.opEscVBarAlt()) token.type = TokenType.ALT;
|
||||
if (syntax.opEscVBarAlt()) {
|
||||
token.type = TokenType.ALT;
|
||||
}
|
||||
break;
|
||||
case '(':
|
||||
if (syntax.opEscLParenSubexp()) token.type = TokenType.SUBEXP_OPEN;
|
||||
if (syntax.opEscLParenSubexp()) {
|
||||
token.type = TokenType.SUBEXP_OPEN;
|
||||
}
|
||||
break;
|
||||
case ')':
|
||||
if (syntax.opEscLParenSubexp()) token.type = TokenType.SUBEXP_CLOSE;
|
||||
if (syntax.opEscLParenSubexp()) {
|
||||
token.type = TokenType.SUBEXP_CLOSE;
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
if (syntax.opEscWWord()) fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
|
||||
if (syntax.opEscWWord()) {
|
||||
fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
|
||||
}
|
||||
break;
|
||||
case 'W':
|
||||
if (syntax.opEscWWord()) fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
|
||||
if (syntax.opEscWWord()) {
|
||||
fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
if (syntax.opEscBWordBound()) fetchTokenFor_anchor(AnchorType.WORD_BOUND);
|
||||
if (syntax.opEscBWordBound()) {
|
||||
fetchTokenFor_anchor(AnchorType.WORD_BOUND);
|
||||
}
|
||||
break;
|
||||
case 'B':
|
||||
if (syntax.opEscBWordBound()) fetchTokenFor_anchor(AnchorType.NOT_WORD_BOUND);
|
||||
if (syntax.opEscBWordBound()) {
|
||||
fetchTokenFor_anchor(AnchorType.NOT_WORD_BOUND);
|
||||
}
|
||||
break;
|
||||
case '<':
|
||||
if (Config.USE_WORD_BEGIN_END && syntax.opEscLtGtWordBeginEnd()) fetchTokenFor_anchor(AnchorType.WORD_BEGIN);
|
||||
if (Config.USE_WORD_BEGIN_END && syntax.opEscLtGtWordBeginEnd()) {
|
||||
fetchTokenFor_anchor(AnchorType.WORD_BEGIN);
|
||||
}
|
||||
break;
|
||||
case '>':
|
||||
if (Config.USE_WORD_BEGIN_END && syntax.opEscLtGtWordBeginEnd()) fetchTokenFor_anchor(AnchorType.WORD_END);
|
||||
if (Config.USE_WORD_BEGIN_END && syntax.opEscLtGtWordBeginEnd()) {
|
||||
fetchTokenFor_anchor(AnchorType.WORD_END);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (syntax.opEscSWhiteSpace()) fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
|
||||
if (syntax.opEscSWhiteSpace()) {
|
||||
fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
if (syntax.opEscSWhiteSpace()) fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
|
||||
if (syntax.opEscSWhiteSpace()) {
|
||||
fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
if (syntax.opEscDDigit()) fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
|
||||
if (syntax.opEscDDigit()) {
|
||||
fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
|
||||
}
|
||||
break;
|
||||
case 'D':
|
||||
if (syntax.opEscDDigit()) fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
|
||||
if (syntax.opEscDDigit()) {
|
||||
fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(false, CharacterType.XDIGIT);
|
||||
if (syntax.op2EscHXDigit()) {
|
||||
fetchTokenInCCFor_charType(false, CharacterType.XDIGIT);
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(true, CharacterType.XDIGIT);
|
||||
if (syntax.op2EscHXDigit()) {
|
||||
fetchTokenInCCFor_charType(true, CharacterType.XDIGIT);
|
||||
}
|
||||
break;
|
||||
case 'A':
|
||||
if (syntax.opEscAZBufAnchor()) fetchTokenFor_anchor(AnchorType.BEGIN_BUF);
|
||||
if (syntax.opEscAZBufAnchor()) {
|
||||
fetchTokenFor_anchor(AnchorType.BEGIN_BUF);
|
||||
}
|
||||
break;
|
||||
case 'Z':
|
||||
if (syntax.opEscAZBufAnchor()) fetchTokenFor_anchor(AnchorType.SEMI_END_BUF);
|
||||
if (syntax.opEscAZBufAnchor()) {
|
||||
fetchTokenFor_anchor(AnchorType.SEMI_END_BUF);
|
||||
}
|
||||
break;
|
||||
case 'z':
|
||||
if (syntax.opEscAZBufAnchor()) fetchTokenFor_anchor(AnchorType.END_BUF);
|
||||
if (syntax.opEscAZBufAnchor()) {
|
||||
fetchTokenFor_anchor(AnchorType.END_BUF);
|
||||
}
|
||||
break;
|
||||
case 'G':
|
||||
if (syntax.opEscCapitalGBeginAnchor()) fetchTokenFor_anchor(AnchorType.BEGIN_POSITION);
|
||||
if (syntax.opEscCapitalGBeginAnchor()) {
|
||||
fetchTokenFor_anchor(AnchorType.BEGIN_POSITION);
|
||||
}
|
||||
break;
|
||||
case '`':
|
||||
if (syntax.op2EscGnuBufAnchor()) fetchTokenFor_anchor(AnchorType.BEGIN_BUF);
|
||||
if (syntax.op2EscGnuBufAnchor()) {
|
||||
fetchTokenFor_anchor(AnchorType.BEGIN_BUF);
|
||||
}
|
||||
break;
|
||||
case '\'':
|
||||
if (syntax.op2EscGnuBufAnchor()) fetchTokenFor_anchor(AnchorType.END_BUF);
|
||||
if (syntax.op2EscGnuBufAnchor()) {
|
||||
fetchTokenFor_anchor(AnchorType.END_BUF);
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
fetchTokenFor_xBrace();
|
||||
@ -684,22 +746,34 @@ class Lexer extends ScannerSupport {
|
||||
{
|
||||
switch(c) {
|
||||
case '.':
|
||||
if (syntax.opDotAnyChar()) token.type = TokenType.ANYCHAR;
|
||||
if (syntax.opDotAnyChar()) {
|
||||
token.type = TokenType.ANYCHAR;
|
||||
}
|
||||
break;
|
||||
case '*':
|
||||
if (syntax.opAsteriskZeroInf()) fetchTokenFor_repeat(0, QuantifierNode.REPEAT_INFINITE);
|
||||
if (syntax.opAsteriskZeroInf()) {
|
||||
fetchTokenFor_repeat(0, QuantifierNode.REPEAT_INFINITE);
|
||||
}
|
||||
break;
|
||||
case '+':
|
||||
if (syntax.opPlusOneInf()) fetchTokenFor_repeat(1, QuantifierNode.REPEAT_INFINITE);
|
||||
if (syntax.opPlusOneInf()) {
|
||||
fetchTokenFor_repeat(1, QuantifierNode.REPEAT_INFINITE);
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
if (syntax.opQMarkZeroOne()) fetchTokenFor_repeat(0, 1);
|
||||
if (syntax.opQMarkZeroOne()) {
|
||||
fetchTokenFor_repeat(0, 1);
|
||||
}
|
||||
break;
|
||||
case '{':
|
||||
if (syntax.opBraceInterval()) fetchTokenFor_openBrace();
|
||||
if (syntax.opBraceInterval()) {
|
||||
fetchTokenFor_openBrace();
|
||||
}
|
||||
break;
|
||||
case '|':
|
||||
if (syntax.opVBarAlt()) token.type = TokenType.ALT;
|
||||
if (syntax.opVBarAlt()) {
|
||||
token.type = TokenType.ALT;
|
||||
}
|
||||
break;
|
||||
|
||||
case '(':
|
||||
@ -713,9 +787,13 @@ class Lexer extends ScannerSupport {
|
||||
}
|
||||
fetch();
|
||||
if (c == syntax.metaCharTable.esc) {
|
||||
if (left()) fetch();
|
||||
if (left()) {
|
||||
fetch();
|
||||
}
|
||||
} else {
|
||||
if (c == ')') break;
|
||||
if (c == ')') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue start; // goto start
|
||||
@ -723,19 +801,29 @@ class Lexer extends ScannerSupport {
|
||||
unfetch();
|
||||
}
|
||||
|
||||
if (syntax.opLParenSubexp()) token.type = TokenType.SUBEXP_OPEN;
|
||||
if (syntax.opLParenSubexp()) {
|
||||
token.type = TokenType.SUBEXP_OPEN;
|
||||
}
|
||||
break;
|
||||
case ')':
|
||||
if (syntax.opLParenSubexp()) token.type = TokenType.SUBEXP_CLOSE;
|
||||
if (syntax.opLParenSubexp()) {
|
||||
token.type = TokenType.SUBEXP_CLOSE;
|
||||
}
|
||||
break;
|
||||
case '^':
|
||||
if (syntax.opLineAnchor()) fetchTokenFor_anchor(isSingleline(env.option) ? AnchorType.BEGIN_BUF : AnchorType.BEGIN_LINE);
|
||||
if (syntax.opLineAnchor()) {
|
||||
fetchTokenFor_anchor(isSingleline(env.option) ? AnchorType.BEGIN_BUF : AnchorType.BEGIN_LINE);
|
||||
}
|
||||
break;
|
||||
case '$':
|
||||
if (syntax.opLineAnchor()) fetchTokenFor_anchor(isSingleline(env.option) ? AnchorType.END_BUF : AnchorType.END_LINE);
|
||||
if (syntax.opLineAnchor()) {
|
||||
fetchTokenFor_anchor(isSingleline(env.option) ? AnchorType.END_BUF : AnchorType.END_LINE);
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
if (syntax.opBracketCC()) token.type = TokenType.CC_CC_OPEN;
|
||||
if (syntax.opBracketCC()) {
|
||||
token.type = TokenType.CC_CC_OPEN;
|
||||
}
|
||||
break;
|
||||
case ']':
|
||||
//if (*src > env->pattern) /* /].../ is allowed. */
|
||||
@ -745,7 +833,9 @@ class Lexer extends ScannerSupport {
|
||||
if (Option.isExtend(env.option)) {
|
||||
while (left()) {
|
||||
fetch();
|
||||
if (EncodingHelper.isNewLine(c)) break;
|
||||
if (EncodingHelper.isNewLine(c)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue start; // goto start
|
||||
}
|
||||
@ -756,7 +846,10 @@ class Lexer extends ScannerSupport {
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\f':
|
||||
if (Option.isExtend(env.option)) continue start; // goto start
|
||||
if (Option.isExtend(env.option))
|
||||
{
|
||||
continue start; // goto start
|
||||
}
|
||||
break;
|
||||
|
||||
default: // string
|
||||
@ -798,8 +891,8 @@ class Lexer extends ScannerSupport {
|
||||
}
|
||||
}
|
||||
|
||||
protected final void syntaxWarn(final String message, final char c) {
|
||||
syntaxWarn(message.replace("<%n>", Character.toString(c)));
|
||||
protected final void syntaxWarn(final String message, final char ch) {
|
||||
syntaxWarn(message.replace("<%n>", Character.toString(ch)));
|
||||
}
|
||||
|
||||
protected final void syntaxWarn(final String message) {
|
||||
|
@ -21,10 +21,10 @@
|
||||
package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isFindLongest;
|
||||
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.constants.AnchorType;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.encoding.IntHolder;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public abstract class Matcher extends IntHolder {
|
||||
protected final Regex regex;
|
||||
|
||||
@ -73,7 +73,9 @@ public abstract class Matcher extends IntHolder {
|
||||
protected final void msaInit(final int option, final int start) {
|
||||
msaOptions = option;
|
||||
msaStart = start;
|
||||
if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) msaBestLen = -1;
|
||||
if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) {
|
||||
msaBestLen = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public final int match(final int at, final int range, final int option) {
|
||||
@ -83,20 +85,19 @@ public abstract class Matcher extends IntHolder {
|
||||
|
||||
if (Config.USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE) {
|
||||
return matchAt(end /*range*/, at, prev);
|
||||
} else {
|
||||
return matchAt(range /*range*/, at, prev);
|
||||
}
|
||||
return matchAt(range /*range*/, at, prev);
|
||||
}
|
||||
|
||||
int low, high; // these are the return values
|
||||
private boolean forwardSearchRange(final char[] chars, final int str, final int end, final int s, final int range, final IntHolder lowPrev) {
|
||||
private boolean forwardSearchRange(final char[] ch, final int string, final int e, final int s, final int range, final IntHolder lowPrev) {
|
||||
int pprev = -1;
|
||||
int p = s;
|
||||
|
||||
if (Config.DEBUG_SEARCH) {
|
||||
Config.log.println("forward_search_range: "+
|
||||
"str: " + str +
|
||||
", end: " + end +
|
||||
"str: " + string +
|
||||
", end: " + e +
|
||||
", s: " + s +
|
||||
", range: " + range);
|
||||
}
|
||||
@ -106,7 +107,7 @@ public abstract class Matcher extends IntHolder {
|
||||
}
|
||||
|
||||
retry:while (true) {
|
||||
p = regex.searchAlgorithm.search(regex, chars, p, end, range);
|
||||
p = regex.searchAlgorithm.search(regex, ch, p, e, range);
|
||||
|
||||
if (p != -1 && p < range) {
|
||||
if (p - regex.dMin < s) {
|
||||
@ -119,9 +120,9 @@ public abstract class Matcher extends IntHolder {
|
||||
if (regex.subAnchor != 0) {
|
||||
switch (regex.subAnchor) {
|
||||
case AnchorType.BEGIN_LINE:
|
||||
if (p != str) {
|
||||
final int prev = EncodingHelper.prevCharHead((pprev != -1) ? pprev : str, p);
|
||||
if (!EncodingHelper.isNewLine(chars, prev, end)) {
|
||||
if (p != string) {
|
||||
final int prev = EncodingHelper.prevCharHead((pprev != -1) ? pprev : string, p);
|
||||
if (!EncodingHelper.isNewLine(ch, prev, e)) {
|
||||
// goto retry_gate;
|
||||
pprev = p;
|
||||
p++;
|
||||
@ -131,17 +132,17 @@ public abstract class Matcher extends IntHolder {
|
||||
break;
|
||||
|
||||
case AnchorType.END_LINE:
|
||||
if (p == end) {
|
||||
if (p == e) {
|
||||
if (!Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
|
||||
final int prev = EncodingHelper.prevCharHead((pprev != -1) ? pprev : str, p);
|
||||
if (prev != -1 && EncodingHelper.isNewLine(chars, prev, end)) {
|
||||
final int prev = EncodingHelper.prevCharHead((pprev != -1) ? pprev : string, p);
|
||||
if (prev != -1 && EncodingHelper.isNewLine(ch, prev, e)) {
|
||||
// goto retry_gate;
|
||||
pprev = p;
|
||||
p++;
|
||||
continue retry;
|
||||
}
|
||||
}
|
||||
} else if (!EncodingHelper.isNewLine(chars, p, end)) {
|
||||
} else if (!EncodingHelper.isNewLine(ch, p, e)) {
|
||||
//if () break;
|
||||
// goto retry_gate;
|
||||
pprev = p;
|
||||
@ -149,6 +150,9 @@ public abstract class Matcher extends IntHolder {
|
||||
continue retry;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
@ -158,7 +162,7 @@ public abstract class Matcher extends IntHolder {
|
||||
if (low > s) {
|
||||
lowPrev.value = EncodingHelper.prevCharHead(s, p);
|
||||
} else {
|
||||
lowPrev.value = EncodingHelper.prevCharHead((pprev != -1) ? pprev : str, p);
|
||||
lowPrev.value = EncodingHelper.prevCharHead((pprev != -1) ? pprev : string, p);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -172,7 +176,7 @@ public abstract class Matcher extends IntHolder {
|
||||
}
|
||||
} else {
|
||||
if (lowPrev != null) {
|
||||
lowPrev.value = EncodingHelper.prevCharHead((pprev != -1) ? pprev : str, low);
|
||||
lowPrev.value = EncodingHelper.prevCharHead((pprev != -1) ? pprev : string, low);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -182,8 +186,8 @@ public abstract class Matcher extends IntHolder {
|
||||
|
||||
if (Config.DEBUG_SEARCH) {
|
||||
Config.log.println("forward_search_range success: "+
|
||||
"low: " + (low - str) +
|
||||
", high: " + (high - str) +
|
||||
"low: " + (low - string) +
|
||||
", high: " + (high - string) +
|
||||
", dmin: " + regex.dMin +
|
||||
", dmax: " + regex.dMax);
|
||||
}
|
||||
@ -196,20 +200,21 @@ public abstract class Matcher extends IntHolder {
|
||||
}
|
||||
|
||||
// low, high
|
||||
private boolean backwardSearchRange(final char[] chars, final int str, final int end, final int s, int range, final int adjrange) {
|
||||
range += regex.dMin;
|
||||
private boolean backwardSearchRange(final char[] ch, final int string, final int e, final int s, final int range, final int adjrange) {
|
||||
int r = range;
|
||||
r += regex.dMin;
|
||||
int p = s;
|
||||
|
||||
retry:while (true) {
|
||||
p = regex.searchAlgorithm.searchBackward(regex, chars, range, adjrange, end, p, s, range);
|
||||
p = regex.searchAlgorithm.searchBackward(regex, ch, r, adjrange, e, p, s, r);
|
||||
|
||||
if (p != -1) {
|
||||
if (regex.subAnchor != 0) {
|
||||
switch (regex.subAnchor) {
|
||||
case AnchorType.BEGIN_LINE:
|
||||
if (p != str) {
|
||||
final int prev = EncodingHelper.prevCharHead(str, p);
|
||||
if (!EncodingHelper.isNewLine(chars, prev, end)) {
|
||||
if (p != string) {
|
||||
final int prev = EncodingHelper.prevCharHead(string, p);
|
||||
if (!EncodingHelper.isNewLine(ch, prev, e)) {
|
||||
p = prev;
|
||||
continue retry;
|
||||
}
|
||||
@ -217,21 +222,28 @@ public abstract class Matcher extends IntHolder {
|
||||
break;
|
||||
|
||||
case AnchorType.END_LINE:
|
||||
if (p == end) {
|
||||
if (p == e) {
|
||||
if (!Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
|
||||
final int prev = EncodingHelper.prevCharHead(adjrange, p);
|
||||
if (prev == -1) return false;
|
||||
if (EncodingHelper.isNewLine(chars, prev, end)) {
|
||||
if (prev == -1) {
|
||||
return false;
|
||||
}
|
||||
if (EncodingHelper.isNewLine(ch, prev, e)) {
|
||||
p = prev;
|
||||
continue retry;
|
||||
}
|
||||
}
|
||||
} else if (!EncodingHelper.isNewLine(chars, p, end)) {
|
||||
} else if (!EncodingHelper.isNewLine(ch, p, e)) {
|
||||
p = EncodingHelper.prevCharHead(adjrange, p);
|
||||
if (p == -1) return false;
|
||||
if (p == -1) {
|
||||
return false;
|
||||
}
|
||||
continue retry;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
@ -243,14 +255,16 @@ public abstract class Matcher extends IntHolder {
|
||||
|
||||
if (Config.DEBUG_SEARCH) {
|
||||
Config.log.println("backward_search_range: "+
|
||||
"low: " + (low - str) +
|
||||
", high: " + (high - str));
|
||||
"low: " + (low - string) +
|
||||
", high: " + (high - string));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Config.DEBUG_SEARCH) Config.log.println("backward_search_range: fail.");
|
||||
if (Config.DEBUG_SEARCH) {
|
||||
Config.log.println("backward_search_range: fail.");
|
||||
}
|
||||
return false;
|
||||
} // while
|
||||
}
|
||||
@ -261,27 +275,36 @@ public abstract class Matcher extends IntHolder {
|
||||
if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) {
|
||||
//range = upperRange;
|
||||
if (matchAt(upperRange, s, prev) != -1) {
|
||||
if (!isFindLongest(regex.options)) return true;
|
||||
if (!isFindLongest(regex.options)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//range = upperRange;
|
||||
if (matchAt(upperRange, s, prev) != -1) return true;
|
||||
if (matchAt(upperRange, s, prev) != -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) {
|
||||
if (matchAt(end, s, prev) != -1) {
|
||||
//range = upperRange;
|
||||
if (!isFindLongest(regex.options)) return true;
|
||||
if (!isFindLongest(regex.options)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//range = upperRange;
|
||||
if (matchAt(end, s, prev) != -1) return true;
|
||||
if (matchAt(end, s, prev) != -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final int search(int start, int range, final int option) {
|
||||
public final int search(final int startp, final int rangep, final int option) {
|
||||
int start = startp, range = rangep;
|
||||
int s, prev;
|
||||
int origStart = start;
|
||||
final int origRange = range;
|
||||
@ -294,7 +317,9 @@ public abstract class Matcher extends IntHolder {
|
||||
", range " + (range - str));
|
||||
}
|
||||
|
||||
if (start > end || start < str) return -1;
|
||||
if (start > end || start < str) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* anchor optimize: resume search range */
|
||||
if (regex.anchor != 0 && str < end) {
|
||||
@ -311,7 +336,10 @@ public abstract class Matcher extends IntHolder {
|
||||
} else if ((regex.anchor & AnchorType.BEGIN_BUF) != 0) {
|
||||
/* search str-position only */
|
||||
if (range > start) {
|
||||
if (start != str) return -1; // mismatch_no_msa;
|
||||
if (start != str)
|
||||
{
|
||||
return -1; // mismatch_no_msa;
|
||||
}
|
||||
range = str + 1;
|
||||
} else {
|
||||
if (range <= str) {
|
||||
@ -324,7 +352,10 @@ public abstract class Matcher extends IntHolder {
|
||||
} else if ((regex.anchor & AnchorType.END_BUF) != 0) {
|
||||
minSemiEnd = maxSemiEnd = end;
|
||||
// !end_buf:!
|
||||
if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa;
|
||||
if (endBuf(start, range, minSemiEnd, maxSemiEnd))
|
||||
{
|
||||
return -1; // mismatch_no_msa;
|
||||
}
|
||||
} else if ((regex.anchor & AnchorType.SEMI_END_BUF) != 0) {
|
||||
final int preEnd = EncodingHelper.stepBack(str, end, 1);
|
||||
maxSemiEnd = end;
|
||||
@ -332,12 +363,18 @@ public abstract class Matcher extends IntHolder {
|
||||
minSemiEnd = preEnd;
|
||||
if (minSemiEnd > str && start <= minSemiEnd) {
|
||||
// !goto end_buf;!
|
||||
if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa;
|
||||
if (endBuf(start, range, minSemiEnd, maxSemiEnd))
|
||||
{
|
||||
return -1; // mismatch_no_msa;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
minSemiEnd = end;
|
||||
// !goto end_buf;!
|
||||
if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa;
|
||||
if (endBuf(start, range, minSemiEnd, maxSemiEnd))
|
||||
{
|
||||
return -1; // mismatch_no_msa;
|
||||
}
|
||||
}
|
||||
} else if ((regex.anchor & AnchorType.ANYCHAR_STAR_ML) != 0) {
|
||||
// goto !begin_position;!
|
||||
@ -359,7 +396,9 @@ public abstract class Matcher extends IntHolder {
|
||||
prev = -1;
|
||||
msaInit(option, start);
|
||||
|
||||
if (matchCheck(end, s, prev)) return match(s);
|
||||
if (matchCheck(end, s, prev)) {
|
||||
return match(s);
|
||||
}
|
||||
return mismatch();
|
||||
}
|
||||
return -1; // goto mismatch_no_msa;
|
||||
@ -389,49 +428,62 @@ public abstract class Matcher extends IntHolder {
|
||||
schRange = end;
|
||||
} else {
|
||||
schRange += regex.dMax;
|
||||
if (schRange > end) schRange = end;
|
||||
if (schRange > end) {
|
||||
schRange = end;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((end - start) < regex.thresholdLength) return mismatch();
|
||||
if ((end - start) < regex.thresholdLength) {
|
||||
return mismatch();
|
||||
}
|
||||
|
||||
if (regex.dMax != MinMaxLen.INFINITE_DISTANCE) {
|
||||
do {
|
||||
if (!forwardSearchRange(chars, str, end, s, schRange, this)) return mismatch(); // low, high, lowPrev
|
||||
if (!forwardSearchRange(chars, str, end, s, schRange, this)) {
|
||||
return mismatch(); // low, high, lowPrev
|
||||
}
|
||||
if (s < low) {
|
||||
s = low;
|
||||
prev = value;
|
||||
}
|
||||
while (s <= high) {
|
||||
if (matchCheck(origRange, s, prev)) return match(s); // ???
|
||||
if (matchCheck(origRange, s, prev)) {
|
||||
return match(s); // ???
|
||||
}
|
||||
prev = s;
|
||||
s++;
|
||||
}
|
||||
} while (s < range);
|
||||
}
|
||||
/* check only. */
|
||||
if (!forwardSearchRange(chars, str, end, s, schRange, null)) {
|
||||
return mismatch();
|
||||
}
|
||||
|
||||
} else { /* check only. */
|
||||
if (!forwardSearchRange(chars, str, end, s, schRange, null)) return mismatch();
|
||||
|
||||
if ((regex.anchor & AnchorType.ANYCHAR_STAR) != 0) {
|
||||
do {
|
||||
if (matchCheck(origRange, s, prev)) return match(s);
|
||||
prev = s;
|
||||
s++;
|
||||
} while (s < range);
|
||||
return mismatch();
|
||||
}
|
||||
|
||||
if ((regex.anchor & AnchorType.ANYCHAR_STAR) != 0) {
|
||||
do {
|
||||
if (matchCheck(origRange, s, prev)) {
|
||||
return match(s);
|
||||
}
|
||||
prev = s;
|
||||
s++;
|
||||
} while (s < range);
|
||||
return mismatch();
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
if (matchCheck(origRange, s, prev)) return match(s);
|
||||
if (matchCheck(origRange, s, prev)) {
|
||||
return match(s);
|
||||
}
|
||||
prev = s;
|
||||
s++;
|
||||
} while (s < range);
|
||||
|
||||
if (s == range) { /* because empty match with /$/. */
|
||||
if (matchCheck(origRange, s, prev)) return match(s);
|
||||
if (matchCheck(origRange, s, prev)) {
|
||||
return match(s);
|
||||
}
|
||||
}
|
||||
} else { /* backward search */
|
||||
if (Config.USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE) {
|
||||
@ -450,37 +502,51 @@ public abstract class Matcher extends IntHolder {
|
||||
if (regex.dMax != MinMaxLen.INFINITE_DISTANCE && (end - range) >= regex.thresholdLength) {
|
||||
do {
|
||||
int schStart = s + regex.dMax;
|
||||
if (schStart > end) schStart = end;
|
||||
if (!backwardSearchRange(chars, str, end, schStart, range, adjrange)) return mismatch(); // low, high
|
||||
if (s > high) s = high;
|
||||
if (schStart > end) {
|
||||
schStart = end;
|
||||
}
|
||||
if (!backwardSearchRange(chars, str, end, schStart, range, adjrange))
|
||||
{
|
||||
return mismatch(); // low, high
|
||||
}
|
||||
if (s > high) {
|
||||
s = high;
|
||||
}
|
||||
while (s != -1 && s >= low) {
|
||||
prev = EncodingHelper.prevCharHead(str, s);
|
||||
if (matchCheck(origStart, s, prev)) return match(s);
|
||||
if (matchCheck(origStart, s, prev)) {
|
||||
return match(s);
|
||||
}
|
||||
s = prev;
|
||||
}
|
||||
} while (s >= range);
|
||||
return mismatch();
|
||||
} else { /* check only. */
|
||||
if ((end - range) < regex.thresholdLength) return mismatch();
|
||||
}
|
||||
if ((end - range) < regex.thresholdLength) {
|
||||
return mismatch();
|
||||
}
|
||||
|
||||
int schStart = s;
|
||||
if (regex.dMax != 0) {
|
||||
if (regex.dMax == MinMaxLen.INFINITE_DISTANCE) {
|
||||
int schStart = s;
|
||||
if (regex.dMax != 0) {
|
||||
if (regex.dMax == MinMaxLen.INFINITE_DISTANCE) {
|
||||
schStart = end;
|
||||
} else {
|
||||
schStart += regex.dMax;
|
||||
if (schStart > end) {
|
||||
schStart = end;
|
||||
} else {
|
||||
schStart += regex.dMax;
|
||||
if (schStart > end) {
|
||||
schStart = end;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!backwardSearchRange(chars, str, end, schStart, range, adjrange)) return mismatch();
|
||||
}
|
||||
if (!backwardSearchRange(chars, str, end, schStart, range, adjrange)) {
|
||||
return mismatch();
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
prev = EncodingHelper.prevCharHead(str, s);
|
||||
if (matchCheck(origStart, s, prev)) return match(s);
|
||||
if (matchCheck(origStart, s, prev)) {
|
||||
return match(s);
|
||||
}
|
||||
s = prev;
|
||||
} while (s >= range);
|
||||
|
||||
@ -488,8 +554,13 @@ public abstract class Matcher extends IntHolder {
|
||||
return mismatch();
|
||||
}
|
||||
|
||||
private boolean endBuf(int start, int range, final int minSemiEnd, final int maxSemiEnd) {
|
||||
if ((maxSemiEnd - str) < regex.anchorDmin) return true; // mismatch_no_msa;
|
||||
private boolean endBuf(final int startp, final int rangep, final int minSemiEnd, final int maxSemiEnd) {
|
||||
int start = startp;
|
||||
int range = rangep;
|
||||
|
||||
if ((maxSemiEnd - str) < regex.anchorDmin) {
|
||||
return true; // mismatch_no_msa;
|
||||
}
|
||||
|
||||
if (range > start) {
|
||||
if ((minSemiEnd - start) > regex.anchorDmax) {
|
||||
@ -502,7 +573,10 @@ public abstract class Matcher extends IntHolder {
|
||||
if ((maxSemiEnd - (range - 1)) < regex.anchorDmin) {
|
||||
range = maxSemiEnd - regex.anchorDmin + 1;
|
||||
}
|
||||
if (start >= range) return true; // mismatch_no_msa;
|
||||
if (start >= range)
|
||||
{
|
||||
return true; // mismatch_no_msa;
|
||||
}
|
||||
} else {
|
||||
if ((minSemiEnd - range) > regex.anchorDmax) {
|
||||
range = minSemiEnd - regex.anchorDmax;
|
||||
@ -510,7 +584,10 @@ public abstract class Matcher extends IntHolder {
|
||||
if ((maxSemiEnd - start) < regex.anchorDmin) {
|
||||
start = maxSemiEnd - regex.anchorDmin;
|
||||
}
|
||||
if (range > start) return true; // mismatch_no_msa;
|
||||
if (range > start)
|
||||
{
|
||||
return true; // mismatch_no_msa;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public abstract class MatcherFactory {
|
||||
public abstract Matcher create(Regex regex, char[] chars, int p, int end);
|
||||
|
||||
|
@ -46,24 +46,40 @@ final class MinMaxLen {
|
||||
};
|
||||
|
||||
int distanceValue() {
|
||||
if (max == INFINITE_DISTANCE) return 0;
|
||||
if (max == INFINITE_DISTANCE) {
|
||||
return 0;
|
||||
}
|
||||
final int d = max - min;
|
||||
/* return dist_vals[d] * 16 / (mm->min + 12); */
|
||||
return d < distValues.length ? distValues[d] : 1;
|
||||
}
|
||||
|
||||
int compareDistanceValue(final MinMaxLen other, int v1, int v2) {
|
||||
if (v2 <= 0) return -1;
|
||||
if (v1 <= 0) return 1;
|
||||
int compareDistanceValue(final MinMaxLen other, final int v1p, final int v2p) {
|
||||
int v1 = v1p, v2 = v2p;
|
||||
|
||||
if (v2 <= 0) {
|
||||
return -1;
|
||||
}
|
||||
if (v1 <= 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
v1 *= distanceValue();
|
||||
v2 *= other.distanceValue();
|
||||
|
||||
if (v2 > v1) return 1;
|
||||
if (v2 < v1) return -1;
|
||||
if (v2 > v1) {
|
||||
return 1;
|
||||
}
|
||||
if (v2 < v1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (other.min < min) return 1;
|
||||
if (other.min > min) return -1;
|
||||
if (other.min < min) {
|
||||
return 1;
|
||||
}
|
||||
if (other.min > min) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -96,27 +112,33 @@ final class MinMaxLen {
|
||||
}
|
||||
|
||||
void altMerge(final MinMaxLen other) {
|
||||
if (min > other.min) min = other.min;
|
||||
if (max < other.max) max = other.max;
|
||||
if (min > other.min) {
|
||||
min = other.min;
|
||||
}
|
||||
if (max < other.max) {
|
||||
max = other.max;
|
||||
}
|
||||
}
|
||||
|
||||
static final int INFINITE_DISTANCE = 0x7FFFFFFF;
|
||||
static int distanceAdd(final int d1, final int d2) {
|
||||
if (d1 == INFINITE_DISTANCE || d2 == INFINITE_DISTANCE) {
|
||||
return INFINITE_DISTANCE;
|
||||
} else {
|
||||
if (d1 <= INFINITE_DISTANCE - d2) return d1 + d2;
|
||||
else return INFINITE_DISTANCE;
|
||||
}
|
||||
if (d1 <= INFINITE_DISTANCE - d2) {
|
||||
return d1 + d2;
|
||||
}
|
||||
return INFINITE_DISTANCE;
|
||||
}
|
||||
|
||||
static int distanceMultiply(final int d, final int m) {
|
||||
if (m == 0) return 0;
|
||||
if (m == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (d < INFINITE_DISTANCE / m) {
|
||||
return d * m;
|
||||
} else {
|
||||
return INFINITE_DISTANCE;
|
||||
}
|
||||
return INFINITE_DISTANCE;
|
||||
}
|
||||
|
||||
static String distanceRangeToString(final int a, final int b) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class NodeOptInfo {
|
||||
final MinMaxLen length = new MinMaxLen();
|
||||
final OptAnchorInfo anchor = new OptAnchorInfo();
|
||||
@ -91,8 +92,12 @@ public final class NodeOptInfo {
|
||||
if (other.length.max > 0) {
|
||||
// TODO: make sure it is not an Oniguruma bug (casting unsigned int to int for arithmetic comparison)
|
||||
int otherLengthMax = other.length.max;
|
||||
if (otherLengthMax == MinMaxLen.INFINITE_DISTANCE) otherLengthMax = -1;
|
||||
if (expr.length > otherLengthMax) expr.length = otherLengthMax;
|
||||
if (otherLengthMax == MinMaxLen.INFINITE_DISTANCE) {
|
||||
otherLengthMax = -1;
|
||||
}
|
||||
if (expr.length > otherLengthMax) {
|
||||
expr.length = otherLengthMax;
|
||||
}
|
||||
if (expr.mmd.max == 0) {
|
||||
exb.select(expr);
|
||||
} else {
|
||||
|
@ -36,14 +36,20 @@ final class OptAnchorInfo implements AnchorType {
|
||||
|
||||
void concat(final OptAnchorInfo left, final OptAnchorInfo right, final int leftLength, final int rightLength) {
|
||||
leftAnchor = left.leftAnchor;
|
||||
if (leftLength == 0) leftAnchor |= right.leftAnchor;
|
||||
if (leftLength == 0) {
|
||||
leftAnchor |= right.leftAnchor;
|
||||
}
|
||||
|
||||
rightAnchor = right.rightAnchor;
|
||||
if (rightLength == 0) rightAnchor |= left.rightAnchor;
|
||||
if (rightLength == 0) {
|
||||
rightAnchor |= left.rightAnchor;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isSet(final int anchor) {
|
||||
if ((leftAnchor & anchor) != 0) return true;
|
||||
if ((leftAnchor & anchor) != 0) {
|
||||
return true;
|
||||
}
|
||||
return (rightAnchor & anchor) != 0;
|
||||
}
|
||||
|
||||
@ -77,14 +83,30 @@ final class OptAnchorInfo implements AnchorType {
|
||||
static String anchorToString(final int anchor) {
|
||||
final StringBuffer s = new StringBuffer("[");
|
||||
|
||||
if ((anchor & AnchorType.BEGIN_BUF) !=0 ) s.append("begin-buf ");
|
||||
if ((anchor & AnchorType.BEGIN_LINE) !=0 ) s.append("begin-line ");
|
||||
if ((anchor & AnchorType.BEGIN_POSITION) !=0 ) s.append("begin-pos ");
|
||||
if ((anchor & AnchorType.END_BUF) !=0 ) s.append("end-buf ");
|
||||
if ((anchor & AnchorType.SEMI_END_BUF) !=0 ) s.append("semi-end-buf ");
|
||||
if ((anchor & AnchorType.END_LINE) !=0 ) s.append("end-line ");
|
||||
if ((anchor & AnchorType.ANYCHAR_STAR) !=0 ) s.append("anychar-star ");
|
||||
if ((anchor & AnchorType.ANYCHAR_STAR_ML) !=0 ) s.append("anychar-star-pl ");
|
||||
if ((anchor & AnchorType.BEGIN_BUF) !=0 ) {
|
||||
s.append("begin-buf ");
|
||||
}
|
||||
if ((anchor & AnchorType.BEGIN_LINE) !=0 ) {
|
||||
s.append("begin-line ");
|
||||
}
|
||||
if ((anchor & AnchorType.BEGIN_POSITION) !=0 ) {
|
||||
s.append("begin-pos ");
|
||||
}
|
||||
if ((anchor & AnchorType.END_BUF) !=0 ) {
|
||||
s.append("end-buf ");
|
||||
}
|
||||
if ((anchor & AnchorType.SEMI_END_BUF) !=0 ) {
|
||||
s.append("semi-end-buf ");
|
||||
}
|
||||
if ((anchor & AnchorType.END_LINE) !=0 ) {
|
||||
s.append("end-line ");
|
||||
}
|
||||
if ((anchor & AnchorType.ANYCHAR_STAR) !=0 ) {
|
||||
s.append("anychar-star ");
|
||||
}
|
||||
if ((anchor & AnchorType.ANYCHAR_STAR_ML) !=0 ) {
|
||||
s.append("anychar-star-pl ");
|
||||
}
|
||||
s.append("]");
|
||||
|
||||
return s.toString();
|
||||
|
@ -56,7 +56,9 @@ final class OptExactInfo {
|
||||
|
||||
void concat(final OptExactInfo other) {
|
||||
if (!ignoreCase && other.ignoreCase) {
|
||||
if (length >= other.length) return; /* avoid */
|
||||
if (length >= other.length) {
|
||||
return; /* avoid */
|
||||
}
|
||||
ignoreCase = true;
|
||||
}
|
||||
|
||||
@ -65,7 +67,9 @@ final class OptExactInfo {
|
||||
|
||||
int i;
|
||||
for (i = length; p < end;) {
|
||||
if (i + 1 > OPT_EXACT_MAXLEN) break;
|
||||
if (i + 1 > OPT_EXACT_MAXLEN) {
|
||||
break;
|
||||
}
|
||||
chars[i++] = other.chars[p++];
|
||||
}
|
||||
|
||||
@ -74,15 +78,20 @@ final class OptExactInfo {
|
||||
|
||||
final OptAnchorInfo tmp = new OptAnchorInfo();
|
||||
tmp.concat(anchor, other.anchor, 1, 1);
|
||||
if (!other.reachEnd) tmp.rightAnchor = 0;
|
||||
if (!other.reachEnd) {
|
||||
tmp.rightAnchor = 0;
|
||||
}
|
||||
anchor.copy(tmp);
|
||||
}
|
||||
|
||||
// ?? raw is not used here
|
||||
void concatStr(final char[] lchars, int p, final int end, final boolean raw) {
|
||||
void concatStr(final char[] lchars, final int pp, final int end, final boolean raw) {
|
||||
int i;
|
||||
int p = pp;
|
||||
for (i = length; p < end && i < OPT_EXACT_MAXLEN;) {
|
||||
if (i + 1 > OPT_EXACT_MAXLEN) break;
|
||||
if (i + 1 > OPT_EXACT_MAXLEN) {
|
||||
break;
|
||||
}
|
||||
chars[i++] = lchars[p++];
|
||||
}
|
||||
|
||||
@ -102,17 +111,23 @@ final class OptExactInfo {
|
||||
|
||||
int i;
|
||||
for (i = 0; i < length && i < other.length; i++) {
|
||||
if (chars[i] != other.chars[i]) break;
|
||||
if (chars[i] != other.chars[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!other.reachEnd || i<other.length || i<length) reachEnd = false;
|
||||
if (!other.reachEnd || i<other.length || i<length) {
|
||||
reachEnd = false;
|
||||
}
|
||||
|
||||
length = i;
|
||||
ignoreCase |= other.ignoreCase;
|
||||
|
||||
anchor.altMerge(other.anchor);
|
||||
|
||||
if (!reachEnd) anchor.rightAnchor = 0;
|
||||
if (!reachEnd) {
|
||||
anchor.rightAnchor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -130,20 +145,32 @@ final class OptExactInfo {
|
||||
v2 = OptMapInfo.positionValue(chars[0] & 0xff);
|
||||
v1 = OptMapInfo.positionValue(alt.chars[0] & 0xff);
|
||||
|
||||
if (length > 1) v1 += 5;
|
||||
if (alt.length > 1) v2 += 5;
|
||||
if (length > 1) {
|
||||
v1 += 5;
|
||||
}
|
||||
if (alt.length > 1) {
|
||||
v2 += 5;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignoreCase) v1 *= 2;
|
||||
if (!alt.ignoreCase) v2 *= 2;
|
||||
if (!ignoreCase) {
|
||||
v1 *= 2;
|
||||
}
|
||||
if (!alt.ignoreCase) {
|
||||
v2 *= 2;
|
||||
}
|
||||
|
||||
if (mmd.compareDistanceValue(alt.mmd, v1, v2) > 0) copy(alt);
|
||||
if (mmd.compareDistanceValue(alt.mmd, v1, v2) > 0) {
|
||||
copy(alt);
|
||||
}
|
||||
}
|
||||
|
||||
// comp_opt_exact_or_map_info
|
||||
private static final int COMP_EM_BASE = 20;
|
||||
int compare(final OptMapInfo m) {
|
||||
if (m.value <= 0) return -1;
|
||||
if (m.value <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
final int ve = COMP_EM_BASE * length * (ignoreCase ? 1 : 2);
|
||||
final int vm = COMP_EM_BASE * 5 * 2 / m.value;
|
||||
|
@ -31,7 +31,9 @@ final class OptMapInfo {
|
||||
mmd.clear();
|
||||
anchor.clear();
|
||||
value = 0;
|
||||
for (int i=0; i<map.length; i++) map[i] = 0;
|
||||
for (int i=0; i<map.length; i++) {
|
||||
map[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void copy(final OptMapInfo other) {
|
||||
@ -50,11 +52,10 @@ final class OptMapInfo {
|
||||
}
|
||||
}
|
||||
|
||||
void addCharAmb(final char[] chars, final int p, final int end, int caseFoldFlag) {
|
||||
void addCharAmb(final char[] chars, final int p, final int end, final int caseFoldFlag) {
|
||||
addChar(chars[p]);
|
||||
|
||||
caseFoldFlag &= ~Config.INTERNAL_ENC_CASE_FOLD_MULTI_CHAR;
|
||||
final char[]items = EncodingHelper.caseFoldCodesByString(caseFoldFlag, chars[p]);
|
||||
final char[]items = EncodingHelper.caseFoldCodesByString(caseFoldFlag & ~Config.INTERNAL_ENC_CASE_FOLD_MULTI_CHAR, chars[p]);
|
||||
|
||||
for (int i=0; i<items.length; i++) {
|
||||
addChar(items[i]);
|
||||
@ -64,7 +65,9 @@ final class OptMapInfo {
|
||||
// select_opt_map_info
|
||||
private static final int z = 1<<15; /* 32768: something big value */
|
||||
void select(final OptMapInfo alt) {
|
||||
if (alt.value == 0) return;
|
||||
if (alt.value == 0) {
|
||||
return;
|
||||
}
|
||||
if (value == 0) {
|
||||
copy(alt);
|
||||
return;
|
||||
@ -73,13 +76,17 @@ final class OptMapInfo {
|
||||
final int v1 = z / value;
|
||||
final int v2 = z /alt.value;
|
||||
|
||||
if (mmd.compareDistanceValue(alt.mmd, v1, v2) > 0) copy(alt);
|
||||
if (mmd.compareDistanceValue(alt.mmd, v1, v2) > 0) {
|
||||
copy(alt);
|
||||
}
|
||||
}
|
||||
|
||||
// alt_merge_opt_map_info
|
||||
void altMerge(final OptMapInfo other) {
|
||||
/* if (! is_equal_mml(&to->mmd, &add->mmd)) return ; */
|
||||
if (value == 0) return;
|
||||
if (value == 0) {
|
||||
return;
|
||||
}
|
||||
if (other.value == 0 || mmd.max < other.mmd.max) {
|
||||
clear();
|
||||
return;
|
||||
@ -89,8 +96,12 @@ final class OptMapInfo {
|
||||
|
||||
int val = 0;
|
||||
for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) {
|
||||
if (other.map[i] != 0) map[i] = 1;
|
||||
if (map[i] != 0) val += positionValue(i);
|
||||
if (other.map[i] != 0) {
|
||||
map[i] = 1;
|
||||
}
|
||||
if (map[i] != 0) {
|
||||
val += positionValue(i);
|
||||
}
|
||||
}
|
||||
|
||||
value = val;
|
||||
@ -112,9 +123,8 @@ final class OptMapInfo {
|
||||
static int positionValue(final int i) {
|
||||
if (i < ByteValTable.length) {
|
||||
return ByteValTable[i];
|
||||
} else {
|
||||
return 4; /* Take it easy. */
|
||||
}
|
||||
return 4; /* Take it easy. */
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public class Option {
|
||||
|
||||
/* options */
|
||||
@ -43,19 +44,43 @@ public class Option {
|
||||
|
||||
public static String toString(final int option) {
|
||||
String options = "";
|
||||
if (isIgnoreCase(option)) options += "IGNORECASE ";
|
||||
if (isExtend(option)) options += "EXTEND ";
|
||||
if (isMultiline(option)) options += "MULTILINE ";
|
||||
if (isSingleline(option)) options += "SINGLELINE ";
|
||||
if (isFindLongest(option)) options += "FIND_LONGEST ";
|
||||
if (isFindNotEmpty(option)) options += "FIND_NOT_EMPTY ";
|
||||
if (isNegateSingleline(option)) options += "NEGATE_SINGLELINE ";
|
||||
if (isDontCaptureGroup(option)) options += "DONT_CAPTURE_GROUP ";
|
||||
if (isCaptureGroup(option)) options += "CAPTURE_GROUP ";
|
||||
if (isIgnoreCase(option)) {
|
||||
options += "IGNORECASE ";
|
||||
}
|
||||
if (isExtend(option)) {
|
||||
options += "EXTEND ";
|
||||
}
|
||||
if (isMultiline(option)) {
|
||||
options += "MULTILINE ";
|
||||
}
|
||||
if (isSingleline(option)) {
|
||||
options += "SINGLELINE ";
|
||||
}
|
||||
if (isFindLongest(option)) {
|
||||
options += "FIND_LONGEST ";
|
||||
}
|
||||
if (isFindNotEmpty(option)) {
|
||||
options += "FIND_NOT_EMPTY ";
|
||||
}
|
||||
if (isNegateSingleline(option)) {
|
||||
options += "NEGATE_SINGLELINE ";
|
||||
}
|
||||
if (isDontCaptureGroup(option)) {
|
||||
options += "DONT_CAPTURE_GROUP ";
|
||||
}
|
||||
if (isCaptureGroup(option)) {
|
||||
options += "CAPTURE_GROUP ";
|
||||
}
|
||||
|
||||
if (isNotBol(option)) options += "NOTBOL ";
|
||||
if (isNotEol(option)) options += "NOTEOL ";
|
||||
if (isPosixRegion(option)) options += "POSIX_REGION ";
|
||||
if (isNotBol(option)) {
|
||||
options += "NOTBOL ";
|
||||
}
|
||||
if (isNotEol(option)) {
|
||||
options += "NOTEOL ";
|
||||
}
|
||||
if (isPosixRegion(option)) {
|
||||
options += "POSIX_REGION ";
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsOnOff;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isDontCaptureGroup;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isIgnoreCase;
|
||||
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ast.AnchorNode;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ast.AnyCharNode;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ast.BackRefNode;
|
||||
@ -77,7 +76,9 @@ class Parser extends Lexer {
|
||||
restore();
|
||||
return true;
|
||||
}
|
||||
if (c == syntax.metaCharTable.esc) inEsc = true;
|
||||
if (c == syntax.metaCharTable.esc) {
|
||||
inEsc = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,7 +166,9 @@ class Parser extends Lexer {
|
||||
arg.vIsRaw = false;
|
||||
fetchTokenInCC();
|
||||
fetched = true;
|
||||
if (token.type == TokenType.CC_RANGE || andStart) env.ccEscWarn("-"); /* [--x] or [a&&-x] is warned. */
|
||||
if (token.type == TokenType.CC_RANGE || andStart) {
|
||||
env.ccEscWarn("-"); /* [--x] or [a&&-x] is warned. */
|
||||
}
|
||||
parseCharClassValEntry(cc, arg); // goto val_entry
|
||||
break;
|
||||
} else if (arg.state == CCSTATE.RANGE) {
|
||||
@ -214,7 +217,9 @@ class Parser extends Lexer {
|
||||
prevCC.and(cc);
|
||||
} else {
|
||||
prevCC = cc;
|
||||
if (workCC == null) workCC = new CClassNode();
|
||||
if (workCC == null) {
|
||||
workCC = new CClassNode();
|
||||
}
|
||||
cc = workCC;
|
||||
}
|
||||
cc.clear();
|
||||
@ -227,7 +232,9 @@ class Parser extends Lexer {
|
||||
throw new InternalException(ERR_PARSER_BUG);
|
||||
} // switch
|
||||
|
||||
if (!fetched) fetchTokenInCC();
|
||||
if (!fetched) {
|
||||
fetchTokenInCC();
|
||||
}
|
||||
|
||||
} // while
|
||||
|
||||
@ -443,7 +450,10 @@ class Parser extends Lexer {
|
||||
}
|
||||
|
||||
private Node parseExp(final TokenType term) {
|
||||
if (token.type == term) return StringNode.EMPTY; // goto end_of_token
|
||||
if (token.type == term)
|
||||
{
|
||||
return StringNode.EMPTY; // goto end_of_token
|
||||
}
|
||||
|
||||
Node node = null;
|
||||
boolean group = false;
|
||||
@ -474,9 +484,8 @@ class Parser extends Lexer {
|
||||
}
|
||||
if (token.escaped) {
|
||||
return parseExpTkRawByte(group); // goto tk_raw_byte
|
||||
} else {
|
||||
return parseExpTkByte(group); // goto tk_byte
|
||||
}
|
||||
return parseExpTkByte(group); // goto tk_byte
|
||||
case STRING:
|
||||
return parseExpTkByte(group); // tk_byte:
|
||||
|
||||
@ -496,7 +505,9 @@ class Parser extends Lexer {
|
||||
if (Config.NON_UNICODE_SDW) {
|
||||
final CClassNode cc = new CClassNode();
|
||||
cc.addCType(token.getPropCType(), false, env, this);
|
||||
if (token.getPropNot()) cc.setNot();
|
||||
if (token.getPropNot()) {
|
||||
cc.setNot();
|
||||
}
|
||||
node = cc;
|
||||
}
|
||||
break;
|
||||
@ -507,7 +518,9 @@ class Parser extends Lexer {
|
||||
// #ifdef USE_SHARED_CCLASS_TABLE ... #endif
|
||||
final CClassNode ccn = new CClassNode();
|
||||
ccn.addCType(token.getPropCType(), false, env, this);
|
||||
if (token.getPropNot()) ccn.setNot();
|
||||
if (token.getPropNot()) {
|
||||
ccn.setNot();
|
||||
}
|
||||
node = ccn;
|
||||
break;
|
||||
|
||||
@ -555,9 +568,8 @@ class Parser extends Lexer {
|
||||
if (syntax.contextIndepRepeatOps()) {
|
||||
if (syntax.contextInvalidRepeatOps()) {
|
||||
throw new SyntaxException(ERR_TARGET_OF_REPEAT_OPERATOR_NOT_SPECIFIED);
|
||||
} else {
|
||||
node = StringNode.EMPTY; // node_new_empty
|
||||
}
|
||||
node = StringNode.EMPTY; // node_new_empty
|
||||
} else {
|
||||
return parseExpTkByte(group); // goto tk_byte
|
||||
}
|
||||
@ -578,7 +590,9 @@ class Parser extends Lexer {
|
||||
final StringNode node = new StringNode(chars, token.backP, p); // tk_byte:
|
||||
while (true) {
|
||||
fetchToken();
|
||||
if (token.type != TokenType.STRING) break;
|
||||
if (token.type != TokenType.STRING) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (token.backP == node.end) {
|
||||
node.end = p; // non escaped character, remain shared, just increase shared range
|
||||
@ -605,7 +619,8 @@ class Parser extends Lexer {
|
||||
return parseExpRepeat(node, group);
|
||||
}
|
||||
|
||||
private Node parseExpRepeat(Node target, final boolean group) {
|
||||
private Node parseExpRepeat(final Node targetp, final boolean group) {
|
||||
Node target = targetp;
|
||||
while (token.type == TokenType.OP_REPEAT || token.type == TokenType.INTERVAL) { // repeat:
|
||||
if (target.isInvalidQuantifier()) {
|
||||
throw new SyntaxException(ERR_TARGET_OF_REPEAT_OPERATOR_INVALID);
|
||||
@ -674,24 +689,25 @@ class Parser extends Lexer {
|
||||
|
||||
if (token.type == TokenType.EOT || token.type == term || token.type == TokenType.ALT) {
|
||||
return node;
|
||||
} else {
|
||||
final ConsAltNode top = ConsAltNode.newListNode(node, null);
|
||||
ConsAltNode t = top;
|
||||
|
||||
while (token.type != TokenType.EOT && token.type != term && token.type != TokenType.ALT) {
|
||||
node = parseExp(term);
|
||||
if (node.getType() == NodeType.LIST) {
|
||||
t.setCdr((ConsAltNode)node);
|
||||
while (((ConsAltNode)node).cdr != null ) node = ((ConsAltNode)node).cdr;
|
||||
|
||||
t = ((ConsAltNode)node);
|
||||
} else {
|
||||
t.setCdr(ConsAltNode.newListNode(node, null));
|
||||
t = t.cdr;
|
||||
}
|
||||
}
|
||||
return top;
|
||||
}
|
||||
final ConsAltNode top = ConsAltNode.newListNode(node, null);
|
||||
ConsAltNode t = top;
|
||||
|
||||
while (token.type != TokenType.EOT && token.type != term && token.type != TokenType.ALT) {
|
||||
node = parseExp(term);
|
||||
if (node.getType() == NodeType.LIST) {
|
||||
t.setCdr((ConsAltNode)node);
|
||||
while (((ConsAltNode)node).cdr != null ) {
|
||||
node = ((ConsAltNode)node).cdr;
|
||||
}
|
||||
|
||||
t = ((ConsAltNode)node);
|
||||
} else {
|
||||
t.setCdr(ConsAltNode.newListNode(node, null));
|
||||
t = t.cdr;
|
||||
}
|
||||
}
|
||||
return top;
|
||||
}
|
||||
|
||||
/* term_tok: TK_EOT or TK_SUBEXP_CLOSE */
|
||||
@ -711,7 +727,9 @@ class Parser extends Lexer {
|
||||
t = t.cdr;
|
||||
}
|
||||
|
||||
if (token.type != term) parseSubExpError(term);
|
||||
if (token.type != term) {
|
||||
parseSubExpError(term);
|
||||
}
|
||||
return top;
|
||||
} else {
|
||||
parseSubExpError(term);
|
||||
@ -719,12 +737,11 @@ class Parser extends Lexer {
|
||||
}
|
||||
}
|
||||
|
||||
private void parseSubExpError(final TokenType term) {
|
||||
private static void parseSubExpError(final TokenType term) {
|
||||
if (term == TokenType.SUBEXP_CLOSE) {
|
||||
throw new SyntaxException(ERR_END_PATTERN_WITH_UNMATCHED_PARENTHESIS);
|
||||
} else {
|
||||
throw new InternalException(ERR_PARSER_BUG);
|
||||
}
|
||||
throw new InternalException(ERR_PARSER_BUG);
|
||||
}
|
||||
|
||||
private Node parseRegexp() {
|
||||
|
@ -24,6 +24,7 @@ import jdk.nashorn.internal.runtime.regexp.joni.constants.RegexState;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.exception.ErrorMessages;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.exception.ValueException;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class Regex implements RegexState {
|
||||
|
||||
int[] code; /* compiled pattern */
|
||||
@ -107,7 +108,8 @@ public final class Regex implements RegexState {
|
||||
}
|
||||
|
||||
// onig_alloc_init
|
||||
public Regex(final char[] chars, final int p, final int end, int option, final int caseFoldFlag, final Syntax syntax, final WarnCallback warnings) {
|
||||
public Regex(final char[] chars, final int p, final int end, final int optionp, final int caseFoldFlag, final Syntax syntax, final WarnCallback warnings) {
|
||||
int option = optionp;
|
||||
|
||||
if ((option & (Option.DONT_CAPTURE_GROUP | Option.CAPTURE_GROUP)) ==
|
||||
(Option.DONT_CAPTURE_GROUP | Option.CAPTURE_GROUP)) {
|
||||
@ -169,19 +171,33 @@ public final class Regex implements RegexState {
|
||||
|
||||
if (len < Config.CHAR_TABLE_SIZE) {
|
||||
// map/skip
|
||||
if (map == null) map = new byte[Config.CHAR_TABLE_SIZE];
|
||||
if (map == null) {
|
||||
map = new byte[Config.CHAR_TABLE_SIZE];
|
||||
}
|
||||
|
||||
for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) map[i] = (byte)len;
|
||||
for (int i=0; i<len-1; i++) map[chars[p + i] & 0xff] = (byte)(len - 1 -i); // oxff ??
|
||||
for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) {
|
||||
map[i] = (byte)len;
|
||||
}
|
||||
for (int i=0; i<len-1; i++)
|
||||
{
|
||||
map[chars[p + i] & 0xff] = (byte)(len - 1 -i); // oxff ??
|
||||
}
|
||||
} else {
|
||||
if (intMap == null) intMap = new int[Config.CHAR_TABLE_SIZE];
|
||||
if (intMap == null) {
|
||||
intMap = new int[Config.CHAR_TABLE_SIZE];
|
||||
}
|
||||
|
||||
for (int i=0; i<len-1; i++) intMap[chars[p + i] & 0xff] = len - 1 - i; // oxff ??
|
||||
for (int i=0; i<len-1; i++)
|
||||
{
|
||||
intMap[chars[p + i] & 0xff] = len - 1 - i; // oxff ??
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setExactInfo(final OptExactInfo e) {
|
||||
if (e.length == 0) return;
|
||||
if (e.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// shall we copy that ?
|
||||
exact = e.chars;
|
||||
@ -257,7 +273,11 @@ public final class Regex implements RegexState {
|
||||
s.append("exact: [").append(exact, exactP, exactEnd - exactP).append("]: length: ").append(exactEnd - exactP).append("\n");
|
||||
} else if (searchAlgorithm == SearchAlgorithm.MAP) {
|
||||
int n=0;
|
||||
for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) if (map[i] != 0) n++;
|
||||
for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) {
|
||||
if (map[i] != 0) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
s.append("map: n = ").append(n).append("\n");
|
||||
if (n > 0) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class Region {
|
||||
static final int REGION_NOTPOS = -1;
|
||||
|
||||
@ -36,7 +37,9 @@ public final class Region {
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Region: \n");
|
||||
for (int i=0; i<beg.length; i++) sb.append(" " + i + ": (" + beg[i] + "-" + end[i] + ")");
|
||||
for (int i=0; i<beg.length; i++) {
|
||||
sb.append(" " + i + ": (" + beg[i] + "-" + end[i] + ")");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
@ -20,11 +20,11 @@
|
||||
package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsClear;
|
||||
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ast.Node;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.exception.ErrorMessages;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class ScanEnvironment {
|
||||
|
||||
private static final int SCANENV_MEMNODES_SIZE = 8;
|
||||
@ -92,7 +92,10 @@ public final class ScanEnvironment {
|
||||
case 'b': return '\010';
|
||||
case 'e': return '\033';
|
||||
case 'v':
|
||||
if (syntax.op2EscVVtab()) return 11; // ???
|
||||
if (syntax.op2EscVVtab())
|
||||
{
|
||||
return 11; // ???
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -60,7 +60,9 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
|
||||
if (Character.isDigit(c)) {
|
||||
final int onum = num;
|
||||
num = num * 10 + EncodingHelper.digitVal(c);
|
||||
if (((onum ^ num) & INT_SIGN_BIT) != 0) return -1;
|
||||
if (((onum ^ num) & INT_SIGN_BIT) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
unfetch();
|
||||
break;
|
||||
@ -70,16 +72,19 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
|
||||
return num;
|
||||
}
|
||||
|
||||
protected final int scanUnsignedHexadecimalNumber(int maxLength) {
|
||||
protected final int scanUnsignedHexadecimalNumber(final int maxLength) {
|
||||
final int last = c;
|
||||
int num = 0;
|
||||
while(left() && maxLength-- != 0) {
|
||||
int ml = maxLength;
|
||||
while(left() && ml-- != 0) {
|
||||
fetch();
|
||||
if (EncodingHelper.isXDigit(c)) {
|
||||
final int onum = num;
|
||||
final int val = EncodingHelper.xdigitVal(c);
|
||||
num = (num << 4) + val;
|
||||
if (((onum ^ num) & INT_SIGN_BIT) != 0) return -1;
|
||||
if (((onum ^ num) & INT_SIGN_BIT) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
unfetch();
|
||||
break;
|
||||
@ -89,16 +94,19 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
|
||||
return num;
|
||||
}
|
||||
|
||||
protected final int scanUnsignedOctalNumber(int maxLength) {
|
||||
protected final int scanUnsignedOctalNumber(final int maxLength) {
|
||||
final int last = c;
|
||||
int num = 0;
|
||||
while(left() && maxLength-- != 0) {
|
||||
int ml = maxLength;
|
||||
while(left() && ml-- != 0) {
|
||||
fetch();
|
||||
if (Character.isDigit(c) && c < '8') {
|
||||
final int onum = num;
|
||||
final int val = EncodingHelper.odigitVal(c);
|
||||
num = (num << 3) + val;
|
||||
if (((onum ^ num) & INT_SIGN_BIT) != 0) return -1;
|
||||
if (((onum ^ num) & INT_SIGN_BIT) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
unfetch();
|
||||
break;
|
||||
@ -144,8 +152,8 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
|
||||
return p < stop ? chars[p] : 0;
|
||||
}
|
||||
|
||||
protected final boolean peekIs(final int c) {
|
||||
return peek() == c;
|
||||
protected final boolean peekIs(final int ch) {
|
||||
return peek() == ch;
|
||||
}
|
||||
|
||||
protected final boolean left() {
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public abstract class SearchAlgorithm {
|
||||
|
||||
public abstract String getName();
|
||||
@ -62,7 +63,9 @@ public abstract class SearchAlgorithm {
|
||||
int end = textEnd;
|
||||
end -= targetEnd - targetP - 1;
|
||||
|
||||
if (end > textRange) end = textRange;
|
||||
if (end > textRange) {
|
||||
end = textRange;
|
||||
}
|
||||
|
||||
int s = textP;
|
||||
|
||||
@ -71,11 +74,15 @@ public abstract class SearchAlgorithm {
|
||||
int p = s + 1;
|
||||
int t = targetP + 1;
|
||||
while (t < targetEnd) {
|
||||
if (target[t] != text[p++]) break;
|
||||
if (target[t] != text[p++]) {
|
||||
break;
|
||||
}
|
||||
t++;
|
||||
}
|
||||
|
||||
if (t == targetEnd) return s;
|
||||
if (t == targetEnd) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
s++;
|
||||
}
|
||||
@ -101,10 +108,14 @@ public abstract class SearchAlgorithm {
|
||||
int p = s + 1;
|
||||
int t = targetP + 1;
|
||||
while (t < targetEnd) {
|
||||
if (target[t] != text[p++]) break;
|
||||
if (target[t] != text[p++]) {
|
||||
break;
|
||||
}
|
||||
t++;
|
||||
}
|
||||
if (t == targetEnd) return s;
|
||||
if (t == targetEnd) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
// s = enc.prevCharHead or s = s <= adjustText ? -1 : s - 1;
|
||||
s--;
|
||||
@ -114,10 +125,8 @@ public abstract class SearchAlgorithm {
|
||||
};
|
||||
|
||||
public static final class SLOW_IC extends SearchAlgorithm {
|
||||
private final int caseFoldFlag;
|
||||
|
||||
public SLOW_IC(final Regex regex) {
|
||||
this.caseFoldFlag = regex.caseFoldFlag;
|
||||
//empty
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -134,11 +143,15 @@ public abstract class SearchAlgorithm {
|
||||
int end = textEnd;
|
||||
end -= targetEnd - targetP - 1;
|
||||
|
||||
if (end > textRange) end = textRange;
|
||||
if (end > textRange) {
|
||||
end = textRange;
|
||||
}
|
||||
int s = textP;
|
||||
|
||||
while (s < end) {
|
||||
if (lowerCaseMatch(target, targetP, targetEnd, text, s, textEnd)) return s;
|
||||
if (lowerCaseMatch(target, targetP, targetEnd, text, s, textEnd)) {
|
||||
return s;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
return -1;
|
||||
@ -158,17 +171,21 @@ public abstract class SearchAlgorithm {
|
||||
}
|
||||
|
||||
while (s >= textP) {
|
||||
if (lowerCaseMatch(target, targetP, targetEnd, text, s, textEnd)) return s;
|
||||
if (lowerCaseMatch(target, targetP, targetEnd, text, s, textEnd)) {
|
||||
return s;
|
||||
}
|
||||
s = EncodingHelper.prevCharHead(adjustText, s);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private boolean lowerCaseMatch(final char[] t, int tP, final int tEnd,
|
||||
final char[] chars, int p, final int end) {
|
||||
private static boolean lowerCaseMatch(final char[] t, final int tPp, final int tEnd,
|
||||
final char[] chars, final int pp, final int end) {
|
||||
|
||||
while (tP < tEnd) {
|
||||
if (t[tP++] != EncodingHelper.toLowerCase(chars[p++])) return false;
|
||||
for (int tP = tPp, p = pp; tP < tEnd; ) {
|
||||
if (t[tP++] != EncodingHelper.toLowerCase(chars[p++])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -188,7 +205,9 @@ public abstract class SearchAlgorithm {
|
||||
final int targetEnd = regex.exactEnd;
|
||||
|
||||
int end = textRange + (targetEnd - targetP) - 1;
|
||||
if (end > textEnd) end = textEnd;
|
||||
if (end > textEnd) {
|
||||
end = textEnd;
|
||||
}
|
||||
|
||||
final int tail = targetEnd - 1;
|
||||
int s = textP + (targetEnd - targetP) - 1;
|
||||
@ -199,7 +218,9 @@ public abstract class SearchAlgorithm {
|
||||
int t = tail;
|
||||
|
||||
while (text[p] == target[t]) {
|
||||
if (t == targetP) return p;
|
||||
if (t == targetP) {
|
||||
return p;
|
||||
}
|
||||
p--; t--;
|
||||
}
|
||||
|
||||
@ -211,7 +232,9 @@ public abstract class SearchAlgorithm {
|
||||
int t = tail;
|
||||
|
||||
while (text[p] == target[t]) {
|
||||
if (t == targetP) return p;
|
||||
if (t == targetP) {
|
||||
return p;
|
||||
}
|
||||
p--; t--;
|
||||
}
|
||||
|
||||
@ -249,7 +272,9 @@ public abstract class SearchAlgorithm {
|
||||
while (t < targetEnd && text[p] == target[t]) {
|
||||
p++; t++;
|
||||
}
|
||||
if (t == targetEnd) return s;
|
||||
if (t == targetEnd) {
|
||||
return s;
|
||||
}
|
||||
|
||||
s -= regex.intMapBackward[text[s] & 0xff];
|
||||
}
|
||||
@ -268,8 +293,12 @@ public abstract class SearchAlgorithm {
|
||||
|
||||
final int len = end - p;
|
||||
|
||||
for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) skip[i] = len;
|
||||
for (int i=len-1; i>0; i--) skip[chars[i] & 0xff] = i;
|
||||
for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) {
|
||||
skip[i] = len;
|
||||
}
|
||||
for (int i=len-1; i>0; i--) {
|
||||
skip[chars[i] & 0xff] = i;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -286,7 +315,9 @@ public abstract class SearchAlgorithm {
|
||||
int s = textP;
|
||||
|
||||
while (s < textRange) {
|
||||
if (text[s] > 0xff || map[text[s]] != 0) return s;
|
||||
if (text[s] > 0xff || map[text[s]] != 0) {
|
||||
return s;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
return -1;
|
||||
@ -297,9 +328,13 @@ public abstract class SearchAlgorithm {
|
||||
final byte[] map = regex.map;
|
||||
int s = textStart;
|
||||
|
||||
if (s >= textEnd) s = textEnd - 1;
|
||||
if (s >= textEnd) {
|
||||
s = textEnd - 1;
|
||||
}
|
||||
while (s >= textP) {
|
||||
if (text[s] > 0xff || map[text[s]] != 0) return s;
|
||||
if (text[s] > 0xff || map[text[s]] != 0) {
|
||||
return s;
|
||||
}
|
||||
s--;
|
||||
}
|
||||
return -1;
|
||||
|
@ -20,7 +20,6 @@
|
||||
package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsAt;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.constants.StackPopLevel;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.constants.StackType;
|
||||
@ -61,12 +60,14 @@ abstract class StackMachine extends Matcher implements StackType {
|
||||
|
||||
static final ThreadLocal<WeakReference<StackEntry[]>> stacks
|
||||
= new ThreadLocal<WeakReference<StackEntry[]>>() {
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
protected WeakReference<StackEntry[]> initialValue() {
|
||||
return new WeakReference<StackEntry[]>(allocateStack());
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static StackEntry[] fetchStack() {
|
||||
WeakReference<StackEntry[]> ref = stacks.get();
|
||||
StackEntry[] stack = ref.get();
|
||||
@ -78,7 +79,9 @@ abstract class StackMachine extends Matcher implements StackType {
|
||||
}
|
||||
|
||||
protected final void init() {
|
||||
if (stack != null) pushEnsured(ALT, regex.codeLength - 1); /* bottom stack */
|
||||
if (stack != null) {
|
||||
pushEnsured(ALT, regex.codeLength - 1); /* bottom stack */
|
||||
}
|
||||
if (repeatStk != null) {
|
||||
for (int i=1; i<=regex.numMem; i++) {
|
||||
repeatStk[i + memStartStk] = repeatStk[i + memEndStk] = INVALID_INDEX;
|
||||
@ -87,9 +90,13 @@ abstract class StackMachine extends Matcher implements StackType {
|
||||
}
|
||||
|
||||
protected final StackEntry ensure1() {
|
||||
if (stk >= stack.length) doubleStack();
|
||||
if (stk >= stack.length) {
|
||||
doubleStack();
|
||||
}
|
||||
StackEntry e = stack[stk];
|
||||
if (e == null) stack[stk] = e = new StackEntry();
|
||||
if (e == null) {
|
||||
stack[stk] = e = new StackEntry();
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -190,7 +197,9 @@ abstract class StackMachine extends Matcher implements StackType {
|
||||
if ((e.type & MASK_MEM_END_OR_MARK) != 0 && e.getMemNum() == mnum) {
|
||||
level++;
|
||||
} else if (e.type == MEM_START && e.getMemNum() == mnum) {
|
||||
if (level == 0) break;
|
||||
if (level == 0) {
|
||||
break;
|
||||
}
|
||||
level--;
|
||||
}
|
||||
}
|
||||
@ -371,9 +380,8 @@ abstract class StackMachine extends Matcher implements StackType {
|
||||
if (e.getNullCheckNum() == id) {
|
||||
if (level == 0) {
|
||||
return e.getNullCheckPStr() == s ? 1 : 0;
|
||||
} else {
|
||||
level--;
|
||||
}
|
||||
level--;
|
||||
}
|
||||
} else if (e.type == NULL_CHECK_END) {
|
||||
level++;
|
||||
@ -393,7 +401,52 @@ abstract class StackMachine extends Matcher implements StackType {
|
||||
if (e.getNullCheckPStr() != s) {
|
||||
isNull = 0;
|
||||
break;
|
||||
} else {
|
||||
}
|
||||
int endp;
|
||||
isNull = 1;
|
||||
while (k < stk) {
|
||||
if (e.type == MEM_START) {
|
||||
if (e.getMemEnd() == INVALID_INDEX) {
|
||||
isNull = 0;
|
||||
break;
|
||||
}
|
||||
if (bsAt(regex.btMemEnd, e.getMemNum())) {
|
||||
endp = stack[e.getMemEnd()].getMemPStr();
|
||||
} else {
|
||||
endp = e.getMemEnd();
|
||||
}
|
||||
if (stack[e.getMemStart()].getMemPStr() != endp) {
|
||||
isNull = 0;
|
||||
break;
|
||||
} else if (endp != s) {
|
||||
isNull = -1; /* empty, but position changed */
|
||||
}
|
||||
}
|
||||
k++;
|
||||
e = stack[k]; // !!
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isNull;
|
||||
}
|
||||
|
||||
protected final int nullCheckMemStRec(final int id, final int s) {
|
||||
int level = 0;
|
||||
int k = stk;
|
||||
int isNull;
|
||||
while (true) {
|
||||
k--;
|
||||
StackEntry e = stack[k];
|
||||
|
||||
if (e.type == NULL_CHECK_START) {
|
||||
if (e.getNullCheckNum() == id) {
|
||||
if (level == 0) {
|
||||
if (e.getNullCheckPStr() != s) {
|
||||
isNull = 0;
|
||||
break;
|
||||
}
|
||||
int endp;
|
||||
isNull = 1;
|
||||
while (k < stk) {
|
||||
@ -415,62 +468,16 @@ abstract class StackMachine extends Matcher implements StackType {
|
||||
}
|
||||
}
|
||||
k++;
|
||||
e = stack[k]; // !!
|
||||
e = stack[k];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return isNull;
|
||||
}
|
||||
|
||||
protected final int nullCheckMemStRec(final int id, final int s) {
|
||||
int level = 0;
|
||||
int k = stk;
|
||||
int isNull;
|
||||
while (true) {
|
||||
k--;
|
||||
StackEntry e = stack[k];
|
||||
|
||||
if (e.type == NULL_CHECK_START) {
|
||||
if (e.getNullCheckNum() == id) {
|
||||
if (level == 0) {
|
||||
if (e.getNullCheckPStr() != s) {
|
||||
isNull = 0;
|
||||
break;
|
||||
} else {
|
||||
int endp;
|
||||
isNull = 1;
|
||||
while (k < stk) {
|
||||
if (e.type == MEM_START) {
|
||||
if (e.getMemEnd() == INVALID_INDEX) {
|
||||
isNull = 0;
|
||||
break;
|
||||
}
|
||||
if (bsAt(regex.btMemEnd, e.getMemNum())) {
|
||||
endp = stack[e.getMemEnd()].getMemPStr();
|
||||
} else {
|
||||
endp = e.getMemEnd();
|
||||
}
|
||||
if (stack[e.getMemStart()].getMemPStr() != endp) {
|
||||
isNull = 0;
|
||||
break;
|
||||
} else if (endp != s) {
|
||||
isNull = -1; /* empty, but position changed */
|
||||
}
|
||||
}
|
||||
k++;
|
||||
e = stack[k];
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
level--;
|
||||
}
|
||||
level--;
|
||||
}
|
||||
} else if (e.type == NULL_CHECK_END) {
|
||||
if (e.getNullCheckNum() == id) level++;
|
||||
if (e.getNullCheckNum() == id) {
|
||||
level++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isNull;
|
||||
@ -485,7 +492,9 @@ abstract class StackMachine extends Matcher implements StackType {
|
||||
|
||||
if (e.type == REPEAT) {
|
||||
if (level == 0) {
|
||||
if (e.getRepeatNum() == id) return k;
|
||||
if (e.getRepeatNum() == id) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
} else if (e.type == CALL_FRAME) {
|
||||
level--;
|
||||
@ -505,9 +514,8 @@ abstract class StackMachine extends Matcher implements StackType {
|
||||
if (e.type == CALL_FRAME) {
|
||||
if (level == 0) {
|
||||
return e.getCallFrameRetAddr();
|
||||
} else {
|
||||
level--;
|
||||
}
|
||||
level--;
|
||||
} else if (e.type == RETURN) {
|
||||
level++;
|
||||
}
|
||||
|
@ -20,10 +20,10 @@
|
||||
package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.constants.MetaChar.INEFFECTIVE_META_CHAR;
|
||||
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.constants.SyntaxProperties;
|
||||
|
||||
public final class Syntax implements SyntaxProperties{
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class Syntax implements SyntaxProperties {
|
||||
private final int op;
|
||||
private final int op2;
|
||||
private final int behavior;
|
||||
|
@ -22,6 +22,7 @@ package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
/**
|
||||
* @author <a href="mailto:ola.bini@gmail.com">Ola Bini</a>
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public interface WarnCallback {
|
||||
WarnCallback DEFAULT = new WarnCallback() {
|
||||
@Override
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public interface Warnings {
|
||||
final String INVALID_BACKREFERENCE = "invalid back reference";
|
||||
final String INVALID_SUBEXP_CALL = "invalid subexp call";
|
||||
|
@ -21,6 +21,7 @@ package jdk.nashorn.internal.runtime.regexp.joni.ast;
|
||||
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.constants.AnchorType;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class AnchorNode extends Node implements AnchorType {
|
||||
public int type;
|
||||
public Node target;
|
||||
@ -65,28 +66,60 @@ public final class AnchorNode extends Node implements AnchorType {
|
||||
}
|
||||
|
||||
public String typeToString() {
|
||||
final StringBuilder type = new StringBuilder();
|
||||
if (isType(BEGIN_BUF)) type.append("BEGIN_BUF ");
|
||||
if (isType(BEGIN_LINE)) type.append("BEGIN_LINE ");
|
||||
if (isType(BEGIN_POSITION)) type.append("BEGIN_POSITION ");
|
||||
if (isType(END_BUF)) type.append("END_BUF ");
|
||||
if (isType(SEMI_END_BUF)) type.append("SEMI_END_BUF ");
|
||||
if (isType(END_LINE)) type.append("END_LINE ");
|
||||
if (isType(WORD_BOUND)) type.append("WORD_BOUND ");
|
||||
if (isType(NOT_WORD_BOUND)) type.append("NOT_WORD_BOUND ");
|
||||
if (isType(WORD_BEGIN)) type.append("WORD_BEGIN ");
|
||||
if (isType(WORD_END)) type.append("WORD_END ");
|
||||
if (isType(PREC_READ)) type.append("PREC_READ ");
|
||||
if (isType(PREC_READ_NOT)) type.append("PREC_READ_NOT ");
|
||||
if (isType(LOOK_BEHIND)) type.append("LOOK_BEHIND ");
|
||||
if (isType(LOOK_BEHIND_NOT)) type.append("LOOK_BEHIND_NOT ");
|
||||
if (isType(ANYCHAR_STAR)) type.append("ANYCHAR_STAR ");
|
||||
if (isType(ANYCHAR_STAR_ML)) type.append("ANYCHAR_STAR_ML ");
|
||||
return type.toString();
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
if (isType(BEGIN_BUF)) {
|
||||
sb.append("BEGIN_BUF ");
|
||||
}
|
||||
if (isType(BEGIN_LINE)) {
|
||||
sb.append("BEGIN_LINE ");
|
||||
}
|
||||
if (isType(BEGIN_POSITION)) {
|
||||
sb.append("BEGIN_POSITION ");
|
||||
}
|
||||
if (isType(END_BUF)) {
|
||||
sb.append("END_BUF ");
|
||||
}
|
||||
if (isType(SEMI_END_BUF)) {
|
||||
sb.append("SEMI_END_BUF ");
|
||||
}
|
||||
if (isType(END_LINE)) {
|
||||
sb.append("END_LINE ");
|
||||
}
|
||||
if (isType(WORD_BOUND)) {
|
||||
sb.append("WORD_BOUND ");
|
||||
}
|
||||
if (isType(NOT_WORD_BOUND)) {
|
||||
sb.append("NOT_WORD_BOUND ");
|
||||
}
|
||||
if (isType(WORD_BEGIN)) {
|
||||
sb.append("WORD_BEGIN ");
|
||||
}
|
||||
if (isType(WORD_END)) {
|
||||
sb.append("WORD_END ");
|
||||
}
|
||||
if (isType(PREC_READ)) {
|
||||
sb.append("PREC_READ ");
|
||||
}
|
||||
if (isType(PREC_READ_NOT)) {
|
||||
sb.append("PREC_READ_NOT ");
|
||||
}
|
||||
if (isType(LOOK_BEHIND)) {
|
||||
sb.append("LOOK_BEHIND ");
|
||||
}
|
||||
if (isType(LOOK_BEHIND_NOT)) {
|
||||
sb.append("LOOK_BEHIND_NOT ");
|
||||
}
|
||||
if (isType(ANYCHAR_STAR)) {
|
||||
sb.append("ANYCHAR_STAR ");
|
||||
}
|
||||
if (isType(ANYCHAR_STAR_ML)) {
|
||||
sb.append("ANYCHAR_STAR_ML ");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private boolean isType(final int type) {
|
||||
return (this.type & type) != 0;
|
||||
private boolean isType(final int t) {
|
||||
return (this.type & t) != 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni.ast;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class AnyCharNode extends Node {
|
||||
public AnyCharNode(){}
|
||||
|
||||
|
@ -21,6 +21,7 @@ package jdk.nashorn.internal.runtime.regexp.joni.ast;
|
||||
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ScanEnvironment;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class BackRefNode extends StateNode {
|
||||
public final int backRef;
|
||||
|
||||
|
@ -34,6 +34,7 @@ import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.exception.SyntaxException;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.exception.ValueException;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class CClassNode extends Node {
|
||||
private static final int FLAG_NCCLASS_NOT = 1<<0;
|
||||
private static final int FLAG_NCCLASS_SHARE = 1<<1;
|
||||
@ -100,7 +101,9 @@ public final class CClassNode extends Node {
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (!(other instanceof CClassNode)) return false;
|
||||
if (!(other instanceof CClassNode)) {
|
||||
return false;
|
||||
}
|
||||
final CClassNode cc = (CClassNode)other;
|
||||
return ctype == cc.ctype && isNot() == cc.isNot();
|
||||
}
|
||||
@ -110,11 +113,12 @@ public final class CClassNode extends Node {
|
||||
if (Config.USE_SHARED_CCLASS_TABLE) {
|
||||
int hash = 0;
|
||||
hash += ctype;
|
||||
if (isNot()) hash++;
|
||||
if (isNot()) {
|
||||
hash++;
|
||||
}
|
||||
return hash + (hash >> 5);
|
||||
} else {
|
||||
return super.hashCode();
|
||||
}
|
||||
return super.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -128,10 +132,14 @@ public final class CClassNode extends Node {
|
||||
}
|
||||
|
||||
public String flagsToString() {
|
||||
final StringBuilder flags = new StringBuilder();
|
||||
if (isNot()) flags.append("NOT ");
|
||||
if (isShare()) flags.append("SHARE ");
|
||||
return flags.toString();
|
||||
final StringBuilder f = new StringBuilder();
|
||||
if (isNot()) {
|
||||
f.append("NOT ");
|
||||
}
|
||||
if (isShare()) {
|
||||
f.append("SHARE ");
|
||||
}
|
||||
return f.toString();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
@ -251,7 +259,7 @@ public final class CClassNode extends Node {
|
||||
}
|
||||
|
||||
// add_ctype_to_cc_by_range // Encoding out!
|
||||
public void addCTypeByRange(final int ctype, final boolean not, final int sbOut, final int mbr[]) {
|
||||
public void addCTypeByRange(final int ct, final boolean not, final int sbOut, final int mbr[]) {
|
||||
final int n = mbr[0];
|
||||
|
||||
if (!not) {
|
||||
@ -294,10 +302,14 @@ public final class CClassNode extends Node {
|
||||
// !goto sb_end2!, remove duplication
|
||||
prev = sbOut;
|
||||
for (i=0; i<n; i++) {
|
||||
if (prev < mbr[2 * i + 1]) addCodeRangeToBuf(prev, mbr[i * 2 + 1] - 1);
|
||||
if (prev < mbr[2 * i + 1]) {
|
||||
addCodeRangeToBuf(prev, mbr[i * 2 + 1] - 1);
|
||||
}
|
||||
prev = mbr[i * 2 + 2] + 1;
|
||||
}
|
||||
if (prev < 0x7fffffff/*!!!*/) addCodeRangeToBuf(prev, 0x7fffffff);
|
||||
if (prev < 0x7fffffff/*!!!*/) {
|
||||
addCodeRangeToBuf(prev, 0x7fffffff);
|
||||
}
|
||||
return;
|
||||
}
|
||||
bs.set(j);
|
||||
@ -312,22 +324,27 @@ public final class CClassNode extends Node {
|
||||
// !sb_end2:!
|
||||
prev = sbOut;
|
||||
for (int i=0; i<n; i++) {
|
||||
if (prev < mbr[2 * i + 1]) addCodeRangeToBuf(prev, mbr[i * 2 + 1] - 1);
|
||||
if (prev < mbr[2 * i + 1]) {
|
||||
addCodeRangeToBuf(prev, mbr[i * 2 + 1] - 1);
|
||||
}
|
||||
prev = mbr[i * 2 + 2] + 1;
|
||||
}
|
||||
if (prev < 0x7fffffff/*!!!*/) addCodeRangeToBuf(prev, 0x7fffffff);
|
||||
if (prev < 0x7fffffff/*!!!*/) {
|
||||
addCodeRangeToBuf(prev, 0x7fffffff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addCType(int ctype, final boolean not, final ScanEnvironment env, final IntHolder sbOut) {
|
||||
public void addCType(final int ctp, final boolean not, final ScanEnvironment env, final IntHolder sbOut) {
|
||||
int ct = ctp;
|
||||
if (Config.NON_UNICODE_SDW) {
|
||||
switch(ctype) {
|
||||
switch (ct) {
|
||||
case CharacterType.D:
|
||||
case CharacterType.S:
|
||||
case CharacterType.W:
|
||||
ctype ^= CharacterType.SPECIAL_MASK;
|
||||
ct ^= CharacterType.SPECIAL_MASK;
|
||||
|
||||
if (env.syntax == Syntax.JAVASCRIPT && ctype == CharacterType.SPACE) {
|
||||
if (env.syntax == Syntax.JAVASCRIPT && ct == CharacterType.SPACE) {
|
||||
// \s in JavaScript includes unicode characters.
|
||||
break;
|
||||
}
|
||||
@ -335,26 +352,32 @@ public final class CClassNode extends Node {
|
||||
if (not) {
|
||||
for (int c = 0; c < BitSet.SINGLE_BYTE_SIZE; c++) {
|
||||
// if (!ASCIIEncoding.INSTANCE.isCodeCType(c, ctype)) bs.set(c);
|
||||
if ((AsciiCtypeTable[c] & (1 << ctype)) == 0) bs.set(c);
|
||||
if ((AsciiCtypeTable[c] & (1 << ct)) == 0) {
|
||||
bs.set(c);
|
||||
}
|
||||
}
|
||||
addAllMultiByteRange();
|
||||
} else {
|
||||
for (int c = 0; c < BitSet.SINGLE_BYTE_SIZE; c++) {
|
||||
// if (ASCIIEncoding.INSTANCE.isCodeCType(c, ctype)) bs.set(c);
|
||||
if ((AsciiCtypeTable[c] & (1 << ctype)) != 0) bs.set(c);
|
||||
if ((AsciiCtypeTable[c] & (1 << ct)) != 0) {
|
||||
bs.set(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final int[] ranges = EncodingHelper.ctypeCodeRange(ctype, sbOut);
|
||||
final int[] ranges = EncodingHelper.ctypeCodeRange(ct, sbOut);
|
||||
if (ranges != null) {
|
||||
addCTypeByRange(ctype, not, sbOut.value, ranges);
|
||||
addCTypeByRange(ct, not, sbOut.value, ranges);
|
||||
return;
|
||||
}
|
||||
|
||||
switch(ctype) {
|
||||
switch(ct) {
|
||||
case CharacterType.ALPHA:
|
||||
case CharacterType.BLANK:
|
||||
case CharacterType.CNTRL:
|
||||
@ -368,12 +391,16 @@ public final class CClassNode extends Node {
|
||||
case CharacterType.ALNUM:
|
||||
if (not) {
|
||||
for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
|
||||
if (!EncodingHelper.isCodeCType(c, ctype)) bs.set(c);
|
||||
if (!EncodingHelper.isCodeCType(c, ct)) {
|
||||
bs.set(c);
|
||||
}
|
||||
}
|
||||
addAllMultiByteRange();
|
||||
} else {
|
||||
for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
|
||||
if (EncodingHelper.isCodeCType(c, ctype)) bs.set(c);
|
||||
if (EncodingHelper.isCodeCType(c, ct)) {
|
||||
bs.set(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -382,11 +409,15 @@ public final class CClassNode extends Node {
|
||||
case CharacterType.PRINT:
|
||||
if (not) {
|
||||
for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
|
||||
if (!EncodingHelper.isCodeCType(c, ctype)) bs.set(c);
|
||||
if (!EncodingHelper.isCodeCType(c, ct)) {
|
||||
bs.set(c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
|
||||
if (EncodingHelper.isCodeCType(c, ctype)) bs.set(c);
|
||||
if (EncodingHelper.isCodeCType(c, ct)) {
|
||||
bs.set(c);
|
||||
}
|
||||
}
|
||||
addAllMultiByteRange();
|
||||
}
|
||||
@ -395,13 +426,17 @@ public final class CClassNode extends Node {
|
||||
case CharacterType.WORD:
|
||||
if (!not) {
|
||||
for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
|
||||
if (EncodingHelper.isWord(c)) bs.set(c);
|
||||
if (EncodingHelper.isWord(c)) {
|
||||
bs.set(c);
|
||||
}
|
||||
}
|
||||
|
||||
addAllMultiByteRange();
|
||||
} else {
|
||||
for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
|
||||
if (!EncodingHelper.isWord(c)) bs.set(c);
|
||||
if (!EncodingHelper.isWord(c)) {
|
||||
bs.set(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -422,7 +457,9 @@ public final class CClassNode extends Node {
|
||||
}
|
||||
|
||||
public void nextStateClass(final CCStateArg arg, final ScanEnvironment env) {
|
||||
if (arg.state == CCSTATE.RANGE) throw new SyntaxException(ErrorMessages.ERR_CHAR_CLASS_VALUE_AT_END_OF_RANGE);
|
||||
if (arg.state == CCSTATE.RANGE) {
|
||||
throw new SyntaxException(ErrorMessages.ERR_CHAR_CLASS_VALUE_AT_END_OF_RANGE);
|
||||
}
|
||||
|
||||
if (arg.state == CCSTATE.VALUE && arg.type != CCVALTYPE.CLASS) {
|
||||
if (arg.type == CCVALTYPE.SB) {
|
||||
@ -440,7 +477,9 @@ public final class CClassNode extends Node {
|
||||
switch(arg.state) {
|
||||
case VALUE:
|
||||
if (arg.type == CCVALTYPE.SB) {
|
||||
if (arg.vs > 0xff) throw new ValueException(ErrorMessages.ERR_INVALID_CODE_POINT_VALUE);
|
||||
if (arg.vs > 0xff) {
|
||||
throw new ValueException(ErrorMessages.ERR_INVALID_CODE_POINT_VALUE);
|
||||
}
|
||||
bs.set(arg.vs);
|
||||
} else if (arg.type == CCVALTYPE.CODE_POINT) {
|
||||
addCodeRange(env, arg.vs, arg.vs);
|
||||
@ -450,16 +489,17 @@ public final class CClassNode extends Node {
|
||||
case RANGE:
|
||||
if (arg.inType == arg.type) {
|
||||
if (arg.inType == CCVALTYPE.SB) {
|
||||
if (arg.vs > 0xff || arg.v > 0xff) throw new ValueException(ErrorMessages.ERR_INVALID_CODE_POINT_VALUE);
|
||||
if (arg.vs > 0xff || arg.v > 0xff) {
|
||||
throw new ValueException(ErrorMessages.ERR_INVALID_CODE_POINT_VALUE);
|
||||
}
|
||||
|
||||
if (arg.vs > arg.v) {
|
||||
if (env.syntax.allowEmptyRangeInCC()) {
|
||||
// goto ccs_range_end
|
||||
arg.state = CCSTATE.COMPLETE;
|
||||
break;
|
||||
} else {
|
||||
throw new ValueException(ErrorMessages.ERR_EMPTY_RANGE_IN_CHAR_CLASS);
|
||||
}
|
||||
throw new ValueException(ErrorMessages.ERR_EMPTY_RANGE_IN_CHAR_CLASS);
|
||||
}
|
||||
bs.setRange(arg.vs, arg.v);
|
||||
} else {
|
||||
@ -471,9 +511,8 @@ public final class CClassNode extends Node {
|
||||
// goto ccs_range_end
|
||||
arg.state = CCSTATE.COMPLETE;
|
||||
break;
|
||||
} else {
|
||||
throw new ValueException(ErrorMessages.ERR_EMPTY_RANGE_IN_CHAR_CLASS);
|
||||
}
|
||||
throw new ValueException(ErrorMessages.ERR_EMPTY_RANGE_IN_CHAR_CLASS);
|
||||
}
|
||||
bs.setRange(arg.vs, arg.v < 0xff ? arg.v : 0xff);
|
||||
addCodeRange(env, arg.vs, arg.v);
|
||||
@ -509,9 +548,8 @@ public final class CClassNode extends Node {
|
||||
|
||||
if (isNot()) {
|
||||
return !found;
|
||||
} else {
|
||||
return found;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
// onig_is_code_in_cc
|
||||
|
@ -24,6 +24,7 @@ import jdk.nashorn.internal.runtime.regexp.joni.WarnCallback;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.exception.ErrorMessages;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class ConsAltNode extends Node {
|
||||
public Node car;
|
||||
public ConsAltNode cdr;
|
||||
@ -31,9 +32,13 @@ public final class ConsAltNode extends Node {
|
||||
|
||||
private ConsAltNode(final Node car, final ConsAltNode cdr, final int type) {
|
||||
this.car = car;
|
||||
if (car != null) car.parent = this;
|
||||
if (car != null) {
|
||||
car.parent = this;
|
||||
}
|
||||
this.cdr = cdr;
|
||||
if (cdr != null) cdr.parent = this;
|
||||
if (cdr != null) {
|
||||
cdr.parent = this;
|
||||
}
|
||||
|
||||
this.type = type;
|
||||
}
|
||||
@ -46,8 +51,9 @@ public final class ConsAltNode extends Node {
|
||||
return new ConsAltNode(left, right, LIST);
|
||||
}
|
||||
|
||||
public static ConsAltNode listAdd(ConsAltNode list, final Node x) {
|
||||
public static ConsAltNode listAdd(final ConsAltNode listp, final Node x) {
|
||||
final ConsAltNode n = newListNode(x, null);
|
||||
ConsAltNode list = listp;
|
||||
|
||||
if (list != null) {
|
||||
while (list.cdr != null) {
|
||||
|
@ -22,6 +22,7 @@ package jdk.nashorn.internal.runtime.regexp.joni.ast;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.Option;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.constants.EncloseType;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class EncloseNode extends StateNode implements EncloseType {
|
||||
|
||||
public final int type; // enclose type
|
||||
|
@ -24,6 +24,7 @@ import jdk.nashorn.internal.runtime.regexp.joni.Config;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.WarnCallback;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.constants.NodeType;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public abstract class Node implements NodeType {
|
||||
public Node parent;
|
||||
|
||||
@ -33,8 +34,12 @@ public abstract class Node implements NodeType {
|
||||
return 1 << getType();
|
||||
}
|
||||
|
||||
protected void setChild(final Node tgt){} // default definition
|
||||
protected Node getChild(){return null;} // default definition
|
||||
protected void setChild(final Node tgt) {
|
||||
//empty, default definition
|
||||
}
|
||||
protected Node getChild() {
|
||||
return null; // default definition
|
||||
}
|
||||
|
||||
public void swap(final Node with) {
|
||||
Node tmp;
|
||||
@ -46,9 +51,13 @@ public abstract class Node implements NodeType {
|
||||
//setChild(with.getChild());
|
||||
//with.setChild(tmp);
|
||||
|
||||
if (parent != null) parent.setChild(with);
|
||||
if (parent != null) {
|
||||
parent.setChild(with);
|
||||
}
|
||||
|
||||
if (with.parent != null) with.parent.setChild(this);
|
||||
if (with.parent != null) {
|
||||
with.parent.setChild(this);
|
||||
}
|
||||
|
||||
tmp = parent;
|
||||
parent = with.parent;
|
||||
@ -81,16 +90,22 @@ public abstract class Node implements NodeType {
|
||||
}
|
||||
|
||||
protected static String pad(final Object value, final int level) {
|
||||
if (value == null) return "NULL";
|
||||
if (value == null) {
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
final StringBuilder pad = new StringBuilder(" ");
|
||||
for (int i=0; i<level; i++) pad.append(pad);
|
||||
for (int i=0; i<level; i++) {
|
||||
pad.append(pad);
|
||||
}
|
||||
|
||||
return value.toString().replace("\n", "\n" + pad);
|
||||
}
|
||||
|
||||
public final boolean isInvalidQuantifier() {
|
||||
if (!Config.VANILLA) return false;
|
||||
if (!Config.VANILLA) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ConsAltNode node;
|
||||
|
||||
@ -107,14 +122,18 @@ public abstract class Node implements NodeType {
|
||||
case LIST:
|
||||
node = (ConsAltNode)this;
|
||||
do {
|
||||
if (!node.car.isInvalidQuantifier()) return false;
|
||||
if (!node.car.isInvalidQuantifier()) {
|
||||
return false;
|
||||
}
|
||||
} while ((node = node.cdr) != null);
|
||||
return false;
|
||||
|
||||
case ALT:
|
||||
node = (ConsAltNode)this;
|
||||
do {
|
||||
if (node.car.isInvalidQuantifier()) return true;
|
||||
if (node.car.isInvalidQuantifier()) {
|
||||
return true;
|
||||
}
|
||||
} while ((node = node.cdr) != null);
|
||||
break;
|
||||
|
||||
|
@ -26,11 +26,11 @@ import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.Reduce
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.PQ_Q;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.P_QQ;
|
||||
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.QQ;
|
||||
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.Config;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.ScanEnvironment;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.constants.TargetInfo;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class QuantifierNode extends StateNode {
|
||||
|
||||
public Node target;
|
||||
@ -78,7 +78,9 @@ public final class QuantifierNode extends StateNode {
|
||||
greedy = true;
|
||||
targetEmptyInfo = TargetInfo.ISNOT_EMPTY;
|
||||
|
||||
if (byNumber) setByNumber();
|
||||
if (byNumber) {
|
||||
setByNumber();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -136,17 +138,27 @@ public final class QuantifierNode extends StateNode {
|
||||
protected int popularNum() {
|
||||
if (greedy) {
|
||||
if (lower == 0) {
|
||||
if (upper == 1) return 0;
|
||||
else if (isRepeatInfinite(upper)) return 1;
|
||||
if (upper == 1) {
|
||||
return 0;
|
||||
} else if (isRepeatInfinite(upper)) {
|
||||
return 1;
|
||||
}
|
||||
} else if (lower == 1) {
|
||||
if (isRepeatInfinite(upper)) return 2;
|
||||
if (isRepeatInfinite(upper)) {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (lower == 0) {
|
||||
if (upper == 1) return 3;
|
||||
else if (isRepeatInfinite(upper)) return 4;
|
||||
if (upper == 1) {
|
||||
return 3;
|
||||
} else if (isRepeatInfinite(upper)) {
|
||||
return 4;
|
||||
}
|
||||
} else if (lower == 1) {
|
||||
if (isRepeatInfinite(upper)) return 5;
|
||||
if (isRepeatInfinite(upper)) {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
@ -171,7 +183,9 @@ public final class QuantifierNode extends StateNode {
|
||||
final int pnum = popularNum();
|
||||
final int cnum = other.popularNum();
|
||||
|
||||
if (pnum < 0 || cnum < 0) return;
|
||||
if (pnum < 0 || cnum < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(REDUCE_TABLE[cnum][pnum]) {
|
||||
case DEL:
|
||||
@ -224,6 +238,9 @@ public final class QuantifierNode extends StateNode {
|
||||
case ASIS:
|
||||
setTarget(other);
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// ??? remove the parent from target ???
|
||||
other.target = null; // remove target from reduced quantifier
|
||||
@ -231,7 +248,9 @@ public final class QuantifierNode extends StateNode {
|
||||
|
||||
@SuppressWarnings("fallthrough")
|
||||
public int setQuantifier(final Node tgt, final boolean group, final ScanEnvironment env, final char[] chars, final int p, final int end) {
|
||||
if (lower == 1 && upper == 1) return 1;
|
||||
if (lower == 1 && upper == 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch(tgt.getType()) {
|
||||
|
||||
|
@ -21,6 +21,7 @@ package jdk.nashorn.internal.runtime.regexp.joni.ast;
|
||||
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.constants.NodeStatus;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public abstract class StateNode extends Node implements NodeStatus {
|
||||
protected int state;
|
||||
|
||||
|
@ -22,6 +22,7 @@ package jdk.nashorn.internal.runtime.regexp.joni.ast;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.EncodingHelper;
|
||||
import jdk.nashorn.internal.runtime.regexp.joni.constants.StringType;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public final class StringNode extends Node implements StringType {
|
||||
|
||||
private static final int NODE_STR_MARGIN = 16;
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni.constants;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public interface AnchorType {
|
||||
final int BEGIN_BUF = (1<<0);
|
||||
final int BEGIN_LINE = (1<<1);
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni.constants;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public interface Arguments {
|
||||
final int SPECIAL = -1;
|
||||
final int NON = 0;
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni.constants;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public interface AsmConstants {
|
||||
final int THIS = 0;
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni.constants;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public enum CCSTATE {
|
||||
VALUE,
|
||||
RANGE,
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni.constants;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public enum CCVALTYPE {
|
||||
SB,
|
||||
CODE_POINT,
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni.constants;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public interface EncloseType {
|
||||
final int MEMORY = 1<<0;
|
||||
final int OPTION = 1<<1;
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni.constants;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public interface MetaChar {
|
||||
final int ESCAPE = 0;
|
||||
final int ANYCHAR = 1;
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni.constants;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public interface NodeStatus {
|
||||
/* status bits */
|
||||
final int NST_MIN_FIXED = (1<<0);
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni.constants;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public interface NodeType {
|
||||
/* node type */
|
||||
final int STR = 0;
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni.constants;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public interface OPCode {
|
||||
final int FINISH = 0; /* matching process terminator (no more alternative) */
|
||||
final int END = 1; /* pattern code terminator (success end) */
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package jdk.nashorn.internal.runtime.regexp.joni.constants;
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public interface OPSize {
|
||||
|
||||
// this might be helpful for potential byte[] migration
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user