Merge branch 'master' into regex

This commit is contained in:
Alexey Andreev 2014-10-12 11:57:59 +04:00
commit 28251840ec
538 changed files with 54999 additions and 2368 deletions

124
README.md
View File

@ -4,73 +4,42 @@ TeaVM
What is TeaVM?
--------------
In short, TeaVM gets a bytecode, running over JVM, and translates it to the JavaScript code,
which does exactly the same thing as the original bytecode does.
It is based on its cross-compiler which transforms `class` files to JavaScript.
But there is something more:
TeaVM is an ahead-of-time translator from Java bytecode to JVM.
It can be compared with GWT, however TeaVM does not require source code of your application and
all required libraries.
You can use TeaVM for building applications for the browser, due to the following features:
* a sophisticated per-method dependency manager, which greatly reduces the JavaScript output;
* an optimizer capable of things like devirtualization, inlining, constant propagation,
loop invariant motion and many other;
* implementation of subset of core Java library;
* per-method dependency analyzer, that determines a set of methods that are really needed
to run your application, so TeaVM won't translate whole JAR files;
* fast JavaScript; for now it is almost as fast as the JavaScript, generated by GWT;
* Java class library emulation;
* integration with Maven and Eclipse;
* generation of source maps;
* debugger;
* interoperation with JavaScript libraries together with the set of predefined browser interfaces.
How to use
----------
There is no TeaVM artifacts in the central Maven repository yet.
So first you need to clone project and install it into the local repository.
In order to install project, just run `mvn install` when you are in the project's root directory.
Quick start
-----------
There are several options of using TeaVM. One is the maven build. First, you write your code as if it were an
ordinary Java project:
There are several options of using TeaVM. One is the Maven build.
The easiest way to create a new TeaVM project is to type in the command line:
```Java
package org.teavm.samples;
mvn -DarchetypeCatalog=local \
-DarchetypeGroupId=org.teavm \
-DarchetypeArtifactId=teavm-maven-webapp \
-DarchetypeVersion=0.2.0 archetype:generate
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
```
Now you can execute `mvn clean package` and get the generated `war` file.
Deploy this `war` in Tomcat or another container, or simply unzip it and open the `index.html` page.
Second, you include the following plugin in your `pom.xml` build section:
It is much easier to develop TeaVM applications using Eclipse.
If you prefer Eclipse, please read [this tutorial](https://github.com/konsoletyper/teavm/wiki/Eclipse-tutorial).
```XML
<plugin>
<groupId>org.teavm</groupId>
<artifactId>teavm-maven-plugin</artifactId>
<version>0.1</version>
<dependencies>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-classlib</artifactId>
<version>0.1</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>generate-javascript</id>
<goals>
<goal>build-javascript</goal>
</goals>
<phase>process-classes</phase>
<configuration>
<minifying>true</minifying>
<mainClass>org.teavm.samples.HelloWorld</mainClass>
<mainPageIncluded>true</mainPageIncluded>
</configuration>
</execution>
</executions>
</plugin>
```
To learn TeaVM deeper, you take a look at the [teavm-samples](teavm-samples) module,
containing examples of TeaVM-based projects.
Also you can read [project's wiki](https://github.com/konsoletyper/teavm/wiki/).
Now you can execute `mvn clean package` and get the generated JavaScript files in `target/javascript` folder.
Just open `target/javascript/main.html` page in your browser, open developer's console and press *Refresh* and
see what happen.
There is [teavm-samples](teavm-samples) module,
containing a complete buildable and runnable example.
DukeScript
----------
@ -80,9 +49,12 @@ easily talk to JavaScript environment to (usually) animate an HTML page. While D
implementation of JVM, called [Bck2Brwsr](http://wiki.apidesign.org/wiki/Bck2Brwsr), TeaVM also provides
support for running DukeScript applications, using [teavm-html4j](teavm-html4j) plugin.
Live examples
-------------
Compare the speed of JavaScript produced by TeaVM and GWT here: http://teavm.org/live-examples/jbox2d-benchmark/
Thanks to [Jaroslav Tulach](http://wiki.apidesign.org/wiki/User:JaroslavTulach), author of DukeScript, we have several
DukeScript example applications. One is the minesweeper game.
You can try its TeaVM-compiled version [here](http://xelfi.cz/minesweeper/teavm/), and then take a look at
@ -93,39 +65,3 @@ Another example is avaialble [here](http://graphhopper.com/teavm/).
It uses [GraphHopper](https://github.com/graphhopper/graphhopper/) to build route in browser.
Unlike original GraphHopper example it works completely in browser instead of querying server.
Thanks to [Peter Karich](https://github.com/karussell).
Advantages over GWT
-------------------
You may notice that TeaVM idea is much similar to GWT. So why we need TeaVM instead of GWT?
Unlinke GWT, TeaVM gets the compiled bytecode, not Java sources.
Thereby it **does not depend on a specific language syntax**. Even not on a specific language.
So, when the next Java version gets a new feature, you can use it in your source code
and TeaVM compiler remains unbroken. Also you may want thigs Scala, Kotlin or Ceilon. TeaVM supports them.
To represent a source code, GWT uses abstract syntax trees (AST).
TeaVM uses control flow graph (CFG) of methods. CFG are much easier to optimize,
so TeaVM **applies aggressive optimizations** to you code to make it running faster.
TeaVM compiler is faster. And TeaVM does not produce permutations.
So with TeaVM you have no permutation explosion problem.
Advantages over JavaScript
--------------------------
JavaScript suffers of its dynamic typing. When you write a new code, dynamic typing accelerates
the development process, allowing you to write less boilerplate code.
But when you are to maintain a large code base, you may need static typing.
Also, it is not dynamic typing that really makes code short.
Good static typed languages can [infer variable types for you](http://en.wikipedia.org/wiki/Type_inference).
And they usually have a lot more useful features like [lambda functions](http://en.wikipedia.org/wiki/Lambda_function),
[lexical closures](http://en.wikipedia.org/wiki/Closure_%28computer_science%29),
[implicit type casting](http://en.wikipedia.org/wiki/Type_conversion#Implicit_type_conversion), etc.
With JavaScript you sometimes have to include large library for only one feature. Or you include many different
libraries for different purposes and your project size grows. TeaVM translates only the methods which
are really needed. So you can depend on as much libraries as you want and get
With JavaScript you are limited to one language. TeaVM supports many of the JVM languages.

View File

@ -27,7 +27,9 @@
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<module name="StringLiteralEquality"/>
<module name="IllegalThrows"/>
<module name="IllegalThrows">
<property name="illegalClassNames" value="java.lang.Error, java.lang.RuntimeException"/>
</module>
<module name="ExplicitInitialization"/>
<module name="DefaultComesLast"/>
<module name="FallThrough"/>
@ -42,7 +44,6 @@
</module>
<module name="UpperEll"/>
<module name="ArrayTypeStyle"/>
<module name="TrailingComment"/>
<module name="RedundantModifier"/>
<module name="ClassTypeParameterName"/>
<module name="LocalFinalVariableName"/>

81
pom.xml
View File

@ -65,17 +65,23 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<html4j.version>0.7.5</html4j.version>
<sonatypeOssDistMgmtSnapshotsUrl>https://oss.sonatype.org/content/repositories/snapshots/</sonatypeOssDistMgmtSnapshotsUrl>
<html4j.version>0.9</html4j.version>
<jetty.version>9.2.1.v20140609</jetty.version>
<slf4j.version>1.7.7</slf4j.version>
</properties>
<modules>
<module>teavm-core</module>
<module>teavm-classlib</module>
<module>teavm-maven-plugin</module>
<module>teavm-maven</module>
<module>teavm-dom</module>
<module>teavm-jso</module>
<module>teavm-html4j</module>
<module>teavm-samples</module>
<module>teavm-platform</module>
<module>teavm-cli</module>
<module>teavm-chrome-rdp</module>
</modules>
<dependencyManagement>
@ -131,6 +137,31 @@
<artifactId>maven-core</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
@ -177,6 +208,40 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.17</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.11</version>
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<configuration>
<encoding>UTF-8</encoding>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
<linkXRef>false</linkXRef>
</configuration>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<propertyExpansion>config_loc=${basedir}/..</propertyExpansion>
<configLocation>../checkstyle.xml</configLocation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.5.3</version>
</plugin>
</plugins>
</pluginManagement>
</build>
@ -203,17 +268,17 @@
</plugins>
</build>
</profile>
<profile>
<id>enable-scala</id>
<modules>
<module>teavm-scala-samples</module>
</modules>
</profile>
<profile>
<id>enable-samples</id>
<modules>
<module>teavm-samples</module>
</modules>
</profile>
<profile>
<id>enable-eclipse</id>
<modules>
<module>teavm-eclipse</module>
</modules>
</profile>
</profiles>
</project>

View File

@ -2,4 +2,3 @@
/.settings
/.classpath
/.project
/.cache

108
teavm-chrome-rdp/pom.xml Normal file
View File

@ -0,0 +1,108 @@
<!--
Copyright 2014 Alexey Andreev.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.teavm</groupId>
<artifactId>teavm</artifactId>
<version>0.2-SNAPSHOT</version>
</parent>
<artifactId>teavm-chrome-rdp</artifactId>
<packaging>bundle</packaging>
<name>TeaVM debugging backend for Google Chrome RDP</name>
<description>TeaVM debugging backend for Google Chrome RDP</description>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<configLocation>../checkstyle.xml</configLocation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Export-Package>org.teavm.chromerdp</Export-Package>
<Bundle-SymbolicName>teavm-chrome-rdp</Bundle-SymbolicName>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,24 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp;
/**
*
* @author Alexey Andreev
*/
public interface ChromeRDPContainer {
void setDebugger(ChromeRDPDebuggerEndpoint debugger);
}

View File

@ -0,0 +1,538 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.teavm.chromerdp.data.*;
import org.teavm.chromerdp.messages.*;
import org.teavm.debugging.javascript.*;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeConsumer {
private static final Logger logger = LoggerFactory.getLogger(ChromeRDPDebugger.class);
private static final Object dummy = new Object();
private ChromeRDPExchange exchange;
private ConcurrentMap<JavaScriptDebuggerListener, Object> listeners = new ConcurrentHashMap<>();
private ConcurrentMap<JavaScriptLocation, RDPBreakpoint> breakpointLocationMap = new ConcurrentHashMap<>();
private ConcurrentMap<RDPBreakpoint, Object> breakpoints = new ConcurrentHashMap<>();
private volatile RDPCallFrame[] callStack = new RDPCallFrame[0];
private ConcurrentMap<String, String> scripts = new ConcurrentHashMap<>();
private ConcurrentMap<String, String> scriptIds = new ConcurrentHashMap<>();
private boolean suspended;
private ObjectMapper mapper = new ObjectMapper();
private ConcurrentMap<Integer, ResponseHandler> responseHandlers = new ConcurrentHashMap<>();
private AtomicInteger messageIdGenerator = new AtomicInteger();
private Lock breakpointLock = new ReentrantLock();
private List<JavaScriptDebuggerListener> getListeners() {
return new ArrayList<>(listeners.keySet());
}
@Override
public void setExchange(ChromeRDPExchange exchange) {
if (this.exchange == exchange) {
return;
}
if (this.exchange != null) {
this.exchange.removeListener(exchangeListener);
}
this.exchange = exchange;
if (exchange != null) {
for (RDPBreakpoint breakpoint : breakpoints.keySet().toArray(new RDPBreakpoint[0])) {
updateBreakpoint(breakpoint);
}
for (JavaScriptDebuggerListener listener : getListeners()) {
listener.attached();
}
} else {
suspended = false;
callStack = null;
for (JavaScriptDebuggerListener listener : getListeners()) {
listener.detached();
}
}
if (this.exchange != null) {
this.exchange.addListener(exchangeListener);
}
}
private ChromeRDPExchangeListener exchangeListener = new ChromeRDPExchangeListener() {
@Override public void received(String messageText) throws IOException {
receiveMessage(messageText);
}
};
private void receiveMessage(final String messageText) {
new Thread() {
@Override public void run() {
try {
JsonNode jsonMessage = mapper.readTree(messageText);
if (jsonMessage.has("id")) {
Response response = mapper.reader(Response.class).readValue(jsonMessage);
if (response.getError() != null) {
if (logger.isWarnEnabled()) {
logger.warn("Error message #{} received from browser: {}", jsonMessage.get("id"),
response.getError().toString());
}
}
responseHandlers.remove(response.getId()).received(response.getResult());
} else {
Message message = mapper.reader(Message.class).readValue(messageText);
if (message.getMethod() == null) {
return;
}
switch (message.getMethod()) {
case "Debugger.paused":
firePaused(parseJson(SuspendedNotification.class, message.getParams()));
break;
case "Debugger.resumed":
fireResumed();
break;
case "Debugger.scriptParsed":
scriptParsed(parseJson(ScriptParsedNotification.class, message.getParams()));
break;
}
}
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error("Error receiving message from Google Chrome", e);
}
}
}
}.start();
}
private synchronized void firePaused(SuspendedNotification params) {
suspended = true;
CallFrameDTO[] callFrameDTOs = params.getCallFrames();
RDPCallFrame[] callStack = new RDPCallFrame[callFrameDTOs.length];
for (int i = 0; i < callStack.length; ++i) {
callStack[i] = map(callFrameDTOs[i]);
}
this.callStack = callStack;
for (JavaScriptDebuggerListener listener : getListeners()) {
listener.paused();
}
}
private synchronized void fireResumed() {
suspended = false;
callStack = null;
for (JavaScriptDebuggerListener listener : getListeners()) {
listener.resumed();
}
}
private synchronized void scriptParsed(ScriptParsedNotification params) {
if (scripts.putIfAbsent(params.getScriptId(), params.getUrl()) != null) {
return;
}
scriptIds.put(params.getUrl(), params.getScriptId());
for (JavaScriptDebuggerListener listener : getListeners()) {
listener.scriptAdded(params.getUrl());
}
}
@Override
public void addListener(JavaScriptDebuggerListener listener) {
listeners.put(listener, dummy);
}
@Override
public void removeListener(JavaScriptDebuggerListener listener) {
listeners.remove(listener);
}
@Override
public void suspend() {
if (exchange == null) {
return;
}
Message message = new Message();
message.setMethod("Debugger.pause");
sendMessage(message);
}
@Override
public void resume() {
if (exchange == null) {
return;
}
Message message = new Message();
message.setMethod("Debugger.resume");
sendMessage(message);
}
@Override
public void stepInto() {
if (exchange == null) {
return;
}
Message message = new Message();
message.setMethod("Debugger.stepInto");
sendMessage(message);
}
@Override
public void stepOut() {
if (exchange == null) {
return;
}
Message message = new Message();
message.setMethod("Debugger.stepOut");
sendMessage(message);
}
@Override
public void stepOver() {
if (exchange == null) {
return;
}
Message message = new Message();
message.setMethod("Debugger.stepOver");
sendMessage(message);
}
@Override
public void continueToLocation(JavaScriptLocation location) {
if (exchange == null) {
return;
}
Message message = new Message();
message.setMethod("Debugger.continueToLocation");
ContinueToLocationCommand params = new ContinueToLocationCommand();
params.setLocation(unmap(location));
message.setParams(mapper.valueToTree(params));
sendMessage(message);
}
@Override
public boolean isSuspended() {
return exchange != null && suspended;
}
@Override
public boolean isAttached() {
return exchange != null;
}
@Override
public void detach() {
if (exchange != null) {
exchange.disconnect();
}
}
@Override
public JavaScriptCallFrame[] getCallStack() {
if (exchange == null) {
return null;
}
JavaScriptCallFrame[] callStack = this.callStack;
return callStack != null ? callStack.clone() : null;
}
@Override
public JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location) {
RDPBreakpoint breakpoint;
breakpointLock.lock();
try {
breakpoint = breakpointLocationMap.get(location);
if (breakpoint == null) {
breakpoint = new RDPBreakpoint(this, location);
breakpointLocationMap.put(location, breakpoint);
updateBreakpoint(breakpoint);
}
breakpoint.referenceCount.incrementAndGet();
breakpoints.put(breakpoint, dummy);
} finally {
breakpointLock.unlock();
}
return breakpoint;
}
void destroyBreakpoint(RDPBreakpoint breakpoint) {
if (breakpoint.referenceCount.decrementAndGet() > 0) {
return;
}
breakpointLock.lock();
try {
if (breakpoint.referenceCount.get() > 0) {
return;
}
breakpointLocationMap.remove(breakpoint.getLocation());
breakpoints.remove(breakpoint);
if (breakpoint.chromeId != null) {
if (logger.isInfoEnabled()) {
logger.info("Removing breakpoint at {}", breakpoint.getLocation());
}
Message message = new Message();
message.setMethod("Debugger.removeBreakpoint");
RemoveBreakpointCommand params = new RemoveBreakpointCommand();
params.setBreakpointId(breakpoint.chromeId);
message.setParams(mapper.valueToTree(params));
sendMessage(message);
}
breakpoint.debugger = null;
breakpoint.chromeId = null;
} finally {
breakpointLock.unlock();
}
}
void fireScriptAdded(String script) {
for (JavaScriptDebuggerListener listener : getListeners()) {
listener.scriptAdded(script);
}
}
void updateBreakpoint(final RDPBreakpoint breakpoint) {
if (exchange == null || breakpoint.chromeId != null) {
return;
}
final Message message = new Message();
message.setId(messageIdGenerator.incrementAndGet());
message.setMethod("Debugger.setBreakpoint");
SetBreakpointCommand params = new SetBreakpointCommand();
params.setLocation(unmap(breakpoint.getLocation()));
message.setParams(mapper.valueToTree(params));
if (logger.isInfoEnabled()) {
logger.info("Setting breakpoint at {}, message id is ", breakpoint.getLocation(), message.getId());
}
ResponseHandler handler = new ResponseHandler() {
@Override public void received(JsonNode node) throws IOException {
if (node != null) {
SetBreakpointResponse response = mapper.reader(SetBreakpointResponse.class).readValue(node);
breakpoint.chromeId = response.getBreakpointId();
} else {
if (logger.isWarnEnabled()) {
logger.warn("Error setting breakpoint at {}, message id is {}",
breakpoint.getLocation(), message.getId());
}
breakpoint.chromeId = null;
}
for (JavaScriptDebuggerListener listener : getListeners()) {
listener.breakpointChanged(breakpoint);
}
}
};
responseHandlers.put(message.getId(), handler);
sendMessage(message);
}
List<RDPLocalVariable> getScope(String scopeId) {
if (exchange == null) {
return Collections.emptyList();
}
Message message = new Message();
message.setId(messageIdGenerator.incrementAndGet());
message.setMethod("Runtime.getProperties");
GetPropertiesCommand params = new GetPropertiesCommand();
params.setObjectId(scopeId);
params.setOwnProperties(true);
message.setParams(mapper.valueToTree(params));
final BlockingQueue<List<RDPLocalVariable>> sync = new LinkedTransferQueue<>();
responseHandlers.put(message.getId(), new ResponseHandler() {
@Override public void received(JsonNode node) throws IOException {
GetPropertiesResponse response = mapper.reader(GetPropertiesResponse.class).readValue(node);
sync.add(parseProperties(response.getResult()));
}
});
sendMessage(message);
try {
return sync.take();
} catch (InterruptedException e) {
return Collections.emptyList();
}
}
String getClassName(String objectId) {
if (exchange == null) {
return null;
}
Message message = new Message();
message.setId(messageIdGenerator.incrementAndGet());
message.setMethod("Runtime.callFunctionOn");
CallFunctionCommand params = new CallFunctionCommand();
CallArgumentDTO arg = new CallArgumentDTO();
arg.setObjectId(objectId);
params.setObjectId(objectId);
params.setArguments(new CallArgumentDTO[] { arg });
params.setFunctionDeclaration("$dbg_class");
message.setParams(mapper.valueToTree(params));
final BlockingQueue<String> sync = new LinkedTransferQueue<>();
responseHandlers.put(message.getId(), new ResponseHandler() {
@Override public void received(JsonNode node) throws IOException {
if (node == null) {
sync.add("");
} else {
CallFunctionResponse response = mapper.reader(CallFunctionResponse.class).readValue(node);
RemoteObjectDTO result = response.getResult();
sync.add(result.getValue() != null ? result.getValue().getTextValue() : "");
}
}
});
sendMessage(message);
try {
String result = sync.take();
return result.isEmpty() ? null : result;
} catch (InterruptedException e) {
return null;
}
}
String getRepresentation(String objectId) {
if (exchange == null) {
return null;
}
Message message = new Message();
message.setId(messageIdGenerator.incrementAndGet());
message.setMethod("Runtime.callFunctionOn");
CallFunctionCommand params = new CallFunctionCommand();
CallArgumentDTO arg = new CallArgumentDTO();
arg.setObjectId(objectId);
params.setObjectId(objectId);
params.setArguments(new CallArgumentDTO[] { arg });
params.setFunctionDeclaration("$dbg_repr");
message.setParams(mapper.valueToTree(params));
final BlockingQueue<RepresentationWrapper> sync = new LinkedTransferQueue<>();
responseHandlers.put(message.getId(), new ResponseHandler() {
@Override public void received(JsonNode node) throws IOException {
if (node == null) {
sync.add(new RepresentationWrapper(null));
} else {
CallFunctionResponse response = mapper.reader(CallFunctionResponse.class).readValue(node);
RemoteObjectDTO result = response.getResult();
sync.add(new RepresentationWrapper(result.getValue() != null ?
result.getValue().getTextValue() : null));
}
}
});
sendMessage(message);
try {
RepresentationWrapper result = sync.take();
return result.repr;
} catch (InterruptedException e) {
return null;
}
}
private static class RepresentationWrapper {
String repr;
public RepresentationWrapper(String repr) {
super();
this.repr = repr;
}
}
private List<RDPLocalVariable> parseProperties(PropertyDescriptorDTO[] properties) {
List<RDPLocalVariable> variables = new ArrayList<>();
if (properties != null) {
for (PropertyDescriptorDTO property : properties) {
RemoteObjectDTO remoteValue = property.getValue();
RDPValue value;
switch (remoteValue.getType()) {
case "undefined":
value = new RDPValue(this, "undefined", "undefined", null, false);
break;
case "object":
case "function":
value = new RDPValue(this, null, remoteValue.getType(), remoteValue.getObjectId(), true);
break;
default:
value = new RDPValue(this, remoteValue.getValue().asText(), remoteValue.getType(),
remoteValue.getObjectId(), false);
break;
}
RDPLocalVariable var = new RDPLocalVariable(property.getName(), value);
variables.add(var);
}
}
return variables;
}
private <T> T parseJson(Class<T> type, JsonNode node) throws IOException {
return mapper.reader(type).readValue(node);
}
private void sendMessage(Message message) {
if (exchange == null) {
return;
}
try {
exchange.send(mapper.writer().writeValueAsString(message));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
RDPCallFrame map(CallFrameDTO dto) {
String scopeId = null;
RDPValue thisObject = null;
RDPValue closure = null;
for (ScopeDTO scope : dto.getScopeChain()) {
if (scope.getType().equals("local")) {
scopeId = scope.getObject().getObjectId();
} else if (scope.getType().equals("closure")) {
closure = new RDPValue(this, scope.getObject().getDescription(), scope.getObject().getType(),
scope.getObject().getObjectId(), true);
} else if (scope.getType().equals("global")) {
thisObject = new RDPValue(this, scope.getObject().getDescription(), scope.getObject().getType(),
scope.getObject().getObjectId(), true);
}
}
return new RDPCallFrame(this, dto.getCallFrameId(), map(dto.getLocation()), new RDPScope(this, scopeId),
thisObject, closure);
}
JavaScriptLocation map(LocationDTO dto) {
return new JavaScriptLocation(scripts.get(dto.getScriptId()), dto.getLineNumber(), dto.getColumnNumber());
}
LocationDTO unmap(JavaScriptLocation location) {
LocationDTO dto = new LocationDTO();
dto.setScriptId(scriptIds.get(location.getScript()));
dto.setLineNumber(location.getLine());
dto.setColumnNumber(location.getColumn());
return dto;
}
interface ResponseHandler {
void received(JsonNode node) throws IOException;
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
/**
*
* @author Alexey Andreev
*/
@ServerEndpoint("/")
public class ChromeRDPDebuggerEndpoint implements ChromeRDPExchange {
public static final int MAX_MESSAGE_SIZE = 65534;
private Session session;
private ChromeRDPExchangeConsumer debugger;
private List<ChromeRDPExchangeListener> listeners = new ArrayList<>();
private StringBuilder messageBuffer = new StringBuilder();
@OnOpen
public void open(Session session) {
this.session = session;
session.setMaxIdleTimeout(0);
Object debugger = session.getUserProperties().get("chrome.rdp");
if (debugger instanceof ChromeRDPExchangeConsumer) {
this.debugger = (ChromeRDPExchangeConsumer)debugger;
this.debugger.setExchange(this);
}
}
@OnClose
public void close() {
if (this.debugger != null) {
this.debugger.setExchange(null);
this.debugger = null;
}
}
@Override
public void disconnect() {
try {
session.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@OnMessage
public void receive(String message) throws IOException {
char ctl = message.charAt(0);
messageBuffer.append(message.substring(1));
if (ctl == '.') {
message = messageBuffer.toString();
for (ChromeRDPExchangeListener listener : listeners) {
listener.received(message);
}
messageBuffer = new StringBuilder();
}
}
@Override
public void send(String message) {
int index = 0;
while (message.length() - index > MAX_MESSAGE_SIZE) {
int next = index + MAX_MESSAGE_SIZE;
session.getAsyncRemote().sendText("," + message.substring(index, next));
index = next;
}
session.getAsyncRemote().sendText("." + message.substring(index));
}
@Override
public void addListener(ChromeRDPExchangeListener listener) {
listeners.add(listener);
}
@Override
public void removeListener(ChromeRDPExchangeListener listener) {
listeners.remove(listener);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface ChromeRDPExchange {
void send(String message);
void disconnect();
void addListener(ChromeRDPExchangeListener listener);
void removeListener(ChromeRDPExchangeListener listener);
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface ChromeRDPExchangeConsumer {
void setExchange(ChromeRDPExchange exchange);
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp;
import java.io.IOException;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface ChromeRDPExchangeListener {
void received(String message) throws IOException;
}

View File

@ -0,0 +1,130 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp;
import java.util.*;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import javax.websocket.Extension;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
/**
*
* @author Alexey Andreev
*/
public class ChromeRDPServer {
private int port = 2357;
private ChromeRDPExchangeConsumer exchangeConsumer;
private Server server;
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public ChromeRDPExchangeConsumer getExchangeConsumer() {
return exchangeConsumer;
}
public void setExchangeConsumer(ChromeRDPExchangeConsumer exchangeConsumer) {
this.exchangeConsumer = exchangeConsumer;
}
public void start() {
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(port);
server.addConnector(connector);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
server.setHandler(context);
ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context);
try {
wscontainer.addEndpoint(new RPDEndpointConfig());
server.start();
server.join();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void stop() {
try {
server.stop();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private class RPDEndpointConfig implements ServerEndpointConfig {
private Map<String, Object> userProperties = new HashMap<>();
public RPDEndpointConfig() {
userProperties.put("chrome.rdp", exchangeConsumer);
}
@Override
public List<Class<? extends Decoder>> getDecoders() {
return Collections.emptyList();
}
@Override
public List<Class<? extends Encoder>> getEncoders() {
return Collections.emptyList();
}
@Override
public Map<String, Object> getUserProperties() {
return userProperties;
}
@Override
public Configurator getConfigurator() {
return null;
}
@Override
public Class<?> getEndpointClass() {
return ChromeRDPDebuggerEndpoint.class;
}
@Override
public List<Extension> getExtensions() {
return Collections.emptyList();
}
@Override
public String getPath() {
return "/";
}
@Override
public List<String> getSubprotocols() {
return Collections.emptyList();
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp;
import java.util.concurrent.atomic.AtomicInteger;
import org.teavm.debugging.javascript.JavaScriptBreakpoint;
import org.teavm.debugging.javascript.JavaScriptLocation;
/**
*
* @author Alexey Andreev
*/
public class RDPBreakpoint implements JavaScriptBreakpoint {
volatile String chromeId;
ChromeRDPDebugger debugger;
private JavaScriptLocation location;
AtomicInteger referenceCount = new AtomicInteger();
RDPBreakpoint(ChromeRDPDebugger debugger, JavaScriptLocation location) {
this.debugger = debugger;
this.location = location;
}
@Override
public JavaScriptLocation getLocation() {
return location;
}
@Override
public void destroy() {
if (debugger != null) {
debugger.destroyBreakpoint(this);
}
}
@Override
public boolean isValid() {
return chromeId != null && debugger != null && debugger.isAttached();
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp;
import java.util.Collections;
import java.util.Map;
import org.teavm.debugging.javascript.*;
/**
*
* @author Alexey Andreev
*/
public class RDPCallFrame implements JavaScriptCallFrame {
private JavaScriptDebugger debugger;
private String chromeId;
private JavaScriptLocation location;
private Map<String, JavaScriptVariable> variables;
private JavaScriptValue thisObject;
private JavaScriptValue closure;
public RDPCallFrame(JavaScriptDebugger debugger, String chromeId, JavaScriptLocation location,
Map<String, ? extends JavaScriptVariable> variables, JavaScriptValue thisObject,
JavaScriptValue closure) {
this.debugger = debugger;
this.chromeId = chromeId;
this.location = location;
this.variables = Collections.unmodifiableMap(variables);
this.thisObject = thisObject;
this.closure = closure;
}
public String getChromeId() {
return chromeId;
}
@Override
public JavaScriptLocation getLocation() {
return location;
}
@Override
public Map<String, JavaScriptVariable> getVariables() {
return variables;
}
@Override
public JavaScriptDebugger getDebugger() {
return debugger;
}
@Override
public JavaScriptValue getThisVariable() {
return thisObject;
}
@Override
public JavaScriptValue getClosureVariable() {
return closure;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp;
import org.teavm.debugging.javascript.JavaScriptValue;
import org.teavm.debugging.javascript.JavaScriptVariable;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class RDPLocalVariable implements JavaScriptVariable {
private String name;
private RDPValue value;
public RDPLocalVariable(String name, RDPValue value) {
this.name = name;
this.value = value;
}
@Override
public String getName() {
return name;
}
@Override
public JavaScriptValue getValue() {
return value;
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class RDPScope extends AbstractMap<String, RDPLocalVariable> {
private AtomicReference<Map<String, RDPLocalVariable>> backingMap = new AtomicReference<>();
private ChromeRDPDebugger debugger;
private String id;
public RDPScope(ChromeRDPDebugger debugger, String id) {
this.debugger = debugger;
this.id = id;
}
@Override
public Set<Entry<String, RDPLocalVariable>> entrySet() {
initBackingMap();
return backingMap.get().entrySet();
}
@Override
public int size() {
initBackingMap();
return backingMap.get().size();
}
@Override
public RDPLocalVariable get(Object key) {
initBackingMap();
return backingMap.get().get(key);
}
private void initBackingMap() {
if (backingMap.get() != null) {
return;
}
Map<String, RDPLocalVariable> newBackingMap = new HashMap<>();
if (id != null) {
for (RDPLocalVariable variable : debugger.getScope(id)) {
newBackingMap.put(variable.getName(), variable);
}
}
backingMap.compareAndSet(null, newBackingMap);
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import org.teavm.debugging.javascript.JavaScriptValue;
import org.teavm.debugging.javascript.JavaScriptVariable;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class RDPValue implements JavaScriptValue {
private AtomicReference<String> representation = new AtomicReference<>();
private AtomicReference<String> className = new AtomicReference<>();
private String typeName;
private ChromeRDPDebugger debugger;
private String objectId;
private Map<String, ? extends JavaScriptVariable> properties;
private boolean innerStructure;
public RDPValue(ChromeRDPDebugger debugger, String representation, String typeName, String objectId,
boolean innerStructure) {
this.representation.set(representation == null && objectId == null ? "" : representation);
this.typeName = typeName;
this.debugger = debugger;
this.objectId = objectId;
this.innerStructure = innerStructure;
properties = objectId != null ? new RDPScope(debugger, objectId) :
Collections.<String, RDPLocalVariable>emptyMap();
}
@Override
public String getRepresentation() {
if (representation.get() == null) {
representation.compareAndSet(null, debugger.getRepresentation(objectId));
}
return representation.get();
}
@Override
public String getClassName() {
if (className.get() == null) {
if (objectId != null) {
String computedClassName = debugger.getClassName(objectId);
className.compareAndSet(null, computedClassName != null ? computedClassName : "@Object");
} else {
className.compareAndSet(null, "@" + typeName);
}
}
return className.get();
}
@SuppressWarnings("unchecked")
@Override
public Map<String, JavaScriptVariable> getProperties() {
return (Map<String, JavaScriptVariable>)properties;
}
@Override
public boolean hasInnerStructure() {
return innerStructure;
}
@Override
public String getInstanceId() {
return objectId;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.data;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class CallArgumentDTO {
private String objectId;
private JsonNode value;
public String getObjectId() {
return objectId;
}
public void setObjectId(String objectId) {
this.objectId = objectId;
}
public JsonNode getValue() {
return value;
}
public void setValue(JsonNode value) {
this.value = value;
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.data;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
/**
*
* @author Alexey Andreev
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class CallFrameDTO {
private String callFrameId;
private LocationDTO location;
private ScopeDTO[] scopeChain;
public String getCallFrameId() {
return callFrameId;
}
public void setCallFrameId(String callFrameId) {
this.callFrameId = callFrameId;
}
public LocationDTO getLocation() {
return location;
}
public void setLocation(LocationDTO location) {
this.location = location;
}
public ScopeDTO[] getScopeChain() {
return scopeChain;
}
public void setScopeChain(ScopeDTO[] scopeChain) {
this.scopeChain = scopeChain;
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.data;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
/**
*
* @author Alexey Andreev
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class LocationDTO {
private int columnNumber;
private int lineNumber;
private String scriptId;
public int getColumnNumber() {
return columnNumber;
}
public void setColumnNumber(int columnNumber) {
this.columnNumber = columnNumber;
}
public int getLineNumber() {
return lineNumber;
}
public void setLineNumber(int lineNumber) {
this.lineNumber = lineNumber;
}
public String getScriptId() {
return scriptId;
}
public void setScriptId(String scriptId) {
this.scriptId = scriptId;
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.data;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
/**
*
* @author Alexey Andreev
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class Message {
private Integer id;
private String method;
private JsonNode params;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public JsonNode getParams() {
return params;
}
public void setParams(JsonNode params) {
this.params = params;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.data;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class PropertyDescriptorDTO {
private String name;
private RemoteObjectDTO value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public RemoteObjectDTO getValue() {
return value;
}
public void setValue(RemoteObjectDTO value) {
this.value = value;
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.data;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class RemoteObjectDTO {
private String className;
private String description;
private String objectId;
private String type;
private JsonNode value;
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getObjectId() {
return objectId;
}
public void setObjectId(String objectId) {
this.objectId = objectId;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public JsonNode getValue() {
return value;
}
public void setValue(JsonNode value) {
this.value = value;
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.data;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
/**
*
* @author Alexey Andreev
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class Response {
private int id;
private JsonNode result;
private JsonNode error;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public JsonNode getResult() {
return result;
}
public void setResult(JsonNode result) {
this.result = result;
}
public JsonNode getError() {
return error;
}
public void setError(JsonNode error) {
this.error = error;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.data;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class ScopeDTO {
private RemoteObjectDTO object;
private String type;
public RemoteObjectDTO getObject() {
return object;
}
public void setObject(RemoteObjectDTO object) {
this.object = object;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.messages;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.teavm.chromerdp.data.CallArgumentDTO;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class CallFunctionCommand {
private String objectId;
private String functionDeclaration;
private CallArgumentDTO[] arguments;
private boolean returnByValue;
public String getObjectId() {
return objectId;
}
public void setObjectId(String objectId) {
this.objectId = objectId;
}
public String getFunctionDeclaration() {
return functionDeclaration;
}
public void setFunctionDeclaration(String functionDeclaration) {
this.functionDeclaration = functionDeclaration;
}
public CallArgumentDTO[] getArguments() {
return arguments;
}
public void setArguments(CallArgumentDTO[] arguments) {
this.arguments = arguments;
}
public boolean isReturnByValue() {
return returnByValue;
}
public void setReturnByValue(boolean returnByValue) {
this.returnByValue = returnByValue;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.messages;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.teavm.chromerdp.data.RemoteObjectDTO;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class CallFunctionResponse {
private RemoteObjectDTO result;
private boolean wasThrown;
public RemoteObjectDTO getResult() {
return result;
}
public void setResult(RemoteObjectDTO result) {
this.result = result;
}
public boolean isWasThrown() {
return wasThrown;
}
public void setWasThrown(boolean wasThrown) {
this.wasThrown = wasThrown;
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.messages;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.teavm.chromerdp.data.LocationDTO;
/**
*
* @author Alexey Andreev
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class ContinueToLocationCommand {
private LocationDTO location;
public LocationDTO getLocation() {
return location;
}
public void setLocation(LocationDTO location) {
this.location = location;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.messages;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class GetPropertiesCommand {
private String objectId;
private boolean ownProperties;
public String getObjectId() {
return objectId;
}
public void setObjectId(String objectId) {
this.objectId = objectId;
}
public boolean isOwnProperties() {
return ownProperties;
}
public void setOwnProperties(boolean ownProperties) {
this.ownProperties = ownProperties;
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.messages;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.teavm.chromerdp.data.PropertyDescriptorDTO;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class GetPropertiesResponse {
private PropertyDescriptorDTO[] result;
public PropertyDescriptorDTO[] getResult() {
return result;
}
public void setResult(PropertyDescriptorDTO[] properties) {
this.result = properties;
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.messages;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
/**
*
* @author Alexey Andreev
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class RemoveBreakpointCommand {
private String breakpointId;
public String getBreakpointId() {
return breakpointId;
}
public void setBreakpointId(String breakpointId) {
this.breakpointId = breakpointId;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.messages;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
/**
*
* @author Alexey Andreev
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class ScriptParsedNotification {
private String scriptId;
private String url;
public String getScriptId() {
return scriptId;
}
public void setScriptId(String scriptId) {
this.scriptId = scriptId;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.messages;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.teavm.chromerdp.data.LocationDTO;
/**
*
* @author Alexey Andreev
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class SetBreakpointCommand {
private LocationDTO location;
public LocationDTO getLocation() {
return location;
}
public void setLocation(LocationDTO location) {
this.location = location;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.messages;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.teavm.chromerdp.data.LocationDTO;
/**
*
* @author Alexey Andreev
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class SetBreakpointResponse {
private String breakpointId;
private LocationDTO actualLocation;
public String getBreakpointId() {
return breakpointId;
}
public void setBreakpointId(String breakpointId) {
this.breakpointId = breakpointId;
}
public LocationDTO getActualLocation() {
return actualLocation;
}
public void setActualLocation(LocationDTO actualLocation) {
this.actualLocation = actualLocation;
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.chromerdp.messages;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.teavm.chromerdp.data.CallFrameDTO;
/**
*
* @author Alexey Andreev
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class SuspendedNotification {
private CallFrameDTO[] callFrames;
private String reason;
private JsonNode data;
public CallFrameDTO[] getCallFrames() {
return callFrames;
}
public void setCallFrames(CallFrameDTO[] callFrames) {
this.callFrames = callFrames;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public JsonNode getData() {
return data;
}
public void setData(JsonNode data) {
this.data = data;
}
}

View File

@ -0,0 +1,98 @@
debuggerAgentMap = {};
chrome.browserAction.onClicked.addListener(function(tab) {
new DebuggerAgent(tab).attach();
});
function DebuggerAgent(tab) {
this.pendingMessages = [];
this.connection = null;
this.tab = null;
this.debuggee = { tabId : tab.id };
this.attachedToDebugger = false;
this.messageBuffer = "";
debuggerAgentMap[tab.id] = this;
}
DebuggerAgent.MAX_MESSAGE_SIZE = 65534;
DebuggerAgent.prototype.attach = function() {
chrome.debugger.attach(this.debuggee, "1.0", (function(callback) {
this.attachedToDebugger = true;
chrome.debugger.sendCommand(this.debuggee, "Debugger.enable", {}, callback);
}).bind(this, this.connectToServer.bind(this)));
};
DebuggerAgent.prototype.connectToServer = function() {
this.connection = new WebSocket("ws://localhost:2357/");
this.connection.onmessage = function(event) {
var str = event.data;
var ctl = str.substring(0, 1);
this.messageBuffer += str.substring(1);
if (ctl == '.') {
this.receiveMessage(JSON.parse(this.messageBuffer));
this.messageBuffer = "";
}
}.bind(this);
this.connection.onclose = function(event) {
if (this.connection != null) {
this.connection = null;
this.disconnect();
}
}.bind(this);
this.connection.onopen = function() {
for (var i = 0; i < this.pendingMessages.length; ++i) {
this.sendMessage(this.pendingMessages[i]);
}
this.pendingMessages = null;
}.bind(this);
};
DebuggerAgent.prototype.receiveMessage = function(message) {
chrome.debugger.sendCommand(this.debuggee, message.method, message.params, function(response) {
if (message.id) {
var responseToServer = { id : message.id, result : response,
error : response ? undefined : chrome.runtime.lastError };
this.sendMessage(responseToServer);
}
}.bind(this));
};
DebuggerAgent.prototype.sendMessage = function(message) {
var str = JSON.stringify(message);
while (str.length > DebuggerAgent.MAX_MESSAGE_SIZE) {
var part = "," + str.substring(0, DebuggerAgent.MAX_MESSAGE_SIZE);
this.connection.send(part);
str = str.substring(DebuggerAgent.MAX_MESSAGE_SIZE);
}
this.connection.send("." + str);
}
DebuggerAgent.prototype.disconnect = function() {
if (this.connection) {
var conn = this.connection;
this.connection = null;
conn.close();
}
if (this.attachedToDebugger) {
chrome.debugger.detach(this.debuggee);
this.attachedToDebugger = false;
}
if (this.debuggee) {
delete debuggerAgentMap[this.debuggee.tabId];
this.debuggee = null;
}
};
chrome.debugger.onEvent.addListener(function(source, method, params) {
var agent = debuggerAgentMap[source.tabId];
if (!agent) {
return;
}
var message = { method : method, params : params };
if (agent.pendingMessages) {
agent.pendingMessages.push(message);
} else if (agent.connection) {
agent.sendMessage(message);
}
});
chrome.debugger.onDetach.addListener(function(source) {
var agent = debuggerAgentMap[source.tabId];
if (agent) {
agent.attachedToDebugger = false;
agent.disconnect();
}
});

View File

@ -0,0 +1,18 @@
{
"manifest_version": 2,
"name": "TeaVM debugger agent",
"description": "TeaVM debugger agent, that sends RDP commands over WebSocket",
"version": "0.2",
"permissions" : ["debugger", "activeTab", "tabs"],
"browser_action" : {
"default_icon": "teavm-16.png",
"default_title ": "Connect to TeaVM debugger"
},
"background": {
"scripts": ["main.js"]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

View File

@ -24,28 +24,58 @@
</parent>
<artifactId>teavm-classlib</artifactId>
<packaging>bundle</packaging>
<name>TeaVM Java class library</name>
<description>TeaVM Java class library emulation</description>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-platform</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.4</version>
</dependency>
</dependencies>
<name>TeaVM JCL</name>
<description>TeaVM Java class library emulation</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Export-Package>org.teavm.classlib.*</Export-Package>
<Bundle-SymbolicName>teavm-classlib</Bundle-SymbolicName>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.teavm</groupId>
<artifactId>teavm-maven-plugin</artifactId>
<version>${project.version}</version>
<dependencies>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-platform</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>generate-javascript-tests</id>
@ -55,7 +85,11 @@
<phase>process-test-classes</phase>
<configuration>
<minifying>false</minifying>
<numThreads>1</numThreads>
<sourceMapsGenerated>true</sourceMapsGenerated>
<sourceFilesCopied>true</sourceFilesCopied>
<properties>
<java.util.Locale.available>en, en_US, en_GB, ru, ru_RU</java.util.Locale.available>
</properties>
</configuration>
</execution>
</executions>
@ -79,7 +113,9 @@
<argument>java.lang.annotation</argument>
<argument>java.lang.reflect</argument>
<argument>java.io</argument>
<argument>java.math</argument>
<argument>java.net</argument>
<argument>java.text</argument>
<argument>java.util</argument>
<argument>java.util.logging</argument>
<argument>java.util.concurrent</argument>
@ -89,6 +125,15 @@
</arguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>org/teavm/platform/metadata/*.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>

View File

@ -32,7 +32,7 @@ public class ClassLookupDependencySupport implements DependencyListener {
@Override
public void classAchieved(DependencyAgent agent, String className) {
allClasses.propagate(className);
allClasses.propagate(agent.getType(className));
}
@Override
@ -41,8 +41,8 @@ public class ClassLookupDependencySupport implements DependencyListener {
if (ref.getClassName().equals("java.lang.Class") && ref.getName().equals("forNameImpl")) {
final DependencyStack stack = method.getStack();
allClasses.addConsumer(new DependencyConsumer() {
@Override public void consume(String type) {
ClassReader cls = agent.getClassSource().get(type);
@Override public void consume(DependencyAgentType type) {
ClassReader cls = agent.getClassSource().get(type.getName());
if (cls == null) {
return;
}

View File

@ -40,7 +40,7 @@ public class EnumDependencySupport implements DependencyListener {
if (cls == null || cls.getParent() == null || !cls.getParent().equals("java.lang.Enum")) {
return;
}
allEnums.propagate(className);
allEnums.propagate(agent.getType(className));
if (enumConstantsStack != null) {
MethodReader method = cls.getMethod(new MethodDescriptor("values",
ValueType.arrayOf(ValueType.object(cls.getName()))));
@ -55,7 +55,7 @@ public class EnumDependencySupport implements DependencyListener {
if (method.getReference().getClassName().equals("java.lang.Class") &&
method.getReference().getName().equals("getEnumConstantsImpl")) {
allEnums.connect(method.getResult().getArrayItem());
method.getResult().propagate("[java.lang.Enum");
method.getResult().propagate(agent.getType("[java.lang.Enum"));
enumConstantsStack = method.getStack();
for (String cls : agent.getAchievableClasses()) {
classAchieved(agent, cls);

View File

@ -0,0 +1,30 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl;
import java.util.Map;
import org.teavm.classlib.impl.unicode.CLDRReader;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class FirstDayOfWeekMetadataGenerator extends WeekMetadataGenerator {
@Override
protected Map<String, Integer> getWeekData(CLDRReader reader) {
return reader.getFirstDayMap();
}
}

View File

@ -15,6 +15,7 @@
*/
package org.teavm.classlib.impl;
import org.teavm.classlib.impl.unicode.CLDRReader;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
@ -32,6 +33,7 @@ public class JCLPlugin implements TeaVMPlugin {
host.add(new EnumTransformer());
host.add(new ClassLookupDependencySupport());
host.add(new NewInstanceDependencySupport());
host.add(new ObjectEnrichRenderer());
ServiceLoaderSupport serviceLoaderSupp = new ServiceLoaderSupport(host.getClassLoader());
host.add(serviceLoaderSupp);
MethodReference loadServicesMethod = new MethodReference("java.util.ServiceLoader", new MethodDescriptor(
@ -40,5 +42,7 @@ public class JCLPlugin implements TeaVMPlugin {
host.add(loadServicesMethod, serviceLoaderSupp);
JavacSupport javacSupport = new JavacSupport();
host.add(javacSupport);
host.registerService(CLDRReader.class, new CLDRReader(host.getProperties(), host.getClassLoader()));
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl;
import java.util.Map;
import org.teavm.classlib.impl.unicode.CLDRReader;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class MinimalDaysInFirstWeekMetadataGenerator extends WeekMetadataGenerator {
@Override
protected Map<String, Integer> getWeekData(CLDRReader reader) {
return reader.getMinDaysMap();
}
}

View File

@ -42,7 +42,7 @@ public class NewInstanceDependencySupport implements DependencyListener {
}
MethodReader method = cls.getMethod(new MethodDescriptor("<init>", ValueType.VOID));
if (method != null) {
allClassesNode.propagate(className);
allClassesNode.propagate(agent.getType(className));
}
}
@ -53,8 +53,8 @@ public class NewInstanceDependencySupport implements DependencyListener {
newInstanceStack = method.getStack();
allClassesNode.connect(method.getResult());
method.getResult().addConsumer(new DependencyConsumer() {
@Override public void consume(String type) {
attachConstructor(agent, type);
@Override public void consume(DependencyAgentType type) {
attachConstructor(agent, type.getName());
}
});
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl;
import java.io.IOException;
import org.teavm.javascript.RenderingContext;
import org.teavm.model.ClassReader;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;
import org.teavm.vm.BuildTarget;
import org.teavm.vm.spi.RendererListener;
/**
*
* @author Alexey Andreev
*/
public class ObjectEnrichRenderer implements RendererListener {
private RenderingContext context;
@Override
public void begin(RenderingContext context, BuildTarget buildTarget) throws IOException {
this.context = context;
}
@Override
public void beforeClass(ClassReader cls) throws IOException {
}
@Override
public void afterClass(ClassReader cls) throws IOException {
if (cls.getName().equals("java.lang.Object")) {
MethodReader toString = cls.getMethod(new MethodDescriptor("toString", String.class));
if (toString != null) {
String clsName = context.getNaming().getNameFor(cls.getName());
String toStringName = context.getNaming().getNameFor(toString.getReference());
context.getWriter().append(clsName).append(".prototype.toString").ws().append('=').ws()
.append("function()").ws().append('{').indent().softNewLine();
context.getWriter().append("return this.").append(toStringName).ws().append('?').ws()
.append("$rt_ustr(this.").append(toStringName).append("())").ws().append(':')
.append("Object.prototype.toString.call(this);").softNewLine();
context.getWriter().outdent().append("}").newLine();
}
}
}
@Override
public void complete() throws IOException {
}
}

View File

@ -89,7 +89,7 @@ public class ServiceLoaderSupport implements Generator, DependencyListener {
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
try (InputStream stream = resource.openStream()) {
parseServiceFile(className, stream);
parseServiceFile(agent, className, stream);
}
}
} catch (IOException e) {
@ -97,7 +97,7 @@ public class ServiceLoaderSupport implements Generator, DependencyListener {
}
}
private void parseServiceFile(String service, InputStream input) throws IOException {
private void parseServiceFile(DependencyAgent agent, String service, InputStream input) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
while (true) {
String line = reader.readLine();
@ -114,7 +114,7 @@ public class ServiceLoaderSupport implements Generator, DependencyListener {
serviceMap.put(service, implementors);
}
implementors.add(line);
allClassesNode.propagate(line);
allClassesNode.propagate(agent.getType(line));
}
}
@ -122,12 +122,12 @@ public class ServiceLoaderSupport implements Generator, DependencyListener {
public void methodAchieved(final DependencyAgent agent, MethodDependency method) {
MethodReference ref = method.getReference();
if (ref.getClassName().equals("java.util.ServiceLoader") && ref.getName().equals("loadServices")) {
method.getResult().propagate("[java.lang.Object");
method.getResult().propagate(agent.getType("[java.lang.Object"));
stack = method.getStack();
allClassesNode.connect(method.getResult().getArrayItem());
method.getResult().getArrayItem().addConsumer(new DependencyConsumer() {
@Override public void consume(String type) {
initConstructor(agent, type);
@Override public void consume(DependencyAgentType type) {
initConstructor(agent, type.getName());
}
});
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl;
import java.util.Map;
import org.teavm.classlib.impl.unicode.CLDRReader;
import org.teavm.model.MethodReference;
import org.teavm.platform.metadata.*;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public abstract class WeekMetadataGenerator implements MetadataGenerator {
@Override
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
ResourceMap<IntResource> map = context.createResourceMap();
for (Map.Entry<String, Integer> entry : getWeekData(context.getService(CLDRReader.class)).entrySet()) {
IntResource valueRes = context.createResource(IntResource.class);
valueRes.setValue(entry.getValue());
map.put(entry.getKey(), valueRes);
}
return map;
}
protected abstract Map<String, Integer> getWeekData(CLDRReader reader);
}

View File

@ -166,7 +166,7 @@ public class JCLComparisonBuilder {
return false;
}
int slashIndex = name.lastIndexOf('/');
return packages.contains(name.substring(0, slashIndex));
return slashIndex >= 0 && packages.contains(name.substring(0, slashIndex));
}
private void compareClass(InputStream input) throws IOException {

View File

@ -0,0 +1,37 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl.unicode;
import org.teavm.model.MethodReference;
import org.teavm.platform.metadata.*;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class AvailableLocalesMetadataGenerator implements MetadataGenerator {
@Override
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
CLDRReader reader = context.getService(CLDRReader.class);
ResourceArray<StringResource> result = context.createResourceArray();
for (String locale : reader.getAvailableLocales()) {
StringResource localeRes = context.createResource(StringResource.class);
localeRes.setValue(locale);
result.add(localeRes);
}
return result;
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl.unicode;
/**
*
* @author Alexey Andreev
*/
public class CLDRDateFormats {
private String shortFormat;
private String mediumFormat;
private String longFormat;
private String fullFormat;
CLDRDateFormats(String shortFormat, String mediumFormat, String longFormat, String fullFormat) {
this.shortFormat = shortFormat;
this.mediumFormat = mediumFormat;
this.longFormat = longFormat;
this.fullFormat = fullFormat;
}
public String getShortFormat() {
return shortFormat;
}
public String getMediumFormat() {
return mediumFormat;
}
public String getLongFormat() {
return longFormat;
}
public String getFullFormat() {
return fullFormat;
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl.unicode;
import org.teavm.platform.metadata.Resource;
/**
*
* @author Alexey Andreev
*/
public interface CLDRDecimalData extends Resource {
int getMaximumFractionDigits();
void setMaximumFractionDigits(int value);
int getMaximumIntegerDigits();
void setMaximumIntegerDigits(int value);
int getMinimumFractionDigits();
void setMinimumFractionDigits(int value);
int getMinimumIntegerDigits();
void setMinimumIntegerDigits(int value);
int getGroupingSeparator();
void setGroupingSeparator(int value);
int getDecimalSeparator();
void setDecimalSeparator(int value);
int getPerMill();
void setPerMill(int value);
int getPercent();
void setPercent(int value);
String getNaN();
void setNaN(String nan);
String getInfinity();
void setInfinity(String infinity);
int getMinusSign();
void setMinusSign(int value);
int getMonetaryDecimalSeparator();
void setMonetaryDecimalSeparator(int value);
String getExponentSeparator();
void setExponentSeparator(String value);
}

View File

@ -0,0 +1,171 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl.unicode;
import org.teavm.classlib.impl.FirstDayOfWeekMetadataGenerator;
import org.teavm.classlib.impl.MinimalDaysInFirstWeekMetadataGenerator;
import org.teavm.platform.metadata.*;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class CLDRHelper {
public static String getCode(String language, String country) {
return !country.isEmpty() ? language + "-" + country : language;
}
public static String getLikelySubtags(String localeCode) {
ResourceMap<StringResource> map = getLikelySubtagsMap();
return map.has(localeCode) ? map.get(localeCode).getValue() : localeCode;
}
@MetadataProvider(LikelySubtagsMetadataGenerator.class)
private static native ResourceMap<StringResource> getLikelySubtagsMap();
public static String[] resolveEras(String language, String country) {
return resolveDateFormatSymbols(getErasMap(), language, country);
}
@MetadataProvider(DateSymbolsMetadataGenerator.class)
private static native ResourceMap<ResourceArray<StringResource>> getErasMap();
public static String[] resolveAmPm(String language, String country) {
return resolveDateFormatSymbols(getAmPmMap(), language, country);
}
@MetadataProvider(DateSymbolsMetadataGenerator.class)
private static native ResourceMap<ResourceArray<StringResource>> getAmPmMap();
public static String[] resolveMonths(String language, String country) {
return resolveDateFormatSymbols(getMonthMap(), language, country);
}
@MetadataProvider(DateSymbolsMetadataGenerator.class)
private static native ResourceMap<ResourceArray<StringResource>> getMonthMap();
public static String[] resolveShortMonths(String language, String country) {
return resolveDateFormatSymbols(getShortMonthMap(), language, country);
}
@MetadataProvider(DateSymbolsMetadataGenerator.class)
private static native ResourceMap<ResourceArray<StringResource>> getShortMonthMap();
public static String[] resolveWeekdays(String language, String country) {
return resolveDateFormatSymbols(getWeekdayMap(), language, country);
}
@MetadataProvider(DateSymbolsMetadataGenerator.class)
private static native ResourceMap<ResourceArray<StringResource>> getWeekdayMap();
public static String[] resolveShortWeekdays(String language, String country) {
return resolveDateFormatSymbols(getShortWeekdayMap(), language, country);
}
@MetadataProvider(DateSymbolsMetadataGenerator.class)
private static native ResourceMap<ResourceArray<StringResource>> getShortWeekdayMap();
private static String[] resolveDateFormatSymbols(ResourceMap<ResourceArray<StringResource>> map, String language,
String country) {
String localeCode = getCode(language, country);
ResourceArray<StringResource> arrayRes = map.has(localeCode) ? map.get(localeCode) :
map.has(language) ? map.get(language) : map.get("root");
String[] result = new String[arrayRes.size()];
for (int i = 0; i < result.length; ++i) {
result[i] = arrayRes.get(i).getValue();
}
return result;
}
@MetadataProvider(LanguageMetadataGenerator.class)
public static native ResourceMap<ResourceMap<StringResource>> getLanguagesMap();
@MetadataProvider(CountryMetadataGenerator.class)
public static native ResourceMap<ResourceMap<StringResource>> getCountriesMap();
@MetadataProvider(DefaultLocaleMetadataGenerator.class)
public static native StringResource getDefaultLocale();
@MetadataProvider(AvailableLocalesMetadataGenerator.class)
public static native ResourceArray<StringResource> getAvailableLocales();
@MetadataProvider(MinimalDaysInFirstWeekMetadataGenerator.class)
public static native ResourceMap<IntResource> getMinimalDaysInFirstWeek();
@MetadataProvider(FirstDayOfWeekMetadataGenerator.class)
public static native ResourceMap<IntResource> getFirstDayOfWeek();
public static DateFormatCollection resolveDateFormats(String language, String country) {
return resolveDateFormats(getDateFormatMap(), language, country);
}
@MetadataProvider(DateFormatMetadataGenerator.class)
private static native ResourceMap<DateFormatCollection> getDateFormatMap();
public static DateFormatCollection resolveTimeFormats(String language, String country) {
return resolveDateFormats(getTimeFormatMap(), language, country);
}
@MetadataProvider(DateFormatMetadataGenerator.class)
private static native ResourceMap<DateFormatCollection> getTimeFormatMap();
public static DateFormatCollection resolveDateTimeFormats(String language, String country) {
return resolveDateFormats(getDateTimeFormatMap(), language, country);
}
@MetadataProvider(DateFormatMetadataGenerator.class)
private static native ResourceMap<DateFormatCollection> getDateTimeFormatMap();
public static String resolveNumberFormat(String language, String country) {
return resolveFormatSymbols(getNumberFormatMap(), language, country);
}
private static native ResourceMap<StringResource> getNumberFormatMap();
public static String resolveDecimalFormat(String language, String country) {
return resolveFormatSymbols(getDecimalFormatMap(), language, country);
}
private static native ResourceMap<StringResource> getDecimalFormatMap();
public static String resolvePercentFormat(String language, String country) {
return resolveFormatSymbols(getPercentFormatMap(), language, country);
}
private static native ResourceMap<StringResource> getPercentFormatMap();
private static DateFormatCollection resolveDateFormats(ResourceMap<DateFormatCollection> map,
String language, String country) {
String localeCode = getCode(language, country);
return map.has(localeCode) ? map.get(localeCode) : map.has(language) ? map.get(language) : map.get("root");
}
private static String resolveFormatSymbols(ResourceMap<StringResource> map, String language, String country) {
String localeCode = getCode(language, country);
StringResource res = map.has(localeCode) ? map.get(localeCode) : map.has(language) ? map.get(language) :
map.get("root");
return res.getValue();
}
public static CLDRDecimalData resolveDecimalData(String language, String country) {
ResourceMap<CLDRDecimalData> map = getDecimalDataMap();
String localeCode = getCode(language, country);
return map.has(localeCode) ? map.get(localeCode) : map.has(language) ? map.get(language) :
map.get("root");
}
private static native ResourceMap<CLDRDecimalData> getDecimalDataMap();
}

View File

@ -0,0 +1,83 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl.unicode;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
/**
*
* @author Alexey Andreev
*/
public class CLDRLocale {
final Map<String, String> languages = new LinkedHashMap<>();
final Map<String, String> territories = new LinkedHashMap<>();
String[] eras;
String[] dayPeriods;
String[] months;
String[] shortMonths;
String[] weekdays;
String[] shortWeekdays;
CLDRDateFormats dateFormats;
CLDRDateFormats timeFormats;
CLDRDateFormats dateTimeFormats;
public Map<String, String> getLanguages() {
return Collections.unmodifiableMap(languages);
}
public Map<String, String> getTerritories() {
return Collections.unmodifiableMap(territories);
}
public String[] getEras() {
return Arrays.copyOf(eras, eras.length);
}
public String[] getDayPeriods() {
return Arrays.copyOf(dayPeriods, dayPeriods.length);
}
public String[] getMonths() {
return Arrays.copyOf(months, months.length);
}
public String[] getShortMonths() {
return Arrays.copyOf(shortMonths, shortMonths.length);
}
public String[] getWeekdays() {
return Arrays.copyOf(weekdays, weekdays.length);
}
public String[] getShortWeekdays() {
return Arrays.copyOf(shortWeekdays, shortWeekdays.length);
}
public CLDRDateFormats getDateFormats() {
return dateFormats;
}
public CLDRDateFormats getTimeFormats() {
return timeFormats;
}
public CLDRDateFormats getDateTimeFormats() {
return dateTimeFormats;
}
}

View File

@ -0,0 +1,325 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl.unicode;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
*
* @author Alexey Andreev
*/
public class CLDRReader {
private static String[] weekdayKeys = { "sun", "mon", "tue", "wed", "thu", "fri", "sat" };
private Map<String, CLDRLocale> knownLocales = new LinkedHashMap<>();
private Map<String, Integer> minDaysMap = new LinkedHashMap<>();
private Map<String, Integer> firstDayMap = new LinkedHashMap<>();
private Map<String, String> likelySubtags = new LinkedHashMap<>();
private Set<String> availableLocales = new LinkedHashSet<>();
private Set<String> availableLanguages = new LinkedHashSet<>();
private Set<String> availableCountries = new LinkedHashSet<>();
private boolean initialized;
private Properties properties;
private ClassLoader classLoader;
public CLDRReader(Properties properties, ClassLoader classLoader) {
this.properties = properties;
this.classLoader = classLoader;
}
private synchronized void ensureInitialized() {
if (!initialized) {
initialized = true;
findAvailableLocales(properties);
readCLDR(classLoader);
}
}
private void findAvailableLocales(Properties properties) {
String availableLocalesString = properties.getProperty("java.util.Locale.available", "en_EN").trim();
for (String locale : Arrays.asList(availableLocalesString.split(" *, *"))) {
int countryIndex = locale.indexOf('_');
if (countryIndex > 0) {
String language = locale.substring(0, countryIndex);
String country = locale.substring(countryIndex + 1);
availableLocales.add(language + "-" + country);
availableLocales.add(language);
availableLanguages.add(language);
availableCountries.add(country);
} else {
availableLocales.add(locale);
availableLanguages.add(locale);
}
}
}
private void readCLDR(ClassLoader classLoader) {
try (ZipInputStream input = new ZipInputStream(classLoader.getResourceAsStream(
"org/teavm/classlib/impl/unicode/cldr-json.zip"))) {
while (true) {
ZipEntry entry = input.getNextEntry();
if (entry == null) {
break;
}
if (!entry.getName().endsWith(".json")) {
continue;
}
if (entry.getName().equals("supplemental/weekData.json")) {
readWeekData(input);
continue;
} else if (entry.getName().equals("supplemental/likelySubtags.json")) {
readLikelySubtags(input);
}
int objectIndex = entry.getName().lastIndexOf('/');
String objectName = entry.getName().substring(objectIndex + 1);
String localeName = entry.getName().substring(0, objectIndex);
if (localeName.startsWith("/")) {
localeName = localeName.substring(1);
}
if (!localeName.equals("root") && !availableLocales.contains(localeName)) {
continue;
}
CLDRLocale localeInfo = knownLocales.get(localeName);
if (localeInfo == null) {
localeInfo = new CLDRLocale();
knownLocales.put(localeName, localeInfo);
}
switch (objectName) {
case "languages.json":
readLanguages(localeName, localeInfo, input);
break;
case "territories.json":
readCountries(localeName, localeInfo, input);
break;
case "ca-gregorian.json": {
JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
readEras(localeName, localeInfo, root);
readAmPms(localeName, localeInfo, root);
readMonths(localeName, localeInfo, root);
readShortMonths(localeName, localeInfo, root);
readWeekdays(localeName, localeInfo, root);
readShortWeekdays(localeName, localeInfo, root);
readDateFormats(localeName, localeInfo, root);
readTimeFormats(localeName, localeInfo, root);
readDateTimeFormats(localeName, localeInfo, root);
break;
}
}
}
} catch (IOException e) {
throw new RuntimeException("Error reading CLDR file", e);
}
}
private void readLanguages(String localeCode, CLDRLocale locale, InputStream input) {
JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
JsonObject languagesJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
.get("localeDisplayNames").getAsJsonObject().get("languages").getAsJsonObject();
for (Map.Entry<String, JsonElement> property : languagesJson.entrySet()) {
String language = property.getKey();
if (availableLanguages.contains(language)) {
locale.languages.put(language, property.getValue().getAsString());
}
}
}
private void readCountries(String localeCode, CLDRLocale locale, InputStream input) {
JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
JsonObject countriesJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
.get("localeDisplayNames").getAsJsonObject().get("territories").getAsJsonObject();
for (Map.Entry<String, JsonElement> property : countriesJson.entrySet()) {
String country = property.getKey();
if (availableCountries.contains(country)) {
locale.territories.put(country, property.getValue().getAsString());
}
}
}
private void readEras(String localeCode, CLDRLocale locale, JsonObject root) {
JsonObject erasJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
.get("gregorian").getAsJsonObject().get("eras").getAsJsonObject().get("eraAbbr").getAsJsonObject();
String bc = erasJson.get("0").getAsString();
String ac = erasJson.get("1").getAsString();
locale.eras = new String[] { bc, ac };
}
private void readAmPms(String localeCode, CLDRLocale locale, JsonObject root) {
JsonObject ampmJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
.get("gregorian").getAsJsonObject().get("dayPeriods").getAsJsonObject()
.get("format").getAsJsonObject().get("abbreviated").getAsJsonObject();
String am = ampmJson.get("am").getAsString();
String pm = ampmJson.get("pm").getAsString();
locale.dayPeriods = new String[] { am, pm };
}
private void readMonths(String localeCode, CLDRLocale locale, JsonObject root) {
JsonObject monthsJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
.get("gregorian").getAsJsonObject().get("months").getAsJsonObject()
.get("format").getAsJsonObject().get("wide").getAsJsonObject();
locale.months = new String[12];
for (int i = 0; i < 12; ++i) {
locale.months[i] = monthsJson.get(String.valueOf(i + 1)).getAsString();
}
}
private void readShortMonths(String localeCode, CLDRLocale locale, JsonObject root) {
JsonObject monthsJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
.get("gregorian").getAsJsonObject().get("months").getAsJsonObject()
.get("format").getAsJsonObject().get("abbreviated").getAsJsonObject();
locale.shortMonths = new String[12];
for (int i = 0; i < 12; ++i) {
locale.shortMonths[i] = monthsJson.get(String.valueOf(i + 1)).getAsString();
}
}
private void readWeekdays(String localeCode, CLDRLocale locale, JsonObject root) {
JsonObject weekdaysJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
.get("gregorian").getAsJsonObject().get("days").getAsJsonObject()
.get("format").getAsJsonObject().get("wide").getAsJsonObject();
locale.weekdays = new String[7];
for (int i = 0; i < 7; ++i) {
locale.weekdays[i] = weekdaysJson.get(weekdayKeys[i]).getAsString();
}
}
private void readShortWeekdays(String localeCode, CLDRLocale locale, JsonObject root) {
JsonObject weekdaysJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
.get("gregorian").getAsJsonObject().get("days").getAsJsonObject()
.get("format").getAsJsonObject().get("abbreviated").getAsJsonObject();
locale.shortWeekdays = new String[7];
for (int i = 0; i < 7; ++i) {
locale.shortWeekdays[i] = weekdaysJson.get(weekdayKeys[i]).getAsString();
}
}
private void readDateFormats(String localeCode, CLDRLocale locale, JsonObject root) {
JsonObject formatsJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
.get("gregorian").getAsJsonObject().get("dateFormats").getAsJsonObject();
locale.dateFormats = new CLDRDateFormats(formatsJson.get("short").getAsString(),
formatsJson.get("medium").getAsString(), formatsJson.get("long").getAsString(),
formatsJson.get("full").getAsString());
}
private void readTimeFormats(String localeCode, CLDRLocale locale, JsonObject root) {
JsonObject formatsJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
.get("gregorian").getAsJsonObject().get("timeFormats").getAsJsonObject();
locale.timeFormats = new CLDRDateFormats(formatsJson.get("short").getAsString(),
formatsJson.get("medium").getAsString(), formatsJson.get("long").getAsString(),
formatsJson.get("full").getAsString());
}
private void readDateTimeFormats(String localeCode, CLDRLocale locale, JsonObject root) {
JsonObject formatsJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()
.get("gregorian").getAsJsonObject().get("dateTimeFormats").getAsJsonObject();
locale.dateTimeFormats = new CLDRDateFormats(formatsJson.get("short").getAsString(),
formatsJson.get("medium").getAsString(), formatsJson.get("long").getAsString(),
formatsJson.get("full").getAsString());
}
private void readWeekData(InputStream input) {
JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
JsonObject weekJson = root.get("supplemental").getAsJsonObject().get("weekData").getAsJsonObject();
JsonObject minDaysJson = weekJson.get("minDays").getAsJsonObject();
for (Map.Entry<String, JsonElement> property : minDaysJson.entrySet()) {
minDaysMap.put(property.getKey(), property.getValue().getAsInt());
}
JsonObject firstDayJson = weekJson.get("firstDay").getAsJsonObject();
for (Map.Entry<String, JsonElement> property : firstDayJson.entrySet()) {
firstDayMap.put(property.getKey(), getNumericDay(property.getValue().getAsString()));
}
}
private void readLikelySubtags(InputStream input) {
JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
JsonObject likelySubtagsJson = root.get("supplemental").getAsJsonObject().get("likelySubtags")
.getAsJsonObject();
for (Map.Entry<String, JsonElement> property : likelySubtagsJson.entrySet()) {
likelySubtags.put(property.getKey(), property.getValue().getAsString());
}
}
private int getNumericDay(String day) {
switch (day) {
case "sun":
return 1;
case "mon":
return 2;
case "tue":
return 3;
case "wed":
return 4;
case "thu":
return 5;
case "fri":
return 6;
case "sat":
return 7;
default:
throw new IllegalArgumentException("Can't recognize day name: " + day);
}
}
public Map<String, CLDRLocale> getKnownLocales() {
ensureInitialized();
return Collections.unmodifiableMap(knownLocales);
}
public Set<String> getAvailableLocales() {
ensureInitialized();
return Collections.unmodifiableSet(availableLocales);
}
public Set<String> getAvailableLanguages() {
ensureInitialized();
return Collections.unmodifiableSet(availableLanguages);
}
public Set<String> getAvailableCountries() {
ensureInitialized();
return Collections.unmodifiableSet(availableCountries);
}
public Map<String, Integer> getMinDaysMap() {
ensureInitialized();
return Collections.unmodifiableMap(minDaysMap);
}
public Map<String, Integer> getFirstDayMap() {
ensureInitialized();
return Collections.unmodifiableMap(firstDayMap);
}
public Map<String, String> getLikelySubtags() {
ensureInitialized();
return Collections.unmodifiableMap(likelySubtags);
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl.unicode;
import java.util.Map;
/**
*
* @author Alexey Andreev
*/
public class CountryMetadataGenerator extends LocaleMetadataGenerator {
@Override
protected Map<String, String> getNameMap(CLDRLocale locale) {
return locale.getTerritories();
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl.unicode;
import org.teavm.platform.metadata.Resource;
/**
*
* @author Alexey Andreev
*/
public interface DateFormatCollection extends Resource {
String getShortFormat();
void setShortFormat(String format);
String getMediumFormat();
void setMediumFormat(String format);
String getLongFormat();
void setLongFormat(String format);
String getFullFormat();
void setFullFormat(String format);
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl.unicode;
import java.util.Map;
import org.teavm.model.MethodReference;
import org.teavm.platform.metadata.*;
/**
*
* @author Alexey Andreev
*/
public class DateFormatMetadataGenerator implements MetadataGenerator {
@Override
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
switch (method.getName()) {
case "getDateFormatMap":
return getDateFormatMap(context, new FormatExtractor() {
@Override public CLDRDateFormats extract(CLDRLocale locale) {
return locale.getDateFormats();
}
});
case "getTimeFormatMap":
return getDateFormatMap(context, new FormatExtractor() {
@Override public CLDRDateFormats extract(CLDRLocale locale) {
return locale.getTimeFormats();
}
});
case "getDateTimeFormatMap":
return getDateFormatMap(context, new FormatExtractor() {
@Override public CLDRDateFormats extract(CLDRLocale locale) {
return locale.getDateTimeFormats();
}
});
default:
throw new IllegalArgumentException("Method is not supported: " + method);
}
}
private Resource getDateFormatMap(MetadataGeneratorContext context, FormatExtractor extractor) {
CLDRReader reader = context.getService(CLDRReader.class);
ResourceMap<DateFormatCollection> result = context.createResourceMap();
for (Map.Entry<String, CLDRLocale> entry : reader.getKnownLocales().entrySet()) {
DateFormatCollection formatRes = context.createResource(DateFormatCollection.class);
CLDRDateFormats formats = extractor.extract(entry.getValue());
formatRes.setShortFormat(formats.getShortFormat());
formatRes.setMediumFormat(formats.getMediumFormat());
formatRes.setLongFormat(formats.getLongFormat());
formatRes.setFullFormat(formats.getFullFormat());
result.put(entry.getKey(), formatRes);
}
return result;
}
interface FormatExtractor {
CLDRDateFormats extract(CLDRLocale locale);
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl.unicode;
import java.util.Map;
import org.teavm.model.MethodReference;
import org.teavm.platform.metadata.*;
/**
*
* @author Alexey Andreev
*/
public class DateSymbolsMetadataGenerator implements MetadataGenerator {
@Override
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
switch (method.getName()) {
case "getErasMap":
return generateSymbols(context, new ResourceExtractor() {
@Override public String[] extract(CLDRLocale locale) { return locale.getEras(); }
});
case "getAmPmMap":
return generateSymbols(context, new ResourceExtractor() {
@Override public String[] extract(CLDRLocale locale) { return locale.getDayPeriods(); }
});
case "getMonthMap":
return generateSymbols(context, new ResourceExtractor() {
@Override public String[] extract(CLDRLocale locale) { return locale.getMonths(); }
});
case "getShortMonthMap":
return generateSymbols(context, new ResourceExtractor() {
@Override public String[] extract(CLDRLocale locale) { return locale.getShortMonths(); }
});
case "getWeekdayMap":
return generateSymbols(context, new ResourceExtractor() {
@Override public String[] extract(CLDRLocale locale) { return locale.getWeekdays(); }
});
case "getShortWeekdayMap":
return generateSymbols(context, new ResourceExtractor() {
@Override public String[] extract(CLDRLocale locale) { return locale.getShortWeekdays(); }
});
default:
throw new AssertionError("Unsupported method: " + method);
}
}
private Resource generateSymbols(MetadataGeneratorContext context, ResourceExtractor extractor) {
CLDRReader reader = context.getService(CLDRReader.class);
ResourceMap<ResourceArray<StringResource>> result = context.createResourceMap();
for (Map.Entry<String, CLDRLocale> localeEntry : reader.getKnownLocales().entrySet()) {
ResourceArray<StringResource> symbolsRes = context.createResourceArray();
result.put(localeEntry.getKey(), symbolsRes);
for (String symbol : extractor.extract(localeEntry.getValue())) {
StringResource symbolRes = context.createResource(StringResource.class);
symbolRes.setValue(symbol);
symbolsRes.add(symbolRes);
}
}
return result;
}
private static interface ResourceExtractor {
String[] extract(CLDRLocale locale);
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl.unicode;
import org.teavm.model.MethodReference;
import org.teavm.platform.metadata.MetadataGenerator;
import org.teavm.platform.metadata.MetadataGeneratorContext;
import org.teavm.platform.metadata.Resource;
import org.teavm.platform.metadata.StringResource;
/**
*
* @author Alexey Andreev
*/
public class DefaultLocaleMetadataGenerator implements MetadataGenerator {
@Override
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
StringResource result = context.createResource(StringResource.class);
result.setValue(context.getProperties().getProperty("java.util.Locale.default", "en_EN"));
return result;
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl.unicode;
import java.util.Map;
/**
*
* @author Alexey Andreev
*/
public class LanguageMetadataGenerator extends LocaleMetadataGenerator {
@Override
protected Map<String, String> getNameMap(CLDRLocale locale) {
return locale.getLanguages();
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl.unicode;
import java.util.Map;
import org.teavm.model.MethodReference;
import org.teavm.platform.metadata.*;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class LikelySubtagsMetadataGenerator implements MetadataGenerator {
@Override
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
CLDRReader reader = context.getService(CLDRReader.class);
ResourceMap<StringResource> map = context.createResourceMap();
for (Map.Entry<String, String> entry : reader.getLikelySubtags().entrySet()) {
StringResource subtagRes = context.createResource(StringResource.class);
subtagRes.setValue(entry.getValue());
map.put(entry.getKey(), subtagRes);
}
return map;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.impl.unicode;
import java.util.Map;
import org.teavm.model.MethodReference;
import org.teavm.platform.metadata.*;
/**
*
* @author Alexey Andreev
*/
public abstract class LocaleMetadataGenerator implements MetadataGenerator {
@Override
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
ResourceMap<ResourceMap<StringResource>> result = context.createResourceMap();
CLDRReader reader = context.getService(CLDRReader.class);
for (Map.Entry<String, CLDRLocale> entry : reader.getKnownLocales().entrySet()) {
CLDRLocale locale = entry.getValue();
ResourceMap<StringResource> names = context.createResourceMap();
result.put(entry.getKey(), names);
for (Map.Entry<String, String> nameEntry : getNameMap(locale).entrySet()) {
StringResource name = context.createResource(StringResource.class);
name.setValue(nameEntry.getValue());
names.put(nameEntry.getKey(), name);
}
}
return result;
}
protected abstract Map<String, String> getNameMap(CLDRLocale locale);
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.io;
import org.teavm.classlib.java.lang.TString;
/**
*
* @author Alexey Andreev
*/
public class TEOFException extends TIOException {
private static final long serialVersionUID = 3045477060413545010L;
public TEOFException() {
super();
}
public TEOFException(TString message) {
super(message);
}
}

View File

@ -19,7 +19,7 @@ import java.io.IOException;
import org.teavm.classlib.impl.unicode.UnicodeHelper;
import org.teavm.classlib.impl.unicode.UnicodeSupport;
import org.teavm.codegen.SourceWriter;
import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.MethodDependency;
import org.teavm.javascript.ni.Generator;
@ -52,11 +52,11 @@ public class CharacterNativeGenerator implements Generator, DependencyPlugin {
}
@Override
public void methodAchieved(DependencyChecker checker, MethodDependency method) {
public void methodAchieved(DependencyAgent agent, MethodDependency method) {
switch (method.getReference().getName()) {
case "obtainDigitMapping":
case "obtainClasses":
method.getResult().propagate("java.lang.String");
method.getResult().propagate(agent.getType("java.lang.String"));
break;
}
}

View File

@ -17,7 +17,7 @@ package org.teavm.classlib.java.lang;
import java.io.IOException;
import org.teavm.codegen.SourceWriter;
import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.MethodDependency;
import org.teavm.javascript.ni.Generator;
@ -186,7 +186,7 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug
}
@Override
public void methodAchieved(DependencyChecker checker, MethodDependency graph) {
public void methodAchieved(DependencyAgent agent, MethodDependency graph) {
switch (graph.getReference().getName()) {
case "voidClass":
case "booleanClass":
@ -202,10 +202,10 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug
case "getComponentType0":
case "forNameImpl":
case "getDeclaringClass":
graph.getResult().propagate("java.lang.Class");
graph.getResult().propagate(agent.getType("java.lang.Class"));
break;
case "newInstance":
checker.linkMethod(new MethodReference(InstantiationException.class.getName(), "<init>",
agent.linkMethod(new MethodReference(InstantiationException.class.getName(), "<init>",
ValueType.VOID), graph.getStack()).use();
break;
}

View File

@ -17,16 +17,12 @@ package org.teavm.classlib.java.lang;
import java.io.IOException;
import org.teavm.codegen.SourceWriter;
import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.MethodDependency;
import org.teavm.dependency.*;
import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.javascript.ni.Injector;
import org.teavm.javascript.ni.InjectorContext;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
/**
*
@ -62,13 +58,13 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu
}
@Override
public void methodAchieved(DependencyChecker checker, MethodDependency method) {
public void methodAchieved(DependencyAgent agent, MethodDependency method) {
switch (method.getReference().getName()) {
case "clone":
method.getVariable(0).connect(method.getResult());
break;
case "getClass":
achieveGetClass(checker, method);
achieveGetClass(agent, method);
break;
case "wrap":
method.getVariable(1).connect(method.getResult());
@ -87,12 +83,10 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu
writer.append(".constructor)");
}
private void achieveGetClass(DependencyChecker checker, MethodDependency method) {
String classClass = "java.lang.Class";
MethodReference initMethod = new MethodReference(classClass, new MethodDescriptor("createNew",
ValueType.object(classClass)));
checker.addEntryPoint(initMethod);
method.getResult().propagate("java.lang.Class");
private void achieveGetClass(DependencyAgent agent, MethodDependency method) {
MethodReference initMethod = new MethodReference(Class.class, "createNew", Class.class);
agent.linkMethod(initMethod, method.getStack()).use();
method.getResult().propagate(agent.getType("java.lang.Class"));
}
private void generateHashCode(GeneratorContext context, SourceWriter writer) throws IOException {

View File

@ -16,7 +16,7 @@
package org.teavm.classlib.java.lang;
import java.io.IOException;
import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.MethodDependency;
import org.teavm.javascript.ni.Injector;
@ -29,7 +29,7 @@ import org.teavm.model.MethodReference;
*/
public class StringNativeGenerator implements Injector, DependencyPlugin {
@Override
public void methodAchieved(DependencyChecker checker, MethodDependency method) {
public void methodAchieved(DependencyAgent agent, MethodDependency method) {
switch (method.getReference().getName()) {
case "wrap":
method.getVariable(1).connect(method.getResult());

View File

@ -54,16 +54,16 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin {
}
@Override
public void methodAchieved(DependencyChecker checker, MethodDependency method) {
public void methodAchieved(DependencyAgent agent, MethodDependency method) {
switch (method.getReference().getName()) {
case "doArrayCopy":
achieveArrayCopy(method);
break;
case "setOut":
achieveSetOut(checker, method);
achieveSetOut(agent, method);
break;
case "setErr":
achieveSetErr(checker, method);
achieveSetErr(agent, method);
break;
}
}
@ -97,13 +97,13 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin {
src.getArrayItem().connect(dest.getArrayItem());
}
private void achieveSetErr(DependencyChecker checker, MethodDependency method) {
FieldDependency fieldDep = checker.linkField(new FieldReference("java.lang.System", "err"), method.getStack());
private void achieveSetErr(DependencyAgent agent, MethodDependency method) {
FieldDependency fieldDep = agent.linkField(new FieldReference("java.lang.System", "err"), method.getStack());
method.getVariable(1).connect(fieldDep.getValue());
}
private void achieveSetOut(DependencyChecker checker, MethodDependency method) {
FieldDependency fieldDep = checker.linkField(new FieldReference("java.lang.System", "out"), method.getStack());
private void achieveSetOut(DependencyAgent agent, MethodDependency method) {
FieldDependency fieldDep = agent.linkField(new FieldReference("java.lang.System", "out"), method.getStack());
method.getVariable(1).connect(fieldDep.getValue());
}
}

View File

@ -82,17 +82,14 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
return this;
}
ensureCapacity(length + string.length());
if (index < length) {
for (int i = length - 1; i >= index; --i) {
buffer[i + string.length()] = buffer[i];
}
length += string.length();
for (int i = length - 1; i >= index; --i) {
buffer[i + string.length()] = buffer[i];
}
length += string.length();
int j = index;
for (int i = 0; i < string.length(); ++i) {
buffer[j++] = string.charAt(i);
}
length = j;
return this;
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.lang;
/**
*
* @author Alexey Andreev
*/
public class TArithmeticException extends TRuntimeException {
private static final long serialVersionUID = 8084592456171302650L;
public TArithmeticException() {
super();
}
public TArithmeticException(TString message) {
super(message);
}
}

View File

@ -163,4 +163,12 @@ public class TClass<T> extends TObject {
@GeneratedBy(ClassNativeGenerator.class)
@PluggableDependency(ClassNativeGenerator.class)
public native TClass<?> getDeclaringClass();
@SuppressWarnings("unchecked")
public <U> TClass<? extends U> asSubclass(TClass<U> clazz) {
if (!clazz.isAssignableFrom(this)) {
throw new TClassCastException();
}
return (TClass<? extends U>)this;
}
}

View File

@ -261,7 +261,7 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
doubleMantissa = abs * 0x1p1022 * binaryExponent(negExp - 1022);
}
long mantissa = (long)(doubleMantissa + 0.5) & 0xFFFFFFFFFFFFFL;
return mantissa | ((exp + 1023L) << 52) | (value < 0 ? (1L << 63) : 0);
return mantissa | ((exp + 1023L) << 52) | (value < 0 || 1 / value == NEGATIVE_INFINITY ? (1L << 63) : 0);
}
public static double longBitsToDouble(long bits) {

View File

@ -263,7 +263,7 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
doubleMantissa = abs * 0x1p126f * binaryExponent(negExp - 126);
}
int mantissa = (int)(doubleMantissa + 0.5f) & 0x7FFFFF;
return mantissa | ((exp + 127) << 23) | (value < 0 ? (1 << 31) : 0);
return mantissa | ((exp + 127) << 23) | (value < 0 || 1 / value == NEGATIVE_INFINITY ? (1 << 31) : 0);
}
public static float intBitsToFloat(int bits) {

View File

@ -79,6 +79,9 @@ public class TInteger extends TNumber implements TComparable<TInteger> {
break;
}
int value = 0;
if (index == s.length()) {
throw new TNumberFormatException();
}
while (index < s.length()) {
int digit = TCharacter.getNumericValue(s.charAt(index++));
if (digit < 0) {

View File

@ -190,7 +190,7 @@ public class TLong extends TNumber implements TComparable<TLong> {
return toString(i, 2);
}
public TString toString(long value) {
public static TString toString(long value) {
return TString.wrap(new TStringBuilder().append(value).toString());
}

View File

@ -152,11 +152,11 @@ public final class TMath extends TObject {
}
public static double ulp(double d) {
return pow(1, -getExponent(d) - 52);
return pow(2, getExponent(d) - 52);
}
public static float ulp(float d) {
return (float)pow(1, -getExponent(d) - 23);
return (float)pow(2, getExponent(d) - 23);
}
public static double signum(double d) {
@ -213,6 +213,7 @@ public final class TMath extends TObject {
int exp = 0;
double[] exponents = ExponentConstants.exponents;
double[] negativeExponents = ExponentConstants.negativeExponents;
double[] negativeExponents2 = ExponentConstants.negativeExponents2;
if (d > 1) {
int expBit = 1 << (exponents.length - 1);
for (int i = exponents.length - 1; i >= 0; --i) {
@ -225,12 +226,12 @@ public final class TMath extends TObject {
} else if (d < 1) {
int expBit = 1 << (negativeExponents.length - 1);
int offset = 0;
if (d <= 0x1p-1023) {
if (d < 0x1p-1022) {
d *= 0x1p52;
offset = 52;
}
for (int i = negativeExponents.length - 1; i >= 0; --i) {
if (d <= negativeExponents[i]) {
for (int i = negativeExponents2.length - 1; i >= 0; --i) {
if (d < negativeExponents2[i]) {
d *= exponents[i];
exp |= expBit;
}
@ -246,6 +247,7 @@ public final class TMath extends TObject {
int exp = 0;
float[] exponents = FloatExponents.exponents;
float[] negativeExponents = FloatExponents.negativeExponents;
float[] negativeExponents2 = FloatExponents.negativeExponents2;
if (f > 1) {
int expBit = 1 << (exponents.length - 1);
for (int i = exponents.length - 1; i >= 0; --i) {
@ -258,12 +260,12 @@ public final class TMath extends TObject {
} else if (f < 1) {
int expBit = 1 << (negativeExponents.length - 1);
int offset = 0;
if (f <= 0x1p-127) {
if (f < 0x1p-126) {
f *= 0x1p23f;
offset = 23;
}
for (int i = negativeExponents.length - 1; i >= 0; --i) {
if (f <= negativeExponents[i]) {
for (int i = negativeExponents2.length - 1; i >= 0; --i) {
if (f < negativeExponents2[i]) {
f *= exponents[i];
exp |= expBit;
}
@ -281,9 +283,9 @@ public final class TMath extends TObject {
return direction > start ? start + ulp(start) : start - ulp(start);
}
public static float nextAfter(float start, float direction) {
public static float nextAfter(float start, double direction) {
if (start == direction) {
return direction;
return start;
}
return direction > start ? start + ulp(start) : start - ulp(start);
}
@ -301,11 +303,15 @@ public final class TMath extends TObject {
0x1p256, 0x1p512 };
public static double[] negativeExponents = { 0x1p-1, 0x1p-2, 0x1p-4, 0x1p-8, 0x1p-16, 0x1p-32,
0x1p-64, 0x1p-128, 0x1p-256, 0x1p-512 };
public static double[] negativeExponents2 = { 0x1p-0, 0x1p-1, 0x1p-3, 0x1p-7, 0x1p-15, 0x1p-31,
0x1p-63, 0x1p-127, 0x1p-255, 0x1p-511 };
}
private static class FloatExponents {
public static float[] exponents = { 0x1p1f, 0x1p2f, 0x1p4f, 0x1p8f, 0x1p16f, 0x1p32f, 0x1p64f };
public static float[] negativeExponents = { 0x1p-1f, 0x1p-2f, 0x1p-4f, 0x1p-8f, 0x1p-16f, 0x1p-32f,
0x1p-64f };
0x1p-64f };
public static float[] negativeExponents2 = { 0x1p-0f, 0x1p-1f, 0x1p-3f, 0x1p-7f, 0x1p-15f, 0x1p-31f,
0x1p-63f };
}
}

View File

@ -0,0 +1,236 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.lang;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public final class TStrictMath extends TObject {
public static double E = 2.71828182845904523536;
public static double PI = 3.14159265358979323846;
private TStrictMath() {
}
public static double sin(double a) {
return TMath.sin(a);
}
public static double cos(double a) {
return TMath.cos(a);
}
public static double tan(double a) {
return TMath.tan(a);
}
public static double asin(double a) {
return TMath.asin(a);
}
public static double acos(double a) {
return TMath.acos(a);
}
public static double atan(double a) {
return TMath.atan(a);
}
public static double toRadians(double angdeg) {
return TMath.toRadians(angdeg);
}
public static double toDegrees(double angrad) {
return TMath.toDegrees(angrad);
}
public static double exp(double a) {
return TMath.exp(a);
}
public static double log(double a) {
return TMath.log(a);
}
public static double log10(double a) {
return TMath.log10(a);
}
public static double sqrt(double a) {
return TMath.sqrt(a);
}
public static double cbrt(double a) {
return TMath.cbrt(a);
}
public static double IEEEremainder(double f1, double f2) {
return TMath.IEEEremainder(f1, f2);
}
public static double ceil(double a) {
return TMath.ceil(a);
}
public static double floor(double a) {
return TMath.floor(a);
}
public static double rint(double a) {
return TMath.rint(a);
}
public static double atan2(double y, double x) {
return TMath.atan2(y, x);
}
public static double pow(double a, double b) {
return TMath.pow(a, b);
}
public static int round(float a) {
return TMath.round(a);
}
public static long round(double a) {
return TMath.round(a);
}
public static double random() {
return TMath.random();
}
public static int abs(int a) {
return TMath.abs(a);
}
public static long abs(long a) {
return TMath.abs(a);
}
public static float abs(float a) {
return TMath.abs(a);
}
public static double abs(double a) {
return TMath.abs(a);
}
public static int max(int a, int b) {
return TMath.max(a, b);
}
public static long max(long a, long b) {
return TMath.max(a, b);
}
public static float max(float a, float b) {
return TMath.max(a, b);
}
public static double max(double a, double b) {
return TMath.max(a, b);
}
public static int min(int a, int b) {
return TMath.min(a, b);
}
public static long min(long a, long b) {
return TMath.min(a, b);
}
public static float min(float a, float b) {
return TMath.min(a, b);
}
public static double min(double a, double b) {
return TMath.min(a, b);
}
public static double ulp(double d) {
return TMath.ulp(d);
}
public static float ulp(float f) {
return TMath.ulp(f);
}
public static double signum(double d) {
return TMath.signum(d);
}
public static float signum(float f) {
return TMath.signum(f);
}
public static double sinh(double x) {
return TMath.sinh(x);
}
public static double cosh(double x) {
return TMath.cosh(x);
}
public static double tanh(double x) {
return TMath.tanh(x);
}
public static double hypot(double x, double y) {
return TMath.hypot(x, y);
}
public static double expm1(double x) {
return TMath.expm1(x);
}
public static double log1p(double x) {
return TMath.log1p(x);
}
public static double copySign(double magnitude, double sign) {
return TMath.copySign(magnitude, sign);
}
public static float copySign(float magnitude, float sign) {
return TMath.copySign(magnitude, sign);
}
public static int getExponent(float f) {
return TMath.getExponent(f);
}
public static int getExponent(double d) {
return TMath.getExponent(d);
}
public static double nextAfter(double start, double direction) {
return TMath.nextAfter(start, direction);
}
public static float nextAfter(float start, double direction) {
return TMath.nextAfter(start, direction);
}
public static double nextUp(double d) {
return TMath.nextUp(d);
}
public static float nextUp(float f) {
return TMath.nextUp(f);
}
}

View File

@ -400,7 +400,7 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
int sz = length() - target.length();
int i = 0;
outer:
for (; i < sz; ++i) {
for (; i <= sz; ++i) {
for (int j = 0; j < target.length(); ++j) {
if (charAt(i + j) != target.charAt(j)) {
sb.append(charAt(i));
@ -556,13 +556,8 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
@Override
public int hashCode() {
if (hashCode == 0) {
hashCode ^= 734262231;
for (char c : characters) {
hashCode = (hashCode << 4) | (hashCode >>> 28);
hashCode ^= 347236277 ^ c;
if (hashCode == 0) {
++hashCode;
}
hashCode = 31 * hashCode + c;
}
}
return hashCode;

View File

@ -77,4 +77,8 @@ public class TThread extends TObject implements TRunnable {
public long getId() {
return 1;
}
public static boolean holdsLock(@SuppressWarnings("unused") TObject obj) {
return true;
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.lang.ref;
import org.teavm.classlib.java.lang.TObject;
/**
*
* @author Alexey Andreev
*/
public abstract class TReference<T> extends TObject {
public T get() {
return null;
}
public void clear() {
}
public boolean isEnqueued() {
return false;
}
public boolean enqueue() {
return false;
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.lang.ref;
/**
*
* @author Alexey Andreev
*/
public class TWeakReference<T> extends TReference<T> {
private T value;
public TWeakReference(T value) {
this.value = value;
}
@Override
public T get() {
return value;
}
@Override
public void clear() {
value = null;
}
}

View File

@ -17,10 +17,7 @@ package org.teavm.classlib.java.lang.reflect;
import java.io.IOException;
import org.teavm.codegen.SourceWriter;
import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyConsumer;
import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.MethodDependency;
import org.teavm.dependency.*;
import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.model.ClassReader;
@ -41,16 +38,16 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
ValueType.INTEGER, ValueType.LONG, ValueType.FLOAT, ValueType.DOUBLE, ValueType.BOOLEAN };
@Override
public void methodAchieved(DependencyChecker checker, MethodDependency method) {
public void methodAchieved(DependencyAgent agent, MethodDependency method) {
switch (method.getReference().getName()) {
case "getLength":
achieveGetLength(checker, method);
achieveGetLength(agent, method);
break;
case "newInstanceImpl":
method.getResult().propagate("[java.lang.Object");
method.getResult().propagate(agent.getType("[java.lang.Object"));
break;
case "getImpl":
achieveGet(checker, method);
achieveGet(agent, method);
break;
}
}
@ -81,13 +78,12 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
writer.append("return " + array + ".data.length;").softNewLine();
}
private void achieveGetLength(final DependencyChecker checker, final MethodDependency method) {
private void achieveGetLength(final DependencyAgent agent, final MethodDependency method) {
method.getVariable(1).addConsumer(new DependencyConsumer() {
@Override public void consume(String type) {
if (!type.startsWith("[")) {
MethodReference cons = new MethodReference("java.lang.IllegalArgumentException",
new MethodDescriptor("<init>", ValueType.VOID));
checker.addEntryPoint(cons);
@Override public void consume(DependencyAgentType type) {
if (!type.getName().startsWith("[")) {
MethodReference cons = new MethodReference(IllegalArgumentException.class, "<init>", void.class);
agent.linkMethod(cons, method.getStack()).use();
}
}
});
@ -129,19 +125,19 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
writer.outdent().append("}").softNewLine();
}
private void achieveGet(final DependencyChecker checker, final MethodDependency method) {
private void achieveGet(final DependencyAgent agent, final MethodDependency method) {
method.getVariable(1).getArrayItem().connect(method.getResult());
method.getVariable(1).addConsumer(new DependencyConsumer() {
@Override public void consume(String type) {
if (type.startsWith("[")) {
type = type.substring(1);
@Override public void consume(DependencyAgentType type) {
if (type.getName().startsWith("[")) {
String typeName = type.getName().substring(1);
for (int i = 0; i < primitiveTypes.length; ++i) {
if (primitiveTypes[i].toString().equals(type)) {
if (primitiveTypes[i].toString().equals(typeName)) {
String wrapper = "java.lang." + primitiveWrappers[i];
MethodReference methodRef = new MethodReference(wrapper, "valueOf",
primitiveTypes[i], ValueType.object(wrapper));
checker.linkMethod(methodRef, method.getStack()).use();
method.getResult().propagate("java.lang." + primitiveWrappers[i]);
agent.linkMethod(methodRef, method.getStack()).use();
method.getResult().propagate(agent.getType("java.lang." + primitiveWrappers[i]));
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,358 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.math;
/**
* Static library that provides all the <b>bit level</b> operations for
* {@link TBigInteger}. The operations are:
* <ul type="circle">
* <li>Left Shifting</li>
* <li>Right Shifting</li>
* <li>Bit clearing</li>
* <li>Bit setting</li>
* <li>Bit counting</li>
* <li>Bit testing</li>
* <li>Getting of the lowest bit set</li>
* </ul>
* All operations are provided in immutable way, and some in both mutable and
* immutable.
*/
class TBitLevel {
/** Just to denote that this class can't be instantiated. */
private TBitLevel() {
}
/** @see TBigInteger#bitLength() */
static int bitLength(TBigInteger val) {
if (val.sign == 0) {
return 0;
}
int bLength = (val.numberLength << 5);
int highDigit = val.digits[val.numberLength - 1];
if (val.sign < 0) {
int i = val.getFirstNonzeroDigit();
// We reduce the problem to the positive case.
if (i == val.numberLength - 1) {
highDigit--;
}
}
// Subtracting all sign bits
bLength -= Integer.numberOfLeadingZeros(highDigit);
return bLength;
}
/** @see TBigInteger#bitCount() */
static int bitCount(TBigInteger val) {
int bCount = 0;
if (val.sign == 0) {
return 0;
}
int i = val.getFirstNonzeroDigit();
if (val.sign > 0) {
for (; i < val.numberLength; i++) {
bCount += Integer.bitCount(val.digits[i]);
}
} else {// (sign < 0)
// this digit absorbs the carry
bCount += Integer.bitCount(-val.digits[i]);
for (i++; i < val.numberLength; i++) {
bCount += Integer.bitCount(~val.digits[i]);
}
// We take the complement sum:
bCount = (val.numberLength << 5) - bCount;
}
return bCount;
}
/**
* Performs a fast bit testing for positive numbers. The bit to to be tested
* must be in the range {@code [0, val.bitLength()-1]}
*/
static boolean testBit(TBigInteger val, int n) {
// PRE: 0 <= n < val.bitLength()
return ((val.digits[n >> 5] & (1 << (n & 31))) != 0);
}
/**
* Check if there are 1s in the lowest bits of this BigInteger
*
* @param numberOfBits
* the number of the lowest bits to check
* @return false if all bits are 0s, true otherwise
*/
static boolean nonZeroDroppedBits(int numberOfBits, int digits[]) {
int intCount = numberOfBits >> 5;
int bitCount = numberOfBits & 31;
int i;
for (i = 0; (i < intCount) && (digits[i] == 0); i++) {
// do nothing
}
return ((i != intCount) || (digits[i] << (32 - bitCount) != 0));
}
/** @see TBigInteger#shiftLeft(int) */
static TBigInteger shiftLeft(TBigInteger source, int count) {
int intCount = count >> 5;
count &= 31; // %= 32
int resLength = source.numberLength + intCount + ((count == 0) ? 0 : 1);
int resDigits[] = new int[resLength];
shiftLeft(resDigits, source.digits, intCount, count);
TBigInteger result = new TBigInteger(source.sign, resLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
/**
* Performs {@code val <<= count}.
*/
// val should have enough place (and one digit more)
static void inplaceShiftLeft(TBigInteger val, int count) {
int intCount = count >> 5; // count of integers
val.numberLength += intCount +
(Integer.numberOfLeadingZeros(val.digits[val.numberLength - 1]) - (count & 31) >= 0 ? 0 : 1);
shiftLeft(val.digits, val.digits, intCount, count & 31);
val.cutOffLeadingZeroes();
val.unCache();
}
/**
* Abstractly shifts left an array of integers in little endian (i.e. shift
* it right). Total shift distance in bits is intCount * 32 + count
*
* @param result
* the destination array
* @param source
* the source array
* @param intCount
* the shift distance in integers
* @param count
* an additional shift distance in bits
*/
static void shiftLeft(int result[], int source[], int intCount, int count) {
if (count == 0) {
System.arraycopy(source, 0, result, intCount, result.length - intCount);
} else {
int rightShiftCount = 32 - count;
result[result.length - 1] = 0;
for (int i = result.length - 1; i > intCount; i--) {
result[i] |= source[i - intCount - 1] >>> rightShiftCount;
result[i - 1] = source[i - intCount - 1] << count;
}
}
for (int i = 0; i < intCount; i++) {
result[i] = 0;
}
}
/**
* Shifts the source digits left one bit, creating a value whose magnitude
* is doubled.
*
* @param result
* an array of digits that will hold the computed result when
* this method returns. The size of this array is
* {@code srcLen + 1}, and the format is the same as
* {@link TBigInteger#digits}.
* @param source
* the array of digits to shift left, in the same format as
* {@link TBigInteger#digits}.
* @param srcLen
* the length of {@code source}; may be less than
* {@code source.length}
*/
static void shiftLeftOneBit(int result[], int source[], int srcLen) {
int carry = 0;
for (int i = 0; i < srcLen; i++) {
int val = source[i];
result[i] = (val << 1) | carry;
carry = val >>> 31;
}
if (carry != 0) {
result[srcLen] = carry;
}
}
static TBigInteger shiftLeftOneBit(TBigInteger source) {
int srcLen = source.numberLength;
int resLen = srcLen + 1;
int resDigits[] = new int[resLen];
shiftLeftOneBit(resDigits, source.digits, srcLen);
TBigInteger result = new TBigInteger(source.sign, resLen, resDigits);
result.cutOffLeadingZeroes();
return result;
}
/** @see TBigInteger#shiftRight(int) */
static TBigInteger shiftRight(TBigInteger source, int count) {
int intCount = count >> 5; // count of integers
count &= 31; // count of remaining bits
if (intCount >= source.numberLength) {
return ((source.sign < 0) ? TBigInteger.MINUS_ONE : TBigInteger.ZERO);
}
int i;
int resLength = source.numberLength - intCount;
int resDigits[] = new int[resLength + 1];
shiftRight(resDigits, resLength, source.digits, intCount, count);
if (source.sign < 0) {
// Checking if the dropped bits are zeros (the remainder equals to
// 0)
for (i = 0; (i < intCount) && (source.digits[i] == 0); i++) {
// do nothing
}
// If the remainder is not zero, add 1 to the result
if ((i < intCount) || ((count > 0) && ((source.digits[i] << (32 - count)) != 0))) {
for (i = 0; (i < resLength) && (resDigits[i] == -1); i++) {
resDigits[i] = 0;
}
if (i == resLength) {
resLength++;
}
resDigits[i]++;
}
}
TBigInteger result = new TBigInteger(source.sign, resLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
/**
* Performs {@code val >>= count} where {@code val} is a positive number.
*/
static void inplaceShiftRight(TBigInteger val, int count) {
int sign = val.signum();
if (count == 0 || val.signum() == 0)
return;
int intCount = count >> 5; // count of integers
val.numberLength -= intCount;
if (!shiftRight(val.digits, val.numberLength, val.digits, intCount, count & 31) && sign < 0) {
// remainder not zero: add one to the result
int i;
for (i = 0; (i < val.numberLength) && (val.digits[i] == -1); i++) {
val.digits[i] = 0;
}
if (i == val.numberLength) {
val.numberLength++;
}
val.digits[i]++;
}
val.cutOffLeadingZeroes();
val.unCache();
}
/**
* Shifts right an array of integers. Total shift distance in bits is
* intCount * 32 + count.
*
* @param result
* the destination array
* @param resultLen
* the destination array's length
* @param source
* the source array
* @param intCount
* the number of elements to be shifted
* @param count
* the number of bits to be shifted
* @return dropped bit's are all zero (i.e. remaider is zero)
*/
static boolean shiftRight(int result[], int resultLen, int source[], int intCount, int count) {
int i;
boolean allZero = true;
for (i = 0; i < intCount; i++) {
allZero &= source[i] == 0;
}
if (count == 0) {
System.arraycopy(source, intCount, result, 0, resultLen);
i = resultLen;
} else {
int leftShiftCount = 32 - count;
allZero &= (source[i] << leftShiftCount) == 0;
for (i = 0; i < resultLen - 1; i++) {
result[i] = (source[i + intCount] >>> count) | (source[i + intCount + 1] << leftShiftCount);
}
result[i] = (source[i + intCount] >>> count);
i++;
}
return allZero;
}
/**
* Performs a flipBit on the BigInteger, returning a BigInteger with the the
* specified bit flipped.
*
* @param intCount
* : the index of the element of the digits array where the
* operation will be performed
* @param bitNumber
* : the bit's position in the intCount element
*/
static TBigInteger flipBit(TBigInteger val, int n) {
int resSign = (val.sign == 0) ? 1 : val.sign;
int intCount = n >> 5;
int bitN = n & 31;
int resLength = Math.max(intCount + 1, val.numberLength) + 1;
int resDigits[] = new int[resLength];
int i;
int bitNumber = 1 << bitN;
System.arraycopy(val.digits, 0, resDigits, 0, val.numberLength);
if (val.sign < 0) {
if (intCount >= val.numberLength) {
resDigits[intCount] = bitNumber;
} else {
// val.sign<0 y intCount < val.numberLength
int firstNonZeroDigit = val.getFirstNonzeroDigit();
if (intCount > firstNonZeroDigit) {
resDigits[intCount] ^= bitNumber;
} else if (intCount < firstNonZeroDigit) {
resDigits[intCount] = -bitNumber;
for (i = intCount + 1; i < firstNonZeroDigit; i++) {
resDigits[i] = -1;
}
resDigits[i] = resDigits[i]--;
} else {
i = intCount;
resDigits[i] = -((-resDigits[intCount]) ^ bitNumber);
if (resDigits[i] == 0) {
for (i++; resDigits[i] == -1; i++) {
resDigits[i] = 0;
}
resDigits[i]++;
}
}
}
} else {// case where val is positive
resDigits[intCount] ^= bitNumber;
}
TBigInteger result = new TBigInteger(resSign, resLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
}

View File

@ -0,0 +1,440 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.math;
/**
* Static library that provides {@link TBigInteger} base conversion from/to any
* integer represented in an {@link java.lang.String} Object.
*/
class TConversion {
/** Just to denote that this class can't be instantiated */
private TConversion() {}
/**
* Holds the maximal exponent for each radix, so that radix<sup>digitFitInInt[radix]</sup>
* fit in an {@code int} (32 bits).
*/
static final int[] digitFitInInt = { -1, -1, 31, 19, 15, 13, 11, 11, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7,
7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5 };
/**
* bigRadices values are precomputed maximal powers of radices (integer
* numbers from 2 to 36) that fit into unsigned int (32 bits). bigRadices[0] =
* 2 ^ 31, bigRadices[8] = 10 ^ 9, etc.
*/
static final int bigRadices[] = { -2147483648, 1162261467, 1073741824, 1220703125, 362797056, 1977326743,
1073741824, 387420489, 1000000000, 214358881, 429981696, 815730721, 1475789056, 170859375, 268435456,
410338673, 612220032, 893871739, 1280000000, 1801088541, 113379904, 148035889, 191102976, 244140625,
308915776, 387420489, 481890304, 594823321, 729000000, 887503681, 1073741824, 1291467969, 1544804416,
1838265625, 60466176 };
/** @see TBigInteger#toString(int) */
static String bigInteger2String(TBigInteger val, int radix) {
int sign = val.sign;
int numberLength = val.numberLength;
int digits[] = val.digits;
if (sign == 0) {
return "0";
}
if (numberLength == 1) {
int highDigit = digits[numberLength - 1];
long v = highDigit & 0xFFFFFFFFL;
if (sign < 0) {
v = -v;
}
return Long.toString(v, radix);
}
if (radix == 10 || radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
return val.toString();
}
double bitsForRadixDigit;
bitsForRadixDigit = Math.log(radix) / Math.log(2);
int resLengthInChars = (int) (val.abs().bitLength() / bitsForRadixDigit + ((sign < 0) ? 1 : 0)) + 1;
char result[] = new char[resLengthInChars];
int currentChar = resLengthInChars;
int resDigit;
if (radix != 16) {
int temp[] = new int[numberLength];
System.arraycopy(digits, 0, temp, 0, numberLength);
int tempLen = numberLength;
int charsPerInt = digitFitInInt[radix];
int i;
// get the maximal power of radix that fits in int
int bigRadix = bigRadices[radix - 2];
while (true) {
// divide the array of digits by bigRadix and convert remainders
// to characters collecting them in the char array
resDigit = TDivision.divideArrayByInt(temp, temp, tempLen, bigRadix);
int previous = currentChar;
do {
result[--currentChar] = Character.forDigit(resDigit % radix, radix);
} while (((resDigit /= radix) != 0) && (currentChar != 0));
int delta = charsPerInt - previous + currentChar;
for (i = 0; i < delta && currentChar > 0; i++) {
result[--currentChar] = '0';
}
for (i = tempLen - 1; (i > 0) && (temp[i] == 0); i--) {
// do nothing
}
tempLen = i + 1;
if ((tempLen == 1) && (temp[0] == 0)) { // the quotient is 0
break;
}
}
} else {
// radix == 16
for (int i = 0; i < numberLength; i++) {
for (int j = 0; (j < 8) && (currentChar > 0); j++) {
resDigit = digits[i] >> (j << 2) & 0xf;
result[--currentChar] = Character.forDigit(resDigit, 16);
}
}
}
while (result[currentChar] == '0') {
currentChar++;
}
if (sign == -1) {
result[--currentChar] = '-';
}
return new String(result, currentChar, resLengthInChars - currentChar);
}
/**
* Builds the correspondent {@code String} representation of {@code val}
* being scaled by {@code scale}.
*
* @see TBigInteger#toString()
* @see TBigDecimal#toString()
*/
static String toDecimalScaledString(TBigInteger val, int scale) {
int sign = val.sign;
int numberLength = val.numberLength;
int digits[] = val.digits;
int resLengthInChars;
int currentChar;
char result[];
if (sign == 0) {
switch (scale) {
case 0:
return "0";
case 1:
return "0.0";
case 2:
return "0.00";
case 3:
return "0.000";
case 4:
return "0.0000";
case 5:
return "0.00000";
case 6:
return "0.000000";
default:
StringBuilder result1 = new StringBuilder();
if (scale < 0) {
result1.append("0E+");
} else {
result1.append("0E");
}
result1.append(-scale);
return result1.toString();
}
}
// one 32-bit unsigned value may contains 10 decimal digits
resLengthInChars = numberLength * 10 + 1 + 7;
// Explanation why +1+7:
// +1 - one char for sign if needed.
// +7 - For "special case 2" (see below) we have 7 free chars for
// inserting necessary scaled digits.
result = new char[resLengthInChars + 1];
// allocated [resLengthInChars+1] characters.
// a free latest character may be used for "special case 1" (see
// below)
currentChar = resLengthInChars;
if (numberLength == 1) {
int highDigit = digits[0];
if (highDigit < 0) {
long v = highDigit & 0xFFFFFFFFL;
do {
long prev = v;
v /= 10;
result[--currentChar] = (char) (0x0030 + ((int) (prev - v * 10)));
} while (v != 0);
} else {
int v = highDigit;
do {
int prev = v;
v /= 10;
result[--currentChar] = (char) (0x0030 + (prev - v * 10));
} while (v != 0);
}
} else {
int temp[] = new int[numberLength];
int tempLen = numberLength;
System.arraycopy(digits, 0, temp, 0, tempLen);
BIG_LOOP: while (true) {
// divide the array of digits by bigRadix and convert
// remainders
// to characters collecting them in the char array
long result11 = 0;
for (int i1 = tempLen - 1; i1 >= 0; i1--) {
long temp1 = (result11 << 32) + (temp[i1] & 0xFFFFFFFFL);
long res = divideLongByBillion(temp1);
temp[i1] = (int) res;
result11 = (int) (res >> 32);
}
int resDigit = (int) result11;
int previous = currentChar;
do {
result[--currentChar] = (char) (0x0030 + (resDigit % 10));
} while ((resDigit /= 10) != 0 && currentChar != 0);
int delta = 9 - previous + currentChar;
for (int i = 0; (i < delta) && (currentChar > 0); i++) {
result[--currentChar] = '0';
}
int j = tempLen - 1;
for (; temp[j] == 0; j--) {
if (j == 0) { // means temp[0] == 0
break BIG_LOOP;
}
}
tempLen = j + 1;
}
while (result[currentChar] == '0') {
currentChar++;
}
}
boolean negNumber = (sign < 0);
int exponent = resLengthInChars - currentChar - scale - 1;
if (scale == 0) {
if (negNumber) {
result[--currentChar] = '-';
}
return new String(result, currentChar, resLengthInChars - currentChar);
}
if ((scale > 0) && (exponent >= -6)) {
if (exponent >= 0) {
// special case 1
int insertPoint = currentChar + exponent;
for (int j = resLengthInChars - 1; j >= insertPoint; j--) {
result[j + 1] = result[j];
}
result[++insertPoint] = '.';
if (negNumber) {
result[--currentChar] = '-';
}
return new String(result, currentChar, resLengthInChars - currentChar + 1);
}
// special case 2
for (int j = 2; j < -exponent + 1; j++) {
result[--currentChar] = '0';
}
result[--currentChar] = '.';
result[--currentChar] = '0';
if (negNumber) {
result[--currentChar] = '-';
}
return new String(result, currentChar, resLengthInChars - currentChar);
}
int startPoint = currentChar + 1;
int endPoint = resLengthInChars;
StringBuilder result1 = new StringBuilder(16 + endPoint - startPoint);
if (negNumber) {
result1.append('-');
}
if (endPoint - startPoint >= 1) {
result1.append(result[currentChar]);
result1.append('.');
result1.append(result, currentChar + 1, resLengthInChars - currentChar - 1);
} else {
result1.append(result, currentChar, resLengthInChars - currentChar);
}
result1.append('E');
if (exponent > 0) {
result1.append('+');
}
result1.append(Integer.toString(exponent));
return result1.toString();
}
/* can process only 32-bit numbers */
static String toDecimalScaledString(long value, int scale) {
int resLengthInChars;
int currentChar;
char result[];
boolean negNumber = value < 0;
if(negNumber) {
value = -value;
}
if (value == 0) {
switch (scale) {
case 0: return "0";
case 1: return "0.0";
case 2: return "0.00";
case 3: return "0.000";
case 4: return "0.0000";
case 5: return "0.00000";
case 6: return "0.000000";
default:
StringBuilder result1 = new StringBuilder();
if (scale < 0) {
result1.append("0E+");
} else {
result1.append("0E");
}
result1.append((scale == Integer.MIN_VALUE) ? "2147483648" : Integer.toString(-scale));
return result1.toString();
}
}
// one 32-bit unsigned value may contains 10 decimal digits
resLengthInChars = 18;
// Explanation why +1+7:
// +1 - one char for sign if needed.
// +7 - For "special case 2" (see below) we have 7 free chars for
// inserting necessary scaled digits.
result = new char[resLengthInChars+1];
// Allocated [resLengthInChars+1] characters.
// a free latest character may be used for "special case 1" (see below)
currentChar = resLengthInChars;
long v = value;
do {
long prev = v;
v /= 10;
result[--currentChar] = (char) (0x0030 + (prev - v * 10));
} while (v != 0);
long exponent = (long)resLengthInChars - (long)currentChar - scale - 1L;
if (scale == 0) {
if (negNumber) {
result[--currentChar] = '-';
}
return new String(result, currentChar, resLengthInChars - currentChar);
}
if (scale > 0 && exponent >= -6) {
if (exponent >= 0) {
// special case 1
int insertPoint = currentChar + (int)exponent;
for(int j = resLengthInChars - 1; j >= insertPoint; j--) {
result[j + 1] = result[j];
}
result[++insertPoint] = '.';
if (negNumber) {
result[--currentChar] = '-';
}
return new String(result, currentChar, resLengthInChars - currentChar + 1);
}
// special case 2
for (int j = 2; j < -exponent + 1; j++) {
result[--currentChar] = '0';
}
result[--currentChar] = '.';
result[--currentChar] = '0';
if (negNumber) {
result[--currentChar] = '-';
}
return new String(result, currentChar, resLengthInChars - currentChar);
}
int startPoint = currentChar + 1;
int endPoint = resLengthInChars;
StringBuilder result1 = new StringBuilder(16 + endPoint - startPoint);
if (negNumber) {
result1.append('-');
}
if (endPoint - startPoint >= 1) {
result1.append(result[currentChar]);
result1.append('.');
result1.append(result, currentChar+1, resLengthInChars - currentChar-1);
} else {
result1.append(result, currentChar, resLengthInChars - currentChar);
}
result1.append('E');
if (exponent > 0) {
result1.append('+');
}
result1.append(Long.toString(exponent));
return result1.toString();
}
static long divideLongByBillion(long a) {
long quot;
long rem;
if (a >= 0) {
long bLong = 1000000000L;
quot = (a / bLong);
rem = (a % bLong);
} else {
/*
* Make the dividend positive shifting it right by 1 bit then get
* the quotient an remainder and correct them properly
*/
long aPos = a >>> 1;
long bPos = 1000000000L >>> 1;
quot = aPos / bPos;
rem = aPos % bPos;
// double the remainder and add 1 if 'a' is odd
rem = (rem << 1) + (a & 1);
}
return (rem << 32) | (quot & 0xFFFFFFFFL);
}
/** @see TBigInteger#doubleValue() */
static double bigInteger2Double(TBigInteger val) {
// val.bitLength() < 64
if ((val.numberLength < 2) || ((val.numberLength == 2) && (val.digits[1] > 0))) {
return val.longValue();
}
// val.bitLength() >= 33 * 32 > 1024
if (val.numberLength > 32) {
return val.sign > 0 ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
}
int bitLen = val.abs().bitLength();
long exponent = bitLen - 1;
int delta = bitLen - 54;
// We need 54 top bits from this, the 53th bit is always 1 in lVal.
long lVal = val.abs().shiftRight(delta).longValue();
/*
* Take 53 bits from lVal to mantissa. The least significant bit is
* needed for rounding.
*/
long mantissa = lVal & 0x1FFFFFFFFFFFFFL;
if (exponent == 1023) {
if (mantissa == 0X1FFFFFFFFFFFFFL) {
return val.sign > 0 ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
}
if (mantissa == 0x1FFFFFFFFFFFFEL) {
return val.sign > 0 ? Double.MAX_VALUE : -Double.MAX_VALUE;
}
}
// Round the mantissa
if ((mantissa & 1) == 1 && (mantissa & 2) == 2 || TBitLevel.nonZeroDroppedBits(delta, val.digits)) {
mantissa += 2;
}
mantissa >>= 1; // drop the rounding bit
long resSign = (val.sign < 0) ? 0x8000000000000000L : 0;
exponent = ((1023 + exponent) << 52) & 0x7FF0000000000000L;
long result = resSign | exponent | mantissa;
return Double.longBitsToDouble(result);
}
}

View File

@ -0,0 +1,952 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.math;
/**
* Static library that provides all operations related with division and modular
* arithmetic to {@link TBigInteger}. Some methods are provided in both mutable
* and immutable way. There are several variants provided listed below:
*
* <ul type="circle">
* <li><b>Division</b>
* <ul type="circle">
* <li>{@link TBigInteger} division and remainder by {@link TBigInteger}.</li>
* <li>{@link TBigInteger} division and remainder by {@code int}.</li>
* <li><i>gcd</i> between {@link TBigInteger} numbers.</li>
* </ul>
* </li>
* <li><b>Modular arithmetic </b>
* <ul type="circle">
* <li>Modular exponentiation between {@link TBigInteger} numbers.</li>
* <li>Modular inverse of a {@link TBigInteger} numbers.</li>
* </ul>
* </li>
* </ul>
*/
class TDivision {
/**
* Divides the array 'a' by the array 'b' and gets the quotient and the
* remainder. Implements the Knuth's division algorithm. See D. Knuth, The
* Art of Computer Programming, vol. 2. Steps D1-D8 correspond the steps in
* the algorithm description.
*
* @param quot
* the quotient
* @param quotLength
* the quotient's length
* @param a
* the dividend
* @param aLength
* the dividend's length
* @param b
* the divisor
* @param bLength
* the divisor's length
* @return the remainder
*/
static int[] divide(int quot[], int quotLength, int a[], int aLength, int b[], int bLength) {
int normA[] = new int[aLength + 1]; // the normalized dividend
// an extra byte is needed for correct shift
int normB[] = new int[bLength + 1]; // the normalized divisor;
int normBLength = bLength;
/*
* Step D1: normalize a and b and put the results to a1 and b1 the
* normalized divisor's first digit must be >= 2^31
*/
int divisorShift = Integer.numberOfLeadingZeros(b[bLength - 1]);
if (divisorShift != 0) {
TBitLevel.shiftLeft(normB, b, 0, divisorShift);
TBitLevel.shiftLeft(normA, a, 0, divisorShift);
} else {
System.arraycopy(a, 0, normA, 0, aLength);
System.arraycopy(b, 0, normB, 0, bLength);
}
int firstDivisorDigit = normB[normBLength - 1];
// Step D2: set the quotient index
int i = quotLength - 1;
int j = aLength;
while (i >= 0) {
// Step D3: calculate a guess digit guessDigit
int guessDigit = 0;
if (normA[j] == firstDivisorDigit) {
// set guessDigit to the largest unsigned int value
guessDigit = -1;
} else {
long product = (((normA[j] & 0xffffffffL) << 32) + (normA[j - 1] & 0xffffffffL));
long res = TDivision.divideLongByInt(product, firstDivisorDigit);
guessDigit = (int) res; // the quotient of divideLongByInt
int rem = (int) (res >> 32); // the remainder of
// divideLongByInt
// decrease guessDigit by 1 while leftHand > rightHand
if (guessDigit != 0) {
long leftHand = 0;
long rightHand = 0;
boolean rOverflowed = false;
guessDigit++; // to have the proper value in the loop
// below
do {
guessDigit--;
if (rOverflowed) {
break;
}
// leftHand always fits in an unsigned long
leftHand = (guessDigit & 0xffffffffL) * (normB[normBLength - 2] & 0xffffffffL);
/*
* rightHand can overflow; in this case the loop
* condition will be true in the next step of the loop
*/
rightHand = ((long) rem << 32) + (normA[j - 2] & 0xffffffffL);
long longR = (rem & 0xffffffffL) + (firstDivisorDigit & 0xffffffffL);
/*
* checks that longR does not fit in an unsigned int;
* this ensures that rightHand will overflow unsigned
* long in the next step
*/
if (Integer.numberOfLeadingZeros((int) (longR >>> 32)) < 32) {
rOverflowed = true;
} else {
rem = (int) longR;
}
} while (((leftHand ^ 0x8000000000000000L) > (rightHand ^ 0x8000000000000000L)));
}
}
// Step D4: multiply normB by guessDigit and subtract the production
// from normA.
if (guessDigit != 0) {
int borrow = TDivision.multiplyAndSubtract(normA, j - normBLength, normB, normBLength, guessDigit);
// Step D5: check the borrow
if (borrow != 0) {
// Step D6: compensating addition
guessDigit--;
long carry = 0;
for (int k = 0; k < normBLength; k++) {
carry += (normA[j - normBLength + k] & 0xffffffffL) + (normB[k] & 0xffffffffL);
normA[j - normBLength + k] = (int) carry;
carry >>>= 32;
}
}
}
if (quot != null) {
quot[i] = guessDigit;
}
// Step D7
j--;
i--;
}
/*
* Step D8: we got the remainder in normA. Denormalize it id needed
*/
if (divisorShift != 0) {
// reuse normB
TBitLevel.shiftRight(normB, normBLength, normA, 0, divisorShift);
return normB;
}
System.arraycopy(normA, 0, normB, 0, bLength);
return normA;
}
/**
* Divides an array by an integer value. Implements the Knuth's division
* algorithm. See D. Knuth, The Art of Computer Programming, vol. 2.
*
* @param dest
* the quotient
* @param src
* the dividend
* @param srcLength
* the length of the dividend
* @param divisor
* the divisor
* @return remainder
*/
static int divideArrayByInt(int dest[], int src[], final int srcLength, final int divisor) {
long rem = 0;
long bLong = divisor & 0xffffffffL;
for (int i = srcLength - 1; i >= 0; i--) {
long temp = (rem << 32) | (src[i] & 0xffffffffL);
long quot;
if (temp >= 0) {
quot = (temp / bLong);
rem = (temp % bLong);
} else {
/*
* make the dividend positive shifting it right by 1 bit then
* get the quotient an remainder and correct them properly
*/
long aPos = temp >>> 1;
long bPos = divisor >>> 1;
quot = aPos / bPos;
rem = aPos % bPos;
// double the remainder and add 1 if a is odd
rem = (rem << 1) + (temp & 1);
if ((divisor & 1) != 0) {
// the divisor is odd
if (quot <= rem) {
rem -= quot;
} else {
if (quot - rem <= bLong) {
rem += bLong - quot;
quot -= 1;
} else {
rem += (bLong << 1) - quot;
quot -= 2;
}
}
}
}
dest[i] = (int) (quot & 0xffffffffL);
}
return (int) rem;
}
/**
* Divides an array by an integer value. Implements the Knuth's division
* algorithm. See D. Knuth, The Art of Computer Programming, vol. 2.
*
* @param src
* the dividend
* @param srcLength
* the length of the dividend
* @param divisor
* the divisor
* @return remainder
*/
static int remainderArrayByInt(int src[], final int srcLength, final int divisor) {
long result = 0;
for (int i = srcLength - 1; i >= 0; i--) {
long temp = (result << 32) + (src[i] & 0xffffffffL);
long res = divideLongByInt(temp, divisor);
result = (int) (res >> 32);
}
return (int) result;
}
/**
* Divides a <code>BigInteger</code> by a signed <code>int</code> and
* returns the remainder.
*
* @param dividend
* the BigInteger to be divided. Must be non-negative.
* @param divisor
* a signed int
* @return divide % divisor
*/
static int remainder(TBigInteger dividend, int divisor) {
return remainderArrayByInt(dividend.digits, dividend.numberLength, divisor);
}
/**
* Divides an unsigned long a by an unsigned int b. It is supposed that the
* most significant bit of b is set to 1, i.e. b < 0
*
* @param a
* the dividend
* @param b
* the divisor
* @return the long value containing the unsigned integer remainder in the
* left half and the unsigned integer quotient in the right half
*/
static long divideLongByInt(long a, int b) {
long quot;
long rem;
long bLong = b & 0xffffffffL;
if (a >= 0) {
quot = (a / bLong);
rem = (a % bLong);
} else {
/*
* Make the dividend positive shifting it right by 1 bit then get
* the quotient an remainder and correct them properly
*/
long aPos = a >>> 1;
long bPos = b >>> 1;
quot = aPos / bPos;
rem = aPos % bPos;
// double the remainder and add 1 if a is odd
rem = (rem << 1) + (a & 1);
if ((b & 1) != 0) { // the divisor is odd
if (quot <= rem) {
rem -= quot;
} else {
if (quot - rem <= bLong) {
rem += bLong - quot;
quot -= 1;
} else {
rem += (bLong << 1) - quot;
quot -= 2;
}
}
}
}
return (rem << 32) | (quot & 0xffffffffL);
}
/**
* Computes the quotient and the remainder after a division by an
* {@code int} number.
*
* @return an array of the form {@code [quotient, remainder]}.
*/
static TBigInteger[] divideAndRemainderByInteger(TBigInteger val, int divisor, int divisorSign) {
// res[0] is a quotient and res[1] is a remainder:
int[] valDigits = val.digits;
int valLen = val.numberLength;
int valSign = val.sign;
if (valLen == 1) {
long a = (valDigits[0] & 0xffffffffL);
long b = (divisor & 0xffffffffL);
long quo = a / b;
long rem = a % b;
if (valSign != divisorSign) {
quo = -quo;
}
if (valSign < 0) {
rem = -rem;
}
return new TBigInteger[] { TBigInteger.valueOf(quo), TBigInteger.valueOf(rem) };
}
int quotientLength = valLen;
int quotientSign = ((valSign == divisorSign) ? 1 : -1);
int quotientDigits[] = new int[quotientLength];
int remainderDigits[];
remainderDigits = new int[] { TDivision.divideArrayByInt(quotientDigits, valDigits, valLen, divisor) };
TBigInteger result0 = new TBigInteger(quotientSign, quotientLength, quotientDigits);
TBigInteger result1 = new TBigInteger(valSign, 1, remainderDigits);
result0.cutOffLeadingZeroes();
result1.cutOffLeadingZeroes();
return new TBigInteger[] { result0, result1 };
}
/**
* Multiplies an array by int and subtracts it from a subarray of another
* array.
*
* @param a
* the array to subtract from
* @param start
* the start element of the subarray of a
* @param b
* the array to be multiplied and subtracted
* @param bLen
* the length of b
* @param c
* the multiplier of b
* @return the carry element of subtraction
*/
static int multiplyAndSubtract(int a[], int start, int b[], int bLen, int c) {
long carry0 = 0;
long carry1 = 0;
for (int i = 0; i < bLen; i++) {
carry0 = TMultiplication.unsignedMultAddAdd(b[i], c, (int) carry0, 0);
carry1 = (a[start + i] & 0xffffffffL) - (carry0 & 0xffffffffL) + carry1;
a[start + i] = (int) carry1;
carry1 >>= 32; // -1 or 0
carry0 >>>= 32;
}
carry1 = (a[start + bLen] & 0xffffffffL) - carry0 + carry1;
a[start + bLen] = (int) carry1;
return (int) (carry1 >> 32); // -1 or 0
}
/**
* @param m
* a positive modulus Return the greatest common divisor of op1
* and op2,
*
* @param op1
* must be greater than zero
* @param op2
* must be greater than zero
* @see TBigInteger#gcd(TBigInteger)
* @return {@code GCD(op1, op2)}
*/
static TBigInteger gcdBinary(TBigInteger op1, TBigInteger op2) {
// PRE: (op1 > 0) and (op2 > 0)
/*
* Divide both number the maximal possible times by 2 without rounding
* gcd(2*a, 2*b) = 2 * gcd(a,b)
*/
int lsb1 = op1.getLowestSetBit();
int lsb2 = op2.getLowestSetBit();
int pow2Count = Math.min(lsb1, lsb2);
TBitLevel.inplaceShiftRight(op1, lsb1);
TBitLevel.inplaceShiftRight(op2, lsb2);
TBigInteger swap;
// I want op2 > op1
if (op1.compareTo(op2) == TBigInteger.GREATER) {
swap = op1;
op1 = op2;
op2 = swap;
}
do { // INV: op2 >= op1 && both are odd unless op1 = 0
// Optimization for small operands
// (op2.bitLength() < 64) implies by INV (op1.bitLength() < 64)
if ((op2.numberLength == 1) || ((op2.numberLength == 2) && (op2.digits[1] > 0))) {
op2 = TBigInteger.valueOf(TDivision.gcdBinary(op1.longValue(), op2.longValue()));
break;
}
// Implements one step of the Euclidean algorithm
// To reduce one operand if it's much smaller than the other one
if (op2.numberLength > op1.numberLength * 1.2) {
op2 = op2.remainder(op1);
if (op2.signum() != 0) {
TBitLevel.inplaceShiftRight(op2, op2.getLowestSetBit());
}
} else {
// Use Knuth's algorithm of successive subtract and shifting
do {
TElementary.inplaceSubtract(op2, op1); // both are odd
TBitLevel.inplaceShiftRight(op2, op2.getLowestSetBit());
} while (op2.compareTo(op1) >= TBigInteger.EQUALS);
}
// now op1 >= op2
swap = op2;
op2 = op1;
op1 = swap;
} while (op1.sign != 0);
return op2.shiftLeft(pow2Count);
}
/**
* Performs the same as {@link #gcdBinary(TBigInteger, TBigInteger)}, but
* with numbers of 63 bits, represented in positives values of {@code long}
* type.
*
* @param op1
* a positive number
* @param op2
* a positive number
* @see #gcdBinary(TBigInteger, TBigInteger)
* @return <code>GCD(op1, op2)</code>
*/
static long gcdBinary(long op1, long op2) {
// PRE: (op1 > 0) and (op2 > 0)
int lsb1 = Long.numberOfTrailingZeros(op1);
int lsb2 = Long.numberOfTrailingZeros(op2);
int pow2Count = Math.min(lsb1, lsb2);
if (lsb1 != 0) {
op1 >>>= lsb1;
}
if (lsb2 != 0) {
op2 >>>= lsb2;
}
do {
if (op1 >= op2) {
op1 -= op2;
op1 >>>= Long.numberOfTrailingZeros(op1);
} else {
op2 -= op1;
op2 >>>= Long.numberOfTrailingZeros(op2);
}
} while (op1 != 0);
return (op2 << pow2Count);
}
/**
* Calculates a.modInverse(p) Based on: Savas, E; Koc, C "The Montgomery
* Modular Inverse - Revised"
*/
static TBigInteger modInverseMontgomery(TBigInteger a, TBigInteger p) {
if (a.sign == 0) {
// ZERO hasn't inverse
throw new ArithmeticException("BigInteger not invertible");
}
if (!p.testBit(0)) {
// montgomery inverse require even modulo
return modInverseHars(a, p);
}
int m = p.numberLength * 32;
// PRE: a \in [1, p - 1]
TBigInteger u, v, r, s;
u = p.copy(); // make copy to use inplace method
v = a.copy();
int max = Math.max(v.numberLength, u.numberLength);
r = new TBigInteger(1, 1, new int[max + 1]);
s = new TBigInteger(1, 1, new int[max + 1]);
s.digits[0] = 1;
// s == 1 && v == 0
int k = 0;
int lsbu = u.getLowestSetBit();
int lsbv = v.getLowestSetBit();
int toShift;
if (lsbu > lsbv) {
TBitLevel.inplaceShiftRight(u, lsbu);
TBitLevel.inplaceShiftRight(v, lsbv);
TBitLevel.inplaceShiftLeft(r, lsbv);
k += lsbu - lsbv;
} else {
TBitLevel.inplaceShiftRight(u, lsbu);
TBitLevel.inplaceShiftRight(v, lsbv);
TBitLevel.inplaceShiftLeft(s, lsbu);
k += lsbv - lsbu;
}
r.sign = 1;
while (v.signum() > 0) {
// INV v >= 0, u >= 0, v odd, u odd (except last iteration when v is
// even (0))
while (u.compareTo(v) > TBigInteger.EQUALS) {
TElementary.inplaceSubtract(u, v);
toShift = u.getLowestSetBit();
TBitLevel.inplaceShiftRight(u, toShift);
TElementary.inplaceAdd(r, s);
TBitLevel.inplaceShiftLeft(s, toShift);
k += toShift;
}
while (u.compareTo(v) <= TBigInteger.EQUALS) {
TElementary.inplaceSubtract(v, u);
if (v.signum() == 0)
break;
toShift = v.getLowestSetBit();
TBitLevel.inplaceShiftRight(v, toShift);
TElementary.inplaceAdd(s, r);
TBitLevel.inplaceShiftLeft(r, toShift);
k += toShift;
}
}
if (!u.isOne()) {
throw new ArithmeticException("BigInteger not invertible.");
}
if (r.compareTo(p) >= TBigInteger.EQUALS) {
TElementary.inplaceSubtract(r, p);
}
r = p.subtract(r);
// Have pair: ((BigInteger)r, (Integer)k) where r == a^(-1) * 2^k mod
// (module)
int n1 = calcN(p);
if (k > m) {
r = monPro(r, TBigInteger.ONE, p, n1);
k = k - m;
}
r = monPro(r, TBigInteger.getPowerOfTwo(m - k), p, n1);
return r;
}
/**
* Calculate the first digit of the inverse
*/
private static int calcN(TBigInteger a) {
long m0 = a.digits[0] & 0xFFFFFFFFL;
long n2 = 1L; // this is a'[0]
long powerOfTwo = 2L;
do {
if (((m0 * n2) & powerOfTwo) != 0) {
n2 |= powerOfTwo;
}
powerOfTwo <<= 1;
} while (powerOfTwo < 0x100000000L);
n2 = -n2;
return (int) (n2 & 0xFFFFFFFFL);
}
static TBigInteger squareAndMultiply(TBigInteger x2, TBigInteger a2, TBigInteger exponent, TBigInteger modulus,
int n2) {
TBigInteger res = x2;
for (int i = exponent.bitLength() - 1; i >= 0; i--) {
res = monPro(res, res, modulus, n2);
if (TBitLevel.testBit(exponent, i)) {
res = monPro(res, a2, modulus, n2);
}
}
return res;
}
/**
* Implements the "Shifting Euclidean modular inverse algorithm". "Laszlo
* Hars - Modular Inverse Algorithms Without Multiplications for
* Cryptographic Applications"
*
* @see TBigInteger#modInverse(TBigInteger)
* @param a
* a positive number
* @param m
* a positive modulus
*/
static TBigInteger modInverseHars(TBigInteger a, TBigInteger m) {
// PRE: (a > 0) and (m > 0)
TBigInteger u, v, r, s, temp;
// u = MAX(a,m), v = MIN(a,m)
if (a.compareTo(m) == TBigInteger.LESS) {
u = m;
v = a;
r = TBigInteger.ZERO;
s = TBigInteger.ONE;
} else {
v = m;
u = a;
s = TBigInteger.ZERO;
r = TBigInteger.ONE;
}
int uLen = u.bitLength();
int vLen = v.bitLength();
int f = uLen - vLen;
while (vLen > 1) {
if (u.sign == v.sign) {
u = u.subtract(v.shiftLeft(f));
r = r.subtract(s.shiftLeft(f));
} else {
u = u.add(v.shiftLeft(f));
r = r.add(s.shiftLeft(f));
}
uLen = u.abs().bitLength();
vLen = v.abs().bitLength();
f = uLen - vLen;
if (f < 0) {
// SWAP(u,v)
temp = u;
u = v;
v = temp;
// SWAP(r,s)
temp = r;
r = s;
s = temp;
f = -f;
vLen = uLen;
}
}
if (v.sign == 0) {
return TBigInteger.ZERO;
}
if (v.sign < 0) {
s = s.negate();
}
if (s.compareTo(m) == TBigInteger.GREATER) {
return s.subtract(m);
}
if (s.sign < 0) {
return s.add(m);
}
return s; // a^(-1) mod m
}
/*
* Implements the Montgomery modular exponentiation based in <i>The sliding
* windows algorithm and the MongomeryReduction</i>.
*
* @ar.org.fitc.ref
* "A. Menezes,P. van Oorschot, S. Vanstone - Handbook of Applied Cryptography"
* ;
*
* @see #oddModPow(BigInteger, BigInteger, BigInteger)
*/
static TBigInteger slidingWindow(TBigInteger x2, TBigInteger a2, TBigInteger exponent, TBigInteger modulus, int n2) {
// fill odd low pows of a2
TBigInteger pows[] = new TBigInteger[8];
TBigInteger res = x2;
int lowexp;
TBigInteger x3;
int acc3;
pows[0] = a2;
x3 = monPro(a2, a2, modulus, n2);
for (int i = 1; i <= 7; i++) {
pows[i] = monPro(pows[i - 1], x3, modulus, n2);
}
for (int i = exponent.bitLength() - 1; i >= 0; i--) {
if (TBitLevel.testBit(exponent, i)) {
lowexp = 1;
acc3 = i;
for (int j = Math.max(i - 3, 0); j <= i - 1; j++) {
if (TBitLevel.testBit(exponent, j)) {
if (j < acc3) {
acc3 = j;
lowexp = (lowexp << (i - j)) ^ 1;
} else {
lowexp = lowexp ^ (1 << (j - acc3));
}
}
}
for (int j = acc3; j <= i; j++) {
res = monPro(res, res, modulus, n2);
}
res = monPro(pows[(lowexp - 1) >> 1], res, modulus, n2);
i = acc3;
} else {
res = monPro(res, res, modulus, n2);
}
}
return res;
}
/**
* Performs modular exponentiation using the Montgomery Reduction. It
* requires that all parameters be positive and the modulus be odd. >
*
* @see TBigInteger#modPow(TBigInteger, TBigInteger)
* @see #monPro(TBigInteger, TBigInteger, TBigInteger, int)
* @see #slidingWindow(TBigInteger, TBigInteger, TBigInteger, TBigInteger,
* int)
* @see #squareAndMultiply(TBigInteger, TBigInteger, TBigInteger,
* TBigInteger, int)
*/
static TBigInteger oddModPow(TBigInteger base, TBigInteger exponent, TBigInteger modulus) {
// PRE: (base > 0), (exponent > 0), (modulus > 0) and (odd modulus)
int k = (modulus.numberLength << 5); // r = 2^k
// n-residue of base [base * r (mod modulus)]
TBigInteger a2 = base.shiftLeft(k).mod(modulus);
// n-residue of base [1 * r (mod modulus)]
TBigInteger x2 = TBigInteger.getPowerOfTwo(k).mod(modulus);
TBigInteger res;
// Compute (modulus[0]^(-1)) (mod 2^32) for odd modulus
int n2 = calcN(modulus);
if (modulus.numberLength == 1) {
res = squareAndMultiply(x2, a2, exponent, modulus, n2);
} else {
res = slidingWindow(x2, a2, exponent, modulus, n2);
}
return monPro(res, TBigInteger.ONE, modulus, n2);
}
/**
* Performs modular exponentiation using the Montgomery Reduction. It
* requires that all parameters be positive and the modulus be even. Based
* <i>The square and multiply algorithm and the Montgomery Reduction C. K.
* Koc - Montgomery Reduction with Even Modulus</i>. The square and multiply
* algorithm and the Montgomery Reduction.
*
* @ar.org.fitc.ref "C. K. Koc - Montgomery Reduction with Even Modulus"
* @see TBigInteger#modPow(TBigInteger, TBigInteger)
*/
static TBigInteger evenModPow(TBigInteger base, TBigInteger exponent, TBigInteger modulus) {
// PRE: (base > 0), (exponent > 0), (modulus > 0) and (modulus even)
// STEP 1: Obtain the factorization 'modulus'= q * 2^j.
int j = modulus.getLowestSetBit();
TBigInteger q = modulus.shiftRight(j);
// STEP 2: Compute x1 := base^exponent (mod q).
TBigInteger x1 = oddModPow(base, exponent, q);
// STEP 3: Compute x2 := base^exponent (mod 2^j).
TBigInteger x2 = pow2ModPow(base, exponent, j);
// STEP 4: Compute q^(-1) (mod 2^j) and y := (x2-x1) * q^(-1) (mod 2^j)
TBigInteger qInv = modPow2Inverse(q, j);
TBigInteger y = (x2.subtract(x1)).multiply(qInv);
inplaceModPow2(y, j);
if (y.sign < 0) {
y = y.add(TBigInteger.getPowerOfTwo(j));
}
// STEP 5: Compute and return: x1 + q * y
return x1.add(q.multiply(y));
}
/**
* It requires that all parameters be positive.
*
* @return {@code base<sup>exponent</sup> mod (2<sup>j</sup>)}.
* @see TBigInteger#modPow(TBigInteger, TBigInteger)
*/
static TBigInteger pow2ModPow(TBigInteger base, TBigInteger exponent, int j) {
// PRE: (base > 0), (exponent > 0) and (j > 0)
TBigInteger res = TBigInteger.ONE;
TBigInteger e = exponent.copy();
TBigInteger baseMod2toN = base.copy();
TBigInteger res2;
/*
* If 'base' is odd then it's coprime with 2^j and phi(2^j) = 2^(j-1);
* so we can reduce reduce the exponent (mod 2^(j-1)).
*/
if (base.testBit(0)) {
inplaceModPow2(e, j - 1);
}
inplaceModPow2(baseMod2toN, j);
for (int i = e.bitLength() - 1; i >= 0; i--) {
res2 = res.copy();
inplaceModPow2(res2, j);
res = res.multiply(res2);
if (TBitLevel.testBit(e, i)) {
res = res.multiply(baseMod2toN);
inplaceModPow2(res, j);
}
}
inplaceModPow2(res, j);
return res;
}
private static void monReduction(int[] res, TBigInteger modulus, int n2) {
/* res + m*modulus_digits */
int[] modulus_digits = modulus.digits;
int modulusLen = modulus.numberLength;
long outerCarry = 0;
for (int i = 0; i < modulusLen; i++) {
long innnerCarry = 0;
int m = (int) TMultiplication.unsignedMultAddAdd(res[i], n2, 0, 0);
for (int j = 0; j < modulusLen; j++) {
innnerCarry = TMultiplication.unsignedMultAddAdd(m, modulus_digits[j], res[i + j], (int) innnerCarry);
res[i + j] = (int) innnerCarry;
innnerCarry >>>= 32;
}
outerCarry += (res[i + modulusLen] & 0xFFFFFFFFL) + innnerCarry;
res[i + modulusLen] = (int) outerCarry;
outerCarry >>>= 32;
}
res[modulusLen << 1] = (int) outerCarry;
/* res / r */
for (int j = 0; j < modulusLen + 1; j++) {
res[j] = res[j + modulusLen];
}
}
/**
* Implements the Montgomery Product of two integers represented by
* {@code int} arrays. The arrays are supposed in <i>little endian</i>
* notation.
*
* @param a
* The first factor of the product.
* @param b
* The second factor of the product.
* @param modulus
* The modulus of the operations. Z<sub>modulus</sub>.
* @param n2
* The digit modulus'[0].
* @ar.org.fitc.ref "C. K. Koc - Analyzing and Comparing Montgomery
* Multiplication Algorithms"
* @see #modPowOdd(TBigInteger, TBigInteger, TBigInteger)
*/
static TBigInteger monPro(TBigInteger a, TBigInteger b, TBigInteger modulus, int n2) {
int modulusLen = modulus.numberLength;
int res[] = new int[(modulusLen << 1) + 1];
TMultiplication.multArraysPAP(a.digits, Math.min(modulusLen, a.numberLength), b.digits,
Math.min(modulusLen, b.numberLength), res);
monReduction(res, modulus, n2);
return finalSubtraction(res, modulus);
}
/**
* Performs the final reduction of the Montgomery algorithm.
*
* @see monPro(BigInteger, BigInteger, BigInteger, long)
* @see monSquare(BigInteger, BigInteger, long)
*/
static TBigInteger finalSubtraction(int res[], TBigInteger modulus) {
// skipping leading zeros
int modulusLen = modulus.numberLength;
boolean doSub = res[modulusLen] != 0;
if (!doSub) {
int modulusDigits[] = modulus.digits;
doSub = true;
for (int i = modulusLen - 1; i >= 0; i--) {
if (res[i] != modulusDigits[i]) {
doSub = (res[i] != 0) && ((res[i] & 0xFFFFFFFFL) > (modulusDigits[i] & 0xFFFFFFFFL));
break;
}
}
}
TBigInteger result = new TBigInteger(1, modulusLen + 1, res);
// if (res >= modulusDigits) compute (res - modulusDigits)
if (doSub) {
TElementary.inplaceSubtract(result, modulus);
}
result.cutOffLeadingZeroes();
return result;
}
/**
* @param x
* an odd positive number.
* @param n
* the exponent by which 2 is raised.
* @return {@code x<sup>-1</sup> (mod 2<sup>n</sup>)}.
*/
static TBigInteger modPow2Inverse(TBigInteger x, int n) {
// PRE: (x > 0), (x is odd), and (n > 0)
TBigInteger y = new TBigInteger(1, new int[1 << n]);
y.numberLength = 1;
y.digits[0] = 1;
y.sign = 1;
for (int i = 1; i < n; i++) {
if (TBitLevel.testBit(x.multiply(y), i)) {
// Adding 2^i to y (setting the i-th bit)
y.digits[i >> 5] |= (1 << (i & 31));
}
}
return y;
}
/**
* Performs {@code x = x mod (2<sup>n</sup>)}.
*
* @param x
* a positive number, it will store the result.
* @param n
* a positive exponent of {@code 2}.
*/
static void inplaceModPow2(TBigInteger x, int n) {
// PRE: (x > 0) and (n >= 0)
int fd = n >> 5;
int leadingZeros;
if ((x.numberLength < fd) || (x.bitLength() <= n)) {
return;
}
leadingZeros = 32 - (n & 31);
x.numberLength = fd + 1;
x.digits[fd] &= (leadingZeros < 32) ? (-1 >>> leadingZeros) : 0;
x.cutOffLeadingZeroes();
}
}

View File

@ -0,0 +1,432 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.math;
/**
* Static library that provides the basic arithmetic mutable operations for
* {@link TBigInteger}. The operations provided are listed below.
* <ul type="circle">
* <li>Addition.</li>
* <li>Subtraction.</li>
* <li>Comparison.</li>
* </ul>
* In addition to this, some <i><b>Inplace</b></i> (mutable) methods are
* provided.
*/
class TElementary {
/** Just to denote that this class can't be instantiated */
private TElementary() {
}
/**
* Compares two arrays. All elements are treated as unsigned integers. The
* magnitude is the bit chain of elements in big-endian order.
*
* @param a
* the first array
* @param b
* the second array
* @param size
* the size of arrays
* @return 1 if a > b, -1 if a < b, 0 if a == b
*/
static int compareArrays(final int[] a, final int[] b, final int size) {
int i;
for (i = size - 1; (i >= 0) && (a[i] == b[i]); i--) {
// do nothing
}
return ((i < 0) ? TBigInteger.EQUALS : (a[i] & 0xFFFFFFFFL) < (b[i] & 0xFFFFFFFFL) ? TBigInteger.LESS
: TBigInteger.GREATER);
}
/** @see TBigInteger#add(TBigInteger) */
static TBigInteger add(TBigInteger op1, TBigInteger op2) {
int resDigits[];
int resSign;
int op1Sign = op1.sign;
int op2Sign = op2.sign;
if (op1Sign == 0) {
return op2;
}
if (op2Sign == 0) {
return op1;
}
int op1Len = op1.numberLength;
int op2Len = op2.numberLength;
if (op1Len + op2Len == 2) {
long a = (op1.digits[0] & 0xFFFFFFFFL);
long b = (op2.digits[0] & 0xFFFFFFFFL);
long res;
int valueLo;
int valueHi;
if (op1Sign == op2Sign) {
res = a + b;
valueLo = (int) res;
valueHi = (int) (res >>> 32);
return ((valueHi == 0) ? new TBigInteger(op1Sign, valueLo) : new TBigInteger(op1Sign, 2, new int[] {
valueLo, valueHi }));
}
return TBigInteger.valueOf((op1Sign < 0) ? (b - a) : (a - b));
} else if (op1Sign == op2Sign) {
resSign = op1Sign;
// an augend should not be shorter than addend
resDigits = (op1Len >= op2Len) ? add(op1.digits, op1Len, op2.digits, op2Len) : add(op2.digits, op2Len,
op1.digits, op1Len);
} else { // signs are different
int cmp = ((op1Len != op2Len) ? ((op1Len > op2Len) ? 1 : -1)
: compareArrays(op1.digits, op2.digits, op1Len));
if (cmp == TBigInteger.EQUALS) {
return TBigInteger.ZERO;
}
// a minuend should not be shorter than subtrahend
if (cmp == TBigInteger.GREATER) {
resSign = op1Sign;
resDigits = subtract(op1.digits, op1Len, op2.digits, op2Len);
} else {
resSign = op2Sign;
resDigits = subtract(op2.digits, op2Len, op1.digits, op1Len);
}
}
TBigInteger res = new TBigInteger(resSign, resDigits.length, resDigits);
res.cutOffLeadingZeroes();
return res;
}
/**
* Performs {@code res = a + b}.
*/
private static void add(int res[], int a[], int aSize, int b[], int bSize) {
// PRE: a.length < max(aSize, bSize)
int i;
long carry = (a[0] & 0xFFFFFFFFL) + (b[0] & 0xFFFFFFFFL);
res[0] = (int) carry;
carry >>= 32;
if (aSize >= bSize) {
for (i = 1; i < bSize; i++) {
carry += (a[i] & 0xFFFFFFFFL) + (b[i] & 0xFFFFFFFFL);
res[i] = (int) carry;
carry >>= 32;
}
for (; i < aSize; i++) {
carry += a[i] & 0xFFFFFFFFL;
res[i] = (int) carry;
carry >>= 32;
}
} else {
for (i = 1; i < aSize; i++) {
carry += (a[i] & 0xFFFFFFFFL) + (b[i] & 0xFFFFFFFFL);
res[i] = (int) carry;
carry >>= 32;
}
for (; i < bSize; i++) {
carry += b[i] & 0xFFFFFFFFL;
res[i] = (int) carry;
carry >>= 32;
}
}
if (carry != 0) {
res[i] = (int) carry;
}
}
/** @see TBigInteger#subtract(TBigInteger) */
static TBigInteger subtract(TBigInteger op1, TBigInteger op2) {
int resSign;
int resDigits[];
int op1Sign = op1.sign;
int op2Sign = op2.sign;
if (op2Sign == 0) {
return op1;
}
if (op1Sign == 0) {
return op2.negate();
}
int op1Len = op1.numberLength;
int op2Len = op2.numberLength;
if (op1Len + op2Len == 2) {
long a = (op1.digits[0] & 0xFFFFFFFFL);
long b = (op2.digits[0] & 0xFFFFFFFFL);
if (op1Sign < 0) {
a = -a;
}
if (op2Sign < 0) {
b = -b;
}
return TBigInteger.valueOf(a - b);
}
int cmp = ((op1Len != op2Len) ? ((op1Len > op2Len) ? 1 : -1) : TElementary.compareArrays(op1.digits, op2.digits,
op1Len));
if (cmp == TBigInteger.LESS) {
resSign = -op2Sign;
resDigits = (op1Sign == op2Sign) ? subtract(op2.digits, op2Len, op1.digits, op1Len) : add(op2.digits,
op2Len, op1.digits, op1Len);
} else {
resSign = op1Sign;
if (op1Sign == op2Sign) {
if (cmp == TBigInteger.EQUALS) {
return TBigInteger.ZERO;
}
resDigits = subtract(op1.digits, op1Len, op2.digits, op2Len);
} else {
resDigits = add(op1.digits, op1Len, op2.digits, op2Len);
}
}
TBigInteger res = new TBigInteger(resSign, resDigits.length, resDigits);
res.cutOffLeadingZeroes();
return res;
}
/**
* Performs {@code res = a - b}. It is assumed the magnitude of a is not
* less than the magnitude of b.
*/
private static void subtract(int res[], int a[], int aSize, int b[], int bSize) {
// PRE: a[] >= b[]
int i;
long borrow = 0;
for (i = 0; i < bSize; i++) {
borrow += (a[i] & 0xFFFFFFFFL) - (b[i] & 0xFFFFFFFFL);
res[i] = (int) borrow;
borrow >>= 32; // -1 or 0
}
for (; i < aSize; i++) {
borrow += a[i] & 0xFFFFFFFFL;
res[i] = (int) borrow;
borrow >>= 32; // -1 or 0
}
}
/**
* Addss the value represented by {@code b} to the value represented by
* {@code a}. It is assumed the magnitude of a is not less than the
* magnitude of b.
*
* @return {@code a + b}
*/
private static int[] add(int a[], int aSize, int b[], int bSize) {
// PRE: a[] >= b[]
int res[] = new int[aSize + 1];
add(res, a, aSize, b, bSize);
return res;
}
/**
* Performs {@code op1 += op2}. {@code op1} must have enough place to store
* the result (i.e. {@code op1.bitLength() >= op2.bitLength()}). Both should
* be positive (i.e. {@code op1 >= op2}).
*
* @param op1
* the input minuend, and the output result.
* @param op2
* the addend
*/
static void inplaceAdd(TBigInteger op1, TBigInteger op2) {
// PRE: op1 >= op2 > 0
add(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
op1.numberLength = Math.min(Math.max(op1.numberLength, op2.numberLength) + 1, op1.digits.length);
op1.cutOffLeadingZeroes();
op1.unCache();
}
/**
* Adds an integer value to the array of integers remembering carry.
*
* @return a possible generated carry (0 or 1)
*/
static int inplaceAdd(int a[], final int aSize, final int addend) {
long carry = addend & 0xFFFFFFFFL;
for (int i = 0; (carry != 0) && (i < aSize); i++) {
carry += a[i] & 0xFFFFFFFFL;
a[i] = (int) carry;
carry >>= 32;
}
return (int) carry;
}
/**
* Performs: {@code op1 += addend}. The number must to have place to hold a
* possible carry.
*/
static void inplaceAdd(TBigInteger op1, final int addend) {
int carry = inplaceAdd(op1.digits, op1.numberLength, addend);
if (carry == 1) {
op1.digits[op1.numberLength] = 1;
op1.numberLength++;
}
op1.unCache();
}
/**
* Performs {@code op1 -= op2}. {@code op1} must have enough place to store
* the result (i.e. {@code op1.bitLength() >= op2.bitLength()}). Both should
* be positive (what implies that {@code op1 >= op2}).
*
* @param op1
* the input minuend, and the output result.
* @param op2
* the subtrahend
*/
static void inplaceSubtract(TBigInteger op1, TBigInteger op2) {
// PRE: op1 >= op2 > 0
subtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
op1.cutOffLeadingZeroes();
op1.unCache();
}
/**
* Performs {@code res = b - a}
*/
private static void inverseSubtract(int res[], int a[], int aSize, int b[], int bSize) {
int i;
long borrow = 0;
if (aSize < bSize) {
for (i = 0; i < aSize; i++) {
borrow += (b[i] & 0xFFFFFFFFL) - (a[i] & 0xFFFFFFFFL);
res[i] = (int) borrow;
borrow >>= 32; // -1 or 0
}
for (; i < bSize; i++) {
borrow += b[i] & 0xFFFFFFFFL;
res[i] = (int) borrow;
borrow >>= 32; // -1 or 0
}
} else {
for (i = 0; i < bSize; i++) {
borrow += (b[i] & 0xFFFFFFFFL) - (a[i] & 0xFFFFFFFFL);
res[i] = (int) borrow;
borrow >>= 32; // -1 or 0
}
for (; i < aSize; i++) {
borrow -= a[i] & 0xFFFFFFFFL;
res[i] = (int) borrow;
borrow >>= 32; // -1 or 0
}
}
}
/**
* Subtracts the value represented by {@code b} from the value represented
* by {@code a}. It is assumed the magnitude of a is not less than the
* magnitude of b.
*
* @return {@code a - b}
*/
private static int[] subtract(int a[], int aSize, int b[], int bSize) {
// PRE: a[] >= b[]
int res[] = new int[aSize];
subtract(res, a, aSize, b, bSize);
return res;
}
/**
* Same as
*
* @link #inplaceSubtract(BigInteger, BigInteger), but without the
* restriction of non-positive values
* @param op1
* should have enough space to save the result
* @param op2
*/
static void completeInPlaceSubtract(TBigInteger op1, TBigInteger op2) {
int resultSign = op1.compareTo(op2);
if (op1.sign == 0) {
System.arraycopy(op2.digits, 0, op1.digits, 0, op2.numberLength);
op1.sign = -op2.sign;
} else if (op1.sign != op2.sign) {
add(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
op1.sign = resultSign;
} else {
int sign = unsignedArraysCompare(op1.digits, op2.digits, op1.numberLength, op2.numberLength);
if (sign > 0) {
subtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
// op1.sign remains equal
} else {
inverseSubtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
op1.sign = -op1.sign;
}
}
op1.numberLength = Math.max(op1.numberLength, op2.numberLength) + 1;
op1.cutOffLeadingZeroes();
op1.unCache();
}
/**
* Same as @link #inplaceAdd(BigInteger, BigInteger), but without the
* restriction of non-positive values
*
* @param op1
* any number
* @param op2
* any number
*/
static void completeInPlaceAdd(TBigInteger op1, TBigInteger op2) {
if (op1.sign == 0)
System.arraycopy(op2.digits, 0, op1.digits, 0, op2.numberLength);
else if (op2.sign == 0)
return;
else if (op1.sign == op2.sign)
add(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
else {
int sign = unsignedArraysCompare(op1.digits, op2.digits, op1.numberLength, op2.numberLength);
if (sign > 0)
subtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
else {
inverseSubtract(op1.digits, op1.digits, op1.numberLength, op2.digits, op2.numberLength);
op1.sign = -op1.sign;
}
}
op1.numberLength = Math.max(op1.numberLength, op2.numberLength) + 1;
op1.cutOffLeadingZeroes();
op1.unCache();
}
/**
* Compares two arrays, representing unsigned integer in little-endian
* order. Returns +1,0,-1 if a is - respective - greater, equal or lesser
* then b
*/
private static int unsignedArraysCompare(int[] a, int[] b, int aSize, int bSize) {
if (aSize > bSize)
return 1;
else if (aSize < bSize)
return -1;
else {
int i;
for (i = aSize - 1; i >= 0 && a[i] == b[i]; i--) {
// do nothing
}
return i < 0 ? TBigInteger.EQUALS : ((a[i] & 0xFFFFFFFFL) < (b[i] & 0xFFFFFFFFL) ? TBigInteger.LESS
: TBigInteger.GREATER);
}
}
}

View File

@ -0,0 +1,808 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.math;
/**
* The library implements some logical operations over {@code BigInteger}. The
* operations provided are listed below.
* <ul type="circle">
* <li>not</li>
* <li>and</li>
* <li>andNot</li>
* <li>or</li>
* <li>xor</li>
* </ul>
*/
class TLogical {
/** Just to denote that this class can't be instantiated. */
private TLogical() {}
/** @see TBigInteger#not() */
static TBigInteger not(TBigInteger val) {
if (val.sign == 0) {
return TBigInteger.MINUS_ONE;
}
if (val.equals(TBigInteger.MINUS_ONE)) {
return TBigInteger.ZERO;
}
int resDigits[] = new int[val.numberLength + 1];
int i;
if (val.sign > 0) {
// ~val = -val + 1
if (val.digits[val.numberLength - 1] != -1) {
for (i = 0; val.digits[i] == -1; i++) {
// do nothing
}
} else {
for (i = 0; (i < val.numberLength) && (val.digits[i] == -1); i++) {
// do nothing
}
if (i == val.numberLength) {
resDigits[i] = 1;
return new TBigInteger(-val.sign, i + 1, resDigits);
}
}
// Here a carry 1 was generated
} else {// (val.sign < 0)
// ~val = -val - 1
for (i = 0; val.digits[i] == 0; i++) {
resDigits[i] = -1;
}
// Here a borrow -1 was generated
}
// Now, the carry/borrow can be absorbed
resDigits[i] = val.digits[i] + val.sign;
// Copying the remaining unchanged digit
for (i++; i < val.numberLength; i++) {
resDigits[i] = val.digits[i];
}
return new TBigInteger(-val.sign, i, resDigits);
}
/** @see TBigInteger#and(TBigInteger) */
static TBigInteger and(TBigInteger val, TBigInteger that) {
if (that.sign == 0 || val.sign == 0) {
return TBigInteger.ZERO;
}
if (that.equals(TBigInteger.MINUS_ONE)){
return val;
}
if (val.equals(TBigInteger.MINUS_ONE)) {
return that;
}
if (val.sign > 0) {
if (that.sign > 0) {
return andPositive(val, that);
} else {
return andDiffSigns(val, that);
}
} else {
if (that.sign > 0) {
return andDiffSigns(that, val);
} else if (val.numberLength > that.numberLength) {
return andNegative(val, that);
} else {
return andNegative(that, val);
}
}
}
/** @return sign = 1, magnitude = val.magnitude & that.magnitude*/
static TBigInteger andPositive(TBigInteger val, TBigInteger that) {
// PRE: both arguments are positive
int resLength = Math.min(val.numberLength, that.numberLength);
int i = Math.max(val.getFirstNonzeroDigit(), that.getFirstNonzeroDigit());
if (i >= resLength) {
return TBigInteger.ZERO;
}
int resDigits[] = new int[resLength];
for ( ; i < resLength; i++) {
resDigits[i] = val.digits[i] & that.digits[i];
}
TBigInteger result = new TBigInteger(1, resLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
/** @return sign = positive.magnitude & magnitude = -negative.magnitude */
static TBigInteger andDiffSigns(TBigInteger positive, TBigInteger negative) {
// PRE: positive is positive and negative is negative
int iPos = positive.getFirstNonzeroDigit();
int iNeg = negative.getFirstNonzeroDigit();
// Look if the trailing zeros of the negative will "blank" all
// the positive digits
if (iNeg >= positive.numberLength) {
return TBigInteger.ZERO;
}
int resLength = positive.numberLength;
int resDigits[] = new int[resLength];
// Must start from max(iPos, iNeg)
int i = Math.max(iPos, iNeg);
if (i == iNeg) {
resDigits[i] = -negative.digits[i] & positive.digits[i];
i++;
}
int limit = Math.min(negative.numberLength, positive.numberLength);
for ( ; i < limit; i++) {
resDigits[i] = ~negative.digits[i] & positive.digits[i];
}
// if the negative was shorter must copy the remaining digits
// from positive
if (i >= negative.numberLength) {
for ( ; i < positive.numberLength; i++) {
resDigits[i] = positive.digits[i];
}
} // else positive ended and must "copy" virtual 0's, do nothing then
TBigInteger result = new TBigInteger(1, resLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
/** @return sign = -1, magnitude = -(-longer.magnitude & -shorter.magnitude)*/
static TBigInteger andNegative(TBigInteger longer, TBigInteger shorter) {
// PRE: longer and shorter are negative
// PRE: longer has at least as many digits as shorter
int iLonger = longer.getFirstNonzeroDigit();
int iShorter = shorter.getFirstNonzeroDigit();
// Does shorter matter?
if (iLonger >= shorter.numberLength) {
return longer;
}
int resLength;
int resDigits[];
int i = Math.max(iShorter, iLonger);
int digit;
if (iShorter > iLonger) {
digit = -shorter.digits[i] & ~longer.digits[i];
} else if (iShorter < iLonger) {
digit = ~shorter.digits[i] & -longer.digits[i];
} else {
digit = -shorter.digits[i] & -longer.digits[i];
}
if (digit == 0) {
for (i++; i < shorter.numberLength && (digit = ~(longer.digits[i] | shorter.digits[i])) == 0; i++) {
// do nothing
}
if (digit == 0) {
// shorter has only the remaining virtual sign bits
for ( ; i < longer.numberLength && (digit = ~longer.digits[i]) == 0; i++) {
// do nothing
}
if (digit == 0) {
resLength = longer.numberLength + 1;
resDigits = new int[resLength];
resDigits[resLength - 1] = 1;
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
return result;
}
}
}
resLength = longer.numberLength;
resDigits = new int[resLength];
resDigits[i] = -digit;
for (i++; i < shorter.numberLength; i++){
// resDigits[i] = ~(~longer.digits[i] & ~shorter.digits[i];)
resDigits[i] = longer.digits[i] | shorter.digits[i];
}
// shorter has only the remaining virtual sign bits
for( ; i < longer.numberLength; i++){
resDigits[i] = longer.digits[i];
}
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
return result;
}
/** @see TBigInteger#andNot(TBigInteger) */
static TBigInteger andNot(TBigInteger val, TBigInteger that) {
if (that.sign == 0 ) {
return val;
}
if (val.sign == 0) {
return TBigInteger.ZERO;
}
if (val.equals(TBigInteger.MINUS_ONE)) {
return that.not();
}
if (that.equals(TBigInteger.MINUS_ONE)){
return TBigInteger.ZERO;
}
//if val == that, return 0
if (val.sign > 0) {
if (that.sign > 0) {
return andNotPositive(val, that);
} else {
return andNotPositiveNegative(val, that);
}
} else {
if (that.sign > 0) {
return andNotNegativePositive(val, that);
} else {
return andNotNegative(val, that);
}
}
}
/** @return sign = 1, magnitude = val.magnitude & ~that.magnitude*/
static TBigInteger andNotPositive(TBigInteger val, TBigInteger that) {
// PRE: both arguments are positive
int resDigits[] = new int[val.numberLength];
int limit = Math.min(val.numberLength, that.numberLength);
int i;
for (i = val.getFirstNonzeroDigit(); i < limit; i++) {
resDigits[i] = val.digits[i] & ~that.digits[i];
}
for ( ; i < val.numberLength; i++) {
resDigits[i] = val.digits[i];
}
TBigInteger result = new TBigInteger(1, val.numberLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
/** @return sign = 1, magnitude = positive.magnitude & ~(-negative.magnitude)*/
static TBigInteger andNotPositiveNegative(TBigInteger positive, TBigInteger negative) {
// PRE: positive > 0 && negative < 0
int iNeg = negative.getFirstNonzeroDigit();
int iPos = positive.getFirstNonzeroDigit();
if (iNeg >= positive.numberLength) {
return positive;
}
int resLength = Math.min(positive.numberLength, negative.numberLength);
int resDigits[] = new int[resLength];
// Always start from first non zero of positive
int i = iPos;
for ( ; i < iNeg; i++) {
// resDigits[i] = positive.digits[i] & -1 (~0)
resDigits[i] = positive.digits[i];
}
if (i == iNeg) {
resDigits[i] = positive.digits[i] & (negative.digits[i] - 1);
i++;
}
for ( ; i < resLength; i++) {
// resDigits[i] = positive.digits[i] & ~(~negative.digits[i]);
resDigits[i] = positive.digits[i] & negative.digits[i];
}
TBigInteger result = new TBigInteger(1, resLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
/** @return sign = -1, magnitude = -(-negative.magnitude & ~positive.magnitude)*/
static TBigInteger andNotNegativePositive(TBigInteger negative, TBigInteger positive) {
// PRE: negative < 0 && positive > 0
int resLength;
int resDigits[];
int limit;
int digit;
int iNeg = negative.getFirstNonzeroDigit();
int iPos = positive.getFirstNonzeroDigit();
if (iNeg >= positive.numberLength) {
return negative;
}
resLength = Math.max(negative.numberLength, positive.numberLength);
int i = iNeg;
if (iPos > iNeg) {
resDigits = new int[resLength];
limit = Math.min(negative.numberLength, iPos);
for ( ; i < limit; i++) {
// 1st case: resDigits [i] = -(-negative.digits[i] & (~0))
// otherwise: resDigits[i] = ~(~negative.digits[i] & ~0) ;
resDigits[i] = negative.digits[i];
}
if (i == negative.numberLength) {
for (i = iPos; i < positive.numberLength; i++) {
// resDigits[i] = ~(~positive.digits[i] & -1);
resDigits[i] = positive.digits[i];
}
}
} else {
digit = -negative.digits[i] & ~positive.digits[i];
if (digit == 0) {
limit = Math.min(positive.numberLength, negative.numberLength);
for (i++; i < limit && (digit = ~(negative.digits[i] | positive.digits[i])) == 0; i++) {
// do nothing
}
if (digit == 0) {
// the shorter has only the remaining virtual sign bits
for ( ; i < positive.numberLength && (digit = ~positive.digits[i]) == 0; i++) {
// do nothing
}
for ( ; i < negative.numberLength && (digit = ~negative.digits[i]) == 0; i++) {
// do nothing
}
if (digit == 0) {
resLength++;
resDigits = new int[resLength];
resDigits[resLength - 1] = 1;
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
return result;
}
}
}
resDigits = new int[resLength];
resDigits[i] = -digit;
i++;
}
limit = Math.min(positive.numberLength, negative.numberLength);
for ( ; i < limit; i++) {
//resDigits[i] = ~(~negative.digits[i] & ~positive.digits[i]);
resDigits[i] = negative.digits[i] | positive.digits[i];
}
// Actually one of the next two cycles will be executed
for ( ; i < negative.numberLength; i++) {
resDigits[i] = negative.digits[i];
}
for ( ; i < positive.numberLength; i++) {
resDigits[i] = positive.digits[i];
}
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
return result;
}
/** @return sign = 1, magnitude = -val.magnitude & ~(-that.magnitude)*/
static TBigInteger andNotNegative(TBigInteger val, TBigInteger that) {
// PRE: val < 0 && that < 0
int iVal = val.getFirstNonzeroDigit();
int iThat = that.getFirstNonzeroDigit();
if (iVal >= that.numberLength) {
return TBigInteger.ZERO;
}
int resLength = that.numberLength;
int resDigits[] = new int[resLength];
int limit;
int i = iVal;
if (iVal < iThat) {
// resDigits[i] = -val.digits[i] & -1;
resDigits[i] = -val.digits[i];
limit = Math.min(val.numberLength, iThat);
for (i++; i < limit; i++) {
// resDigits[i] = ~val.digits[i] & -1;
resDigits[i] = ~val.digits[i];
}
if (i == val.numberLength) {
for ( ; i < iThat; i++) {
// resDigits[i] = -1 & -1;
resDigits[i] = -1;
}
// resDigits[i] = -1 & ~-that.digits[i];
resDigits[i] = that.digits[i] - 1;
} else {
// resDigits[i] = ~val.digits[i] & ~-that.digits[i];
resDigits[i] = ~val.digits[i] & (that.digits[i] - 1);
}
} else if (iThat < iVal ) {
// resDigits[i] = -val.digits[i] & ~~that.digits[i];
resDigits[i] = -val.digits[i] & that.digits[i];
} else {
// resDigits[i] = -val.digits[i] & ~-that.digits[i];
resDigits[i] = -val.digits[i] & (that.digits[i] - 1);
}
limit = Math.min(val.numberLength, that.numberLength);
for (i++; i < limit; i++) {
// resDigits[i] = ~val.digits[i] & ~~that.digits[i];
resDigits[i] = ~val.digits[i] & that.digits[i];
}
for ( ; i < that.numberLength; i++) {
// resDigits[i] = -1 & ~~that.digits[i];
resDigits[i] = that.digits[i];
}
TBigInteger result = new TBigInteger(1, resLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
/** @see TBigInteger#or(TBigInteger) */
static TBigInteger or(TBigInteger val, TBigInteger that) {
if (that.equals(TBigInteger.MINUS_ONE) || val.equals(TBigInteger.MINUS_ONE)) {
return TBigInteger.MINUS_ONE;
}
if (that.sign == 0) {
return val;
}
if (val.sign == 0) {
return that;
}
if (val.sign > 0) {
if (that.sign > 0) {
if (val.numberLength > that.numberLength) {
return orPositive(val, that);
} else {
return orPositive(that, val);
}
} else {
return orDiffSigns(val, that);
}
} else {
if (that.sign > 0) {
return orDiffSigns(that, val);
} else if (that.getFirstNonzeroDigit() > val.getFirstNonzeroDigit()) {
return orNegative(that, val);
} else {
return orNegative(val, that);
}
}
}
/** @return sign = 1, magnitude = longer.magnitude | shorter.magnitude*/
static TBigInteger orPositive(TBigInteger longer, TBigInteger shorter) {
// PRE: longer and shorter are positive;
// PRE: longer has at least as many digits as shorter
int resLength = longer.numberLength;
int resDigits[] = new int[resLength];
int i = Math.min(longer.getFirstNonzeroDigit(), shorter.getFirstNonzeroDigit());
for (i = 0; i < shorter.numberLength; i++) {
resDigits[i] = longer.digits[i] | shorter.digits[i];
}
for ( ; i < resLength; i++) {
resDigits[i] = longer.digits[i];
}
TBigInteger result = new TBigInteger(1, resLength, resDigits);
return result;
}
/** @return sign = -1, magnitude = -(-val.magnitude | -that.magnitude) */
static TBigInteger orNegative(TBigInteger val, TBigInteger that){
// PRE: val and that are negative;
// PRE: val has at least as many trailing zeros digits as that
int iThat = that.getFirstNonzeroDigit();
int iVal = val.getFirstNonzeroDigit();
int i;
if (iVal >= that.numberLength) {
return that;
}else if (iThat >= val.numberLength) {
return val;
}
int resLength = Math.min(val.numberLength, that.numberLength);
int resDigits[] = new int[resLength];
//Looking for the first non-zero digit of the result
if (iThat == iVal) {
resDigits[iVal] = -(-val.digits[iVal] | -that.digits[iVal]);
i = iVal;
} else {
for (i = iThat; i < iVal; i++) {
resDigits[i] = that.digits[i];
}
resDigits[i] = that.digits[i] & (val.digits[i] - 1);
}
for (i++; i < resLength; i++) {
resDigits[i] = val.digits[i] & that.digits[i];
}
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
/** @return sign = -1, magnitude = -(positive.magnitude | -negative.magnitude) */
static TBigInteger orDiffSigns(TBigInteger positive, TBigInteger negative){
// Jumping over the least significant zero bits
int iNeg = negative.getFirstNonzeroDigit();
int iPos = positive.getFirstNonzeroDigit();
int i;
int limit;
// Look if the trailing zeros of the positive will "copy" all
// the negative digits
if (iPos >= negative.numberLength) {
return negative;
}
int resLength = negative.numberLength;
int resDigits[] = new int[resLength];
if (iNeg < iPos ) {
// We know for sure that this will
// be the first non zero digit in the result
for (i = iNeg; i < iPos; i++) {
resDigits[i] = negative.digits[i];
}
} else if (iPos < iNeg) {
i = iPos;
resDigits[i] = -positive.digits[i];
limit = Math.min(positive.numberLength, iNeg);
for(i++; i < limit; i++ ) {
resDigits[i] = ~positive.digits[i];
}
if (i != positive.numberLength) {
resDigits[i] = ~(-negative.digits[i] | positive.digits[i]);
} else{
for (; i<iNeg; i++) {
resDigits[i] = -1;
}
// resDigits[i] = ~(-negative.digits[i] | 0);
resDigits[i] = negative.digits[i] - 1;
}
i++;
} else {// iNeg == iPos
// Applying two complement to negative and to result
i = iPos;
resDigits[i] = -(-negative.digits[i] | positive.digits[i]);
i++;
}
limit = Math.min(negative.numberLength, positive.numberLength);
for (; i < limit; i++) {
// Applying two complement to negative and to result
// resDigits[i] = ~(~negative.digits[i] | positive.digits[i] );
resDigits[i] = negative.digits[i] & ~positive.digits[i];
}
for( ; i < negative.numberLength; i++) {
resDigits[i] = negative.digits[i];
}
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
/** @see TBigInteger#xor(TBigInteger) */
static TBigInteger xor(TBigInteger val, TBigInteger that) {
if (that.sign == 0) {
return val;
}
if (val.sign == 0) {
return that;
}
if (that.equals(TBigInteger.MINUS_ONE)) {
return val.not();
}
if (val.equals(TBigInteger.MINUS_ONE)) {
return that.not();
}
if (val.sign > 0) {
if (that.sign > 0) {
if (val.numberLength > that.numberLength) {
return xorPositive(val, that);
} else {
return xorPositive(that, val);
}
} else {
return xorDiffSigns(val, that);
}
} else {
if (that.sign > 0) {
return xorDiffSigns(that, val);
} else if (that.getFirstNonzeroDigit() > val.getFirstNonzeroDigit()) {
return xorNegative(that, val);
} else {
return xorNegative(val, that);
}
}
}
/** @return sign = 0, magnitude = longer.magnitude | shorter.magnitude */
static TBigInteger xorPositive(TBigInteger longer, TBigInteger shorter) {
// PRE: longer and shorter are positive;
// PRE: longer has at least as many digits as shorter
int resLength = longer.numberLength;
int resDigits[] = new int[resLength];
int i = Math.min(longer.getFirstNonzeroDigit(), shorter.getFirstNonzeroDigit());
for ( ; i < shorter.numberLength; i++) {
resDigits[i] = longer.digits[i] ^ shorter.digits[i];
}
for( ; i < longer.numberLength; i++ ){
resDigits[i] = longer.digits[i];
}
TBigInteger result = new TBigInteger(1, resLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
/** @return sign = 0, magnitude = -val.magnitude ^ -that.magnitude */
static TBigInteger xorNegative(TBigInteger val, TBigInteger that){
// PRE: val and that are negative
// PRE: val has at least as many trailing zero digits as that
int resLength = Math.max(val.numberLength, that.numberLength);
int resDigits[] = new int[resLength];
int iVal = val.getFirstNonzeroDigit();
int iThat = that.getFirstNonzeroDigit();
int i = iThat;
int limit;
if (iVal == iThat) {
resDigits[i] = -val.digits[i] ^ -that.digits[i];
} else {
resDigits[i] = -that.digits[i];
limit = Math.min(that.numberLength, iVal);
for (i++; i < limit; i++) {
resDigits[i] = ~that.digits[i];
}
// Remains digits in that?
if (i == that.numberLength) {
//Jumping over the remaining zero to the first non one
for ( ;i < iVal; i++) {
//resDigits[i] = 0 ^ -1;
resDigits[i] = -1;
}
//resDigits[i] = -val.digits[i] ^ -1;
resDigits[i] = val.digits[i] - 1;
} else {
resDigits[i] = -val.digits[i] ^ ~that.digits[i];
}
}
limit = Math.min(val.numberLength, that.numberLength);
//Perform ^ between that al val until that ends
for (i++; i < limit; i++) {
//resDigits[i] = ~val.digits[i] ^ ~that.digits[i];
resDigits[i] = val.digits[i] ^ that.digits[i];
}
//Perform ^ between val digits and -1 until val ends
for ( ; i < val.numberLength; i++) {
//resDigits[i] = ~val.digits[i] ^ -1 ;
resDigits[i] = val.digits[i] ;
}
for ( ; i < that.numberLength; i++) {
//resDigits[i] = -1 ^ ~that.digits[i] ;
resDigits[i] = that.digits[i];
}
TBigInteger result = new TBigInteger(1, resLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
/** @return sign = 1, magnitude = -(positive.magnitude ^ -negative.magnitude)*/
static TBigInteger xorDiffSigns(TBigInteger positive, TBigInteger negative){
int resLength = Math.max(negative.numberLength, positive.numberLength);
int resDigits[];
int iNeg = negative.getFirstNonzeroDigit();
int iPos = positive.getFirstNonzeroDigit();
int i;
int limit;
//The first
if (iNeg < iPos) {
resDigits = new int[resLength];
i = iNeg;
//resDigits[i] = -(-negative.digits[i]);
resDigits[i] = negative.digits[i];
limit = Math.min(negative.numberLength, iPos);
//Skip the positive digits while they are zeros
for (i++; i < limit; i++) {
//resDigits[i] = ~(~negative.digits[i]);
resDigits[i] = negative.digits[i];
}
//if the negative has no more elements, must fill the
//result with the remaining digits of the positive
if (i == negative.numberLength) {
for ( ; i < positive.numberLength; i++) {
//resDigits[i] = ~(positive.digits[i] ^ -1) -> ~(~positive.digits[i])
resDigits[i] = positive.digits[i];
}
}
} else if (iPos < iNeg) {
resDigits = new int[resLength];
i = iPos;
//Applying two complement to the first non-zero digit of the result
resDigits[i] = -positive.digits[i];
limit = Math.min(positive.numberLength, iNeg);
for (i++; i < limit; i++) {
//Continue applying two complement the result
resDigits[i] = ~positive.digits[i];
}
//When the first non-zero digit of the negative is reached, must apply
//two complement (arithmetic negation) to it, and then operate
if (i == iNeg) {
resDigits[i] = ~(positive.digits[i] ^ -negative.digits[i]);
i++;
} else {
//if the positive has no more elements must fill the remaining digits with
//the negative ones
for ( ; i < iNeg; i++) {
// resDigits[i] = ~(0 ^ 0)
resDigits[i] = -1;
}
for ( ; i < negative.numberLength; i++) {
//resDigits[i] = ~(~negative.digits[i] ^ 0)
resDigits[i] = negative.digits[i];
}
}
} else {
int digit;
//The first non-zero digit of the positive and negative are the same
i = iNeg;
digit = positive.digits[i] ^ -negative.digits[i];
if (digit == 0) {
limit = Math.min(positive.numberLength, negative.numberLength);
for (i++; i < limit && (digit = positive.digits[i] ^ ~negative.digits[i]) == 0; i++) {
// do nothing
}
if (digit == 0) {
// shorter has only the remaining virtual sign bits
for ( ; i < positive.numberLength && (digit = ~positive.digits[i]) == 0; i++) {
// do nothing
}
for ( ; i < negative.numberLength && (digit = ~negative.digits[i]) == 0; i++) {
// do nothing
}
if (digit == 0) {
resLength = resLength + 1;
resDigits = new int[resLength];
resDigits[resLength - 1] = 1;
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
return result;
}
}
}
resDigits = new int[resLength];
resDigits[i] = -digit;
i++;
}
limit = Math.min(negative.numberLength, positive.numberLength);
for ( ; i < limit; i++) {
resDigits[i] = ~(~negative.digits[i] ^ positive.digits[i]);
}
for ( ; i < positive.numberLength; i++) {
// resDigits[i] = ~(positive.digits[i] ^ -1)
resDigits[i] = positive.digits[i];
}
for ( ; i < negative.numberLength; i++) {
// resDigits[i] = ~(0 ^ ~negative.digits[i])
resDigits[i] = negative.digits[i];
}
TBigInteger result = new TBigInteger(-1, resLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
}

View File

@ -0,0 +1,290 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.math;
import java.io.Serializable;
/**
* Immutable objects describing settings such as rounding mode and digit
* precision for the numerical operations provided by class {@link TBigDecimal}.
*/
public final class TMathContext implements Serializable {
/**
* A {@code MathContext} which corresponds to the IEEE 754r quadruple
* decimal precision format: 34 digit precision and
* {@link TRoundingMode#HALF_EVEN} rounding.
*/
public static final TMathContext DECIMAL128 = new TMathContext(34,
TRoundingMode.HALF_EVEN);
/**
* A {@code MathContext} which corresponds to the IEEE 754r single decimal
* precision format: 7 digit precision and {@link TRoundingMode#HALF_EVEN}
* rounding.
*/
public static final TMathContext DECIMAL32 = new TMathContext(7,
TRoundingMode.HALF_EVEN);
/**
* A {@code MathContext} which corresponds to the IEEE 754r double decimal
* precision format: 16 digit precision and {@link TRoundingMode#HALF_EVEN}
* rounding.
*/
public static final TMathContext DECIMAL64 = new TMathContext(16,
TRoundingMode.HALF_EVEN);
/**
* A {@code MathContext} for unlimited precision with
* {@link TRoundingMode#HALF_UP} rounding.
*/
public static final TMathContext UNLIMITED = new TMathContext(0,
TRoundingMode.HALF_UP);
/** This is the serialVersionUID used by the sun implementation */
private static final long serialVersionUID = 5579720004786848255L;
/**
* The number of digits to be used for an operation; results are rounded to
* this precision.
*/
private int precision;
/**
* A {@code RoundingMode} object which specifies the algorithm to be used
* for rounding.
*/
private TRoundingMode roundingMode;
/**
* An array of {@code char} containing: {@code
* 'p','r','e','c','i','s','i','o','n','='}. It's used to improve the
* methods related to {@code String} conversion.
*
* @see #MathContext(String)
* @see #toString()
*/
private final static char[] chPrecision = { 'p', 'r', 'e', 'c', 'i', 's',
'i', 'o', 'n', '=' };
/**
* An array of {@code char} containing: {@code
* 'r','o','u','n','d','i','n','g','M','o','d','e','='}. It's used to
* improve the methods related to {@code String} conversion.
*
* @see #MathContext(String)
* @see #toString()
*/
private final static char[] chRoundingMode = { 'r', 'o', 'u', 'n', 'd',
'i', 'n', 'g', 'M', 'o', 'd', 'e', '=' };
/**
* Constructs a new {@code MathContext} with the specified precision and
* with the rounding mode {@link TRoundingMode#HALF_UP HALF_UP}. If the
* precision passed is zero, then this implies that the computations have to
* be performed exact, the rounding mode in this case is irrelevant.
*
* @param precision
* the precision for the new {@code MathContext}.
* @throws IllegalArgumentException
* if {@code precision < 0}.
*/
public TMathContext(int precision) {
this(precision, TRoundingMode.HALF_UP);
}
/**
* Constructs a new {@code MathContext} with the specified precision and
* with the specified rounding mode. If the precision passed is zero, then
* this implies that the computations have to be performed exact, the
* rounding mode in this case is irrelevant.
*
* @param precision
* the precision for the new {@code MathContext}.
* @param roundingMode
* the rounding mode for the new {@code MathContext}.
* @throws IllegalArgumentException
* if {@code precision < 0}.
* @throws NullPointerException
* if {@code roundingMode} is {@code null}.
*/
public TMathContext(int precision, TRoundingMode roundingMode) {
if (precision < 0) {
throw new IllegalArgumentException("Digits < 0");
}
if (roundingMode == null) {
throw new NullPointerException("null RoundingMode");
}
this.precision = precision;
this.roundingMode = roundingMode;
}
/**
* Constructs a new {@code MathContext} from a string. The string has to
* specify the precision and the rounding mode to be used and has to follow
* the following syntax: "precision=&lt;precision&gt; roundingMode=&lt;roundingMode&gt;"
* This is the same form as the one returned by the {@link #toString}
* method.
*
* @param val
* a string describing the precision and rounding mode for the
* new {@code MathContext}.
* @throws IllegalArgumentException
* if the string is not in the correct format or if the
* precision specified is < 0.
*/
public TMathContext(String val) {
char[] charVal = val.toCharArray();
int i; // Index of charVal
int j; // Index of chRoundingMode
int digit; // It will contain the digit parsed
if ((charVal.length < 27) || (charVal.length > 45)) {
throw new IllegalArgumentException("bad string format");
}
// Parsing "precision=" String
for (i = 0; (i < chPrecision.length) && (charVal[i] == chPrecision[i]); i++) {
// do nothing
}
if (i < chPrecision.length) {
throw new IllegalArgumentException("bad string format");
}
// Parsing the value for "precision="...
digit = Character.digit(charVal[i], 10);
if (digit == -1) {
throw new IllegalArgumentException("bad string format");
}
this.precision = this.precision * 10 + digit;
i++;
do {
digit = Character.digit(charVal[i], 10);
if (digit == -1) {
if (charVal[i] == ' ') {
// It parsed all the digits
i++;
break;
}
// It isn't a valid digit, and isn't a white space
throw new IllegalArgumentException("bad string format");
}
// Accumulating the value parsed
this.precision = this.precision * 10 + digit;
if (this.precision < 0) {
throw new IllegalArgumentException("bad string format");
}
i++;
} while (true);
// Parsing "roundingMode="
for (j = 0; (j < chRoundingMode.length) && (charVal[i] == chRoundingMode[j]); i++, j++) {
// do nothing
}
if (j < chRoundingMode.length) {
throw new IllegalArgumentException("bad string format");
}
// Parsing the value for "roundingMode"...
this.roundingMode = TRoundingMode.valueOf(String.valueOf(charVal, i, charVal.length - i));
}
/* Public Methods */
/**
* Returns the precision. The precision is the number of digits used for an
* operation. Results are rounded to this precision. The precision is
* guaranteed to be non negative. If the precision is zero, then the
* computations have to be performed exact, results are not rounded in this
* case.
*
* @return the precision.
*/
public int getPrecision() {
return precision;
}
/**
* Returns the rounding mode. The rounding mode is the strategy to be used
* to round results.
* <p>
* The rounding mode is one of
* {@link TRoundingMode#UP},
* {@link TRoundingMode#DOWN},
* {@link TRoundingMode#CEILING},
* {@link TRoundingMode#FLOOR},
* {@link TRoundingMode#HALF_UP},
* {@link TRoundingMode#HALF_DOWN},
* {@link TRoundingMode#HALF_EVEN}, or
* {@link TRoundingMode#UNNECESSARY}.
*
* @return the rounding mode.
*/
public TRoundingMode getRoundingMode() {
return roundingMode;
}
/**
* Returns true if x is a {@code MathContext} with the same precision
* setting and the same rounding mode as this {@code MathContext} instance.
*
* @param x
* object to be compared.
* @return {@code true} if this {@code MathContext} instance is equal to the
* {@code x} argument; {@code false} otherwise.
*/
@Override
public boolean equals(Object x) {
return ((x instanceof TMathContext)
&& (((TMathContext) x).getPrecision() == precision) && (((TMathContext) x)
.getRoundingMode() == roundingMode));
}
/**
* Returns the hash code for this {@code MathContext} instance.
*
* @return the hash code for this {@code MathContext}.
*/
@Override
public int hashCode() {
// Make place for the necessary bits to represent 8 rounding modes
return ((precision << 3) | roundingMode.ordinal());
}
/**
* Returns the string representation for this {@code MathContext} instance.
* The string has the form
* {@code
* "precision=&lt;precision&gt; roundingMode=&lt;roundingMode&gt;"
* } where {@code &lt;precision&gt;} is an integer describing the number
* of digits used for operations and {@code &lt;roundingMode&gt;} is the
* string representation of the rounding mode.
*
* @return a string representation for this {@code MathContext} instance
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder(45);
sb.append(chPrecision);
sb.append(precision);
sb.append(' ');
sb.append(chRoundingMode);
sb.append(roundingMode);
return sb.toString();
}
}

View File

@ -0,0 +1,510 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.math;
/**
* Static library that provides all multiplication of {@link TBigInteger} methods.
*/
class TMultiplication {
/** Just to denote that this class can't be instantiated. */
private TMultiplication() {}
/**
* Break point in digits (number of {@code int} elements)
* between Karatsuba and Pencil and Paper multiply.
*/
static final int whenUseKaratsuba = 63; // an heuristic value
/**
* An array with powers of ten that fit in the type {@code int}.
* ({@code 10^0,10^1,...,10^9})
*/
static final int tenPows[] = {
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
};
/**
* An array with powers of five that fit in the type {@code int}.
* ({@code 5^0,5^1,...,5^13})
*/
static final int fivePows[] = {
1, 5, 25, 125, 625, 3125, 15625, 78125, 390625,
1953125, 9765625, 48828125, 244140625, 1220703125
};
/**
* An array with the first powers of ten in {@code BigInteger} version.
* ({@code 10^0,10^1,...,10^31})
*/
static final TBigInteger[] bigTenPows = new TBigInteger[32];
/**
* An array with the first powers of five in {@code BigInteger} version.
* ({@code 5^0,5^1,...,5^31})
*/
static final TBigInteger bigFivePows[] = new TBigInteger[32];
static {
int i;
long fivePow = 1L;
for (i = 0; i <= 18; i++) {
bigFivePows[i] = TBigInteger.valueOf(fivePow);
bigTenPows[i] = TBigInteger.valueOf(fivePow << i);
fivePow *= 5;
}
for (; i < bigTenPows.length; i++) {
bigFivePows[i] = bigFivePows[i - 1].multiply(bigFivePows[1]);
bigTenPows[i] = bigTenPows[i - 1].multiply(TBigInteger.TEN);
}
}
/**
* Performs a multiplication of two BigInteger and hides the algorithm used.
* @see TBigInteger#multiply(TBigInteger)
*/
static TBigInteger multiply(TBigInteger x, TBigInteger y) {
return karatsuba(x, y);
}
/**
* Performs the multiplication with the Karatsuba's algorithm.
* <b>Karatsuba's algorithm:</b>
*<tt>
* u = u<sub>1</sub> * B + u<sub>0</sub><br>
* v = v<sub>1</sub> * B + v<sub>0</sub><br>
*
*
* u*v = (u<sub>1</sub> * v<sub>1</sub>) * B<sub>2</sub> + ((u<sub>1</sub> - u<sub>0</sub>) * (v<sub>0</sub> - v<sub>1</sub>) + u<sub>1</sub> * v<sub>1</sub> +
* u<sub>0</sub> * v<sub>0</sub> ) * B + u<sub>0</sub> * v<sub>0</sub><br>
*</tt>
* @param op1 first factor of the product
* @param op2 second factor of the product
* @return {@code op1 * op2}
* @see #multiply(TBigInteger, TBigInteger)
*/
static TBigInteger karatsuba(TBigInteger op1, TBigInteger op2) {
TBigInteger temp;
if (op2.numberLength > op1.numberLength) {
temp = op1;
op1 = op2;
op2 = temp;
}
if (op2.numberLength < whenUseKaratsuba) {
return multiplyPAP(op1, op2);
}
/* Karatsuba: u = u1*B + u0
* v = v1*B + v0
* u*v = (u1*v1)*B^2 + ((u1-u0)*(v0-v1) + u1*v1 + u0*v0)*B + u0*v0
*/
// ndiv2 = (op1.numberLength / 2) * 32
int ndiv2 = (op1.numberLength & 0xFFFFFFFE) << 4;
TBigInteger upperOp1 = op1.shiftRight(ndiv2);
TBigInteger upperOp2 = op2.shiftRight(ndiv2);
TBigInteger lowerOp1 = op1.subtract(upperOp1.shiftLeft(ndiv2));
TBigInteger lowerOp2 = op2.subtract(upperOp2.shiftLeft(ndiv2));
TBigInteger upper = karatsuba(upperOp1, upperOp2);
TBigInteger lower = karatsuba(lowerOp1, lowerOp2);
TBigInteger middle = karatsuba( upperOp1.subtract(lowerOp1),
lowerOp2.subtract(upperOp2));
middle = middle.add(upper).add(lower);
middle = middle.shiftLeft(ndiv2);
upper = upper.shiftLeft(ndiv2 << 1);
return upper.add(middle).add(lower);
}
/**
* Multiplies two BigIntegers.
* Implements traditional scholar algorithm described by Knuth.
*
* <br><tt>
* <table border="0">
* <tbody>
*
*
* <tr>
* <td align="center">A=</td>
* <td>a<sub>3</sub></td>
* <td>a<sub>2</sub></td>
* <td>a<sub>1</sub></td>
* <td>a<sub>0</sub></td>
* <td></td>
* <td></td>
* </tr>
*
*<tr>
* <td align="center">B=</td>
* <td></td>
* <td>b<sub>2</sub></td>
* <td>b<sub>1</sub></td>
* <td>b<sub>1</sub></td>
* <td></td>
* <td></td>
* </tr>
*
* <tr>
* <td></td>
* <td></td>
* <td></td>
* <td>b<sub>0</sub>*a<sub>3</sub></td>
* <td>b<sub>0</sub>*a<sub>2</sub></td>
* <td>b<sub>0</sub>*a<sub>1</sub></td>
* <td>b<sub>0</sub>*a<sub>0</sub></td>
* </tr>
*
* <tr>
* <td></td>
* <td></td>
* <td>b<sub>1</sub>*a<sub>3</sub></td>
* <td>b<sub>1</sub>*a<sub>2</sub></td>
* <td>b<sub>1</sub>*a1</td>
* <td>b<sub>1</sub>*a0</td>
* </tr>
*
* <tr>
* <td>+</td>
* <td>b<sub>2</sub>*a<sub>3</sub></td>
* <td>b<sub>2</sub>*a<sub>2</sub></td>
* <td>b<sub>2</sub>*a<sub>1</sub></td>
* <td>b<sub>2</sub>*a<sub>0</sub></td>
* </tr>
*
*<tr>
* <td></td>
*<td>______</td>
* <td>______</td>
* <td>______</td>
* <td>______</td>
* <td>______</td>
* <td>______</td>
*</tr>
*
* <tr>
*
* <td align="center">A*B=R=</td>
* <td align="center">r<sub>5</sub></td>
* <td align="center">r<sub>4</sub></td>
* <td align="center">r<sub>3</sub></td>
* <td align="center">r<sub>2</sub></td>
* <td align="center">r<sub>1</sub></td>
* <td align="center">r<sub>0</sub></td>
* <td></td>
* </tr>
*
* </tbody>
* </table>
*
*</tt>
*
* @param op1 first factor of the multiplication {@code op1 >= 0}
* @param op2 second factor of the multiplication {@code op2 >= 0}
* @return a {@code BigInteger} of value {@code op1 * op2}
*/
static TBigInteger multiplyPAP(TBigInteger a, TBigInteger b) {
// PRE: a >= b
int aLen = a.numberLength;
int bLen = b.numberLength;
int resLength = aLen + bLen;
int resSign = (a.sign != b.sign) ? -1 : 1;
// A special case when both numbers don't exceed int
if (resLength == 2) {
long val = unsignedMultAddAdd(a.digits[0], b.digits[0], 0, 0);
int valueLo = (int)val;
int valueHi = (int)(val >>> 32);
return ((valueHi == 0)
? new TBigInteger(resSign, valueLo)
: new TBigInteger(resSign, 2, new int[]{valueLo, valueHi}));
}
int[] aDigits = a.digits;
int[] bDigits = b.digits;
int resDigits[] = new int[resLength];
// Common case
multArraysPAP(aDigits, aLen, bDigits, bLen, resDigits);
TBigInteger result = new TBigInteger(resSign, resLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
static void multArraysPAP(int[] aDigits, int aLen, int[] bDigits, int bLen, int[] resDigits) {
if(aLen == 0 || bLen == 0) return;
if(aLen == 1) {
resDigits[bLen] = multiplyByInt(resDigits, bDigits, bLen, aDigits[0]);
} else if(bLen == 1) {
resDigits[aLen] = multiplyByInt(resDigits, aDigits, aLen, bDigits[0]);
} else {
multPAP(aDigits, bDigits, resDigits, aLen, bLen);
}
}
static void multPAP(int a[], int b[], int t[], int aLen, int bLen) {
if(a == b && aLen == bLen) {
square(a, aLen, t);
return;
}
for(int i = 0; i < aLen; i++){
long carry = 0;
int aI = a[i];
for (int j = 0; j < bLen; j++){
carry = unsignedMultAddAdd(aI, b[j], t[i+j], (int)carry);
t[i+j] = (int) carry;
carry >>>= 32;
}
t[i+bLen] = (int) carry;
}
}
/**
* Multiplies an array of integers by an integer value
* and saves the result in {@code res}.
* @param a the array of integers
* @param aSize the number of elements of intArray to be multiplied
* @param factor the multiplier
* @return the top digit of production
*/
private static int multiplyByInt(int res[], int a[], final int aSize, final int factor) {
long carry = 0;
for (int i = 0; i < aSize; i++) {
carry = unsignedMultAddAdd(a[i], factor, (int)carry, 0);
res[i] = (int)carry;
carry >>>= 32;
}
return (int)carry;
}
/**
* Multiplies an array of integers by an integer value.
* @param a the array of integers
* @param aSize the number of elements of intArray to be multiplied
* @param factor the multiplier
* @return the top digit of production
*/
static int multiplyByInt(int a[], final int aSize, final int factor) {
return multiplyByInt(a, a, aSize, factor);
}
/**
* Multiplies a number by a positive integer.
* @param val an arbitrary {@code BigInteger}
* @param factor a positive {@code int} number
* @return {@code val * factor}
*/
static TBigInteger multiplyByPositiveInt(TBigInteger val, int factor) {
int resSign = val.sign;
if (resSign == 0) {
return TBigInteger.ZERO;
}
int aNumberLength = val.numberLength;
int[] aDigits = val.digits;
if (aNumberLength == 1) {
long res = unsignedMultAddAdd(aDigits[0], factor, 0, 0);
int resLo = (int)res;
int resHi = (int)(res >>> 32);
return ((resHi == 0)
? new TBigInteger(resSign, resLo)
: new TBigInteger(resSign, 2, new int[]{resLo, resHi}));
}
// Common case
int resLength = aNumberLength + 1;
int resDigits[] = new int[resLength];
resDigits[aNumberLength] = multiplyByInt(resDigits, aDigits, aNumberLength, factor);
TBigInteger result = new TBigInteger(resSign, resLength, resDigits);
result.cutOffLeadingZeroes();
return result;
}
static TBigInteger pow(TBigInteger base, int exponent) {
// PRE: exp > 0
TBigInteger res = TBigInteger.ONE;
TBigInteger acc = base;
for (; exponent > 1; exponent >>= 1) {
if ((exponent & 1) != 0) {
// if odd, multiply one more time by acc
res = res.multiply(acc);
}
// acc = base^(2^i)
//a limit where karatsuba performs a faster square than the square algorithm
if ( acc.numberLength == 1 ){
acc = acc.multiply(acc); // square
}
else{
acc = new TBigInteger(1, square(acc.digits, acc.numberLength, new int [acc.numberLength<<1]));
}
}
// exponent == 1, multiply one more time
res = res.multiply(acc);
return res;
}
/**
* Performs a<sup>2</sup>
* @param a The number to square.
* @param aLen The length of the number to square.
*/
static int[] square(int[] a, int aLen, int[] res) {
long carry;
for(int i = 0; i < aLen; i++){
carry = 0;
for (int j = i+1; j < aLen; j++){
carry = unsignedMultAddAdd(a[i], a[j], res[i+j], (int)carry);
res[i+j] = (int) carry;
carry >>>= 32;
}
res[i+aLen] = (int) carry;
}
TBitLevel.shiftLeftOneBit(res, res, aLen << 1);
carry = 0;
for(int i = 0, index = 0; i < aLen; i++, index++){
carry = unsignedMultAddAdd(a[i], a[i], res[index],(int)carry);
res[index] = (int) carry;
carry >>>= 32;
index++;
carry += res[index] & 0xFFFFFFFFL;
res[index] = (int)carry;
carry >>>= 32;
}
return res;
}
/**
* Multiplies a number by a power of ten.
* This method is used in {@code BigDecimal} class.
* @param val the number to be multiplied
* @param exp a positive {@code long} exponent
* @return {@code val * 10<sup>exp</sup>}
*/
static TBigInteger multiplyByTenPow(TBigInteger val, long exp) {
// PRE: exp >= 0
return ((exp < tenPows.length)
? multiplyByPositiveInt(val, tenPows[(int)exp])
: val.multiply(powerOf10(exp)));
}
/**
* It calculates a power of ten, which exponent could be out of 32-bit range.
* Note that internally this method will be used in the worst case with
* an exponent equals to: {@code Integer.MAX_VALUE - Integer.MIN_VALUE}.
* @param exp the exponent of power of ten, it must be positive.
* @return a {@code BigInteger} with value {@code 10<sup>exp</sup>}.
*/
static TBigInteger powerOf10(long exp) {
// PRE: exp >= 0
int intExp = (int)exp;
// "SMALL POWERS"
if (exp < bigTenPows.length) {
// The largest power that fit in 'long' type
return bigTenPows[intExp];
} else if (exp <= 50) {
// To calculate: 10^exp
return TBigInteger.TEN.pow(intExp);
} else if (exp <= 1000) {
// To calculate: 5^exp * 2^exp
return bigFivePows[1].pow(intExp).shiftLeft(intExp);
}
// "LARGE POWERS"
/*
* To check if there is free memory to allocate a BigInteger of the
* estimated size, measured in bytes: 1 + [exp / log10(2)]
*/
long byteArraySize = 1 + (long)(exp / 2.4082399653118496);
if (byteArraySize > 1000000) {
throw new ArithmeticException("power of ten too big");
}
if (exp <= Integer.MAX_VALUE) {
// To calculate: 5^exp * 2^exp
return bigFivePows[1].pow(intExp).shiftLeft(intExp);
}
/*
* "HUGE POWERS"
*
* This branch probably won't be executed since the power of ten is too
* big.
*/
// To calculate: 5^exp
TBigInteger powerOfFive = bigFivePows[1].pow(Integer.MAX_VALUE);
TBigInteger res = powerOfFive;
long longExp = exp - Integer.MAX_VALUE;
intExp = (int)(exp % Integer.MAX_VALUE);
while (longExp > Integer.MAX_VALUE) {
res = res.multiply(powerOfFive);
longExp -= Integer.MAX_VALUE;
}
res = res.multiply(bigFivePows[1].pow(intExp));
// To calculate: 5^exp << exp
res = res.shiftLeft(Integer.MAX_VALUE);
longExp = exp - Integer.MAX_VALUE;
while (longExp > Integer.MAX_VALUE) {
res = res.shiftLeft(Integer.MAX_VALUE);
longExp -= Integer.MAX_VALUE;
}
res = res.shiftLeft(intExp);
return res;
}
/**
* Multiplies a number by a power of five.
* This method is used in {@code BigDecimal} class.
* @param val the number to be multiplied
* @param exp a positive {@code int} exponent
* @return {@code val * 5<sup>exp</sup>}
*/
static TBigInteger multiplyByFivePow(TBigInteger val, int exp) {
// PRE: exp >= 0
if (exp < fivePows.length) {
return multiplyByPositiveInt(val, fivePows[exp]);
} else if (exp < bigFivePows.length) {
return val.multiply(bigFivePows[exp]);
} else {// Large powers of five
return val.multiply(bigFivePows[1].pow(exp));
}
}
/**
* Computes the value unsigned ((uint)a*(uint)b + (uint)c + (uint)d). This
* method could improve the readability and performance of the code.
*
* @param a
* parameter 1
* @param b
* parameter 2
* @param c
* parameter 3
* @param d
* parameter 4
* @return value of expression
*/
static long unsignedMultAddAdd(int a, int b, int c, int d) {
return (a & 0xFFFFFFFFL) * (b & 0xFFFFFFFFL) + (c & 0xFFFFFFFFL) + (d & 0xFFFFFFFFL);
}
}

View File

@ -0,0 +1,270 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.math;
import java.util.Arrays;
import java.util.Random;
/**
* Provides primality probabilistic methods.
*/
class TPrimality {
/** Just to denote that this class can't be instantiated. */
private TPrimality() {
}
/** All prime numbers with bit length lesser than 10 bits. */
private static final int primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181,
191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307,
311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433,
439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571,
577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853,
857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997,
1009, 1013, 1019, 1021 };
/** All {@code BigInteger} prime numbers with bit length lesser than 8 bits. */
private static final TBigInteger BIprimes[] = new TBigInteger[primes.length];
/**
* It encodes how many iterations of Miller-Rabin test are need to get an
* error bound not greater than {@code 2<sup>(-100)</sup>}. For example: for
* a {@code 1000}-bit number we need {@code 4} iterations, since
* {@code BITS[3] < 1000 <= BITS[4]}.
*/
private static final int[] BITS = { 0, 0, 1854, 1233, 927, 747, 627, 543, 480, 431, 393, 361, 335, 314, 295, 279,
265, 253, 242, 232, 223, 216, 181, 169, 158, 150, 145, 140, 136, 132, 127, 123, 119, 114, 110, 105, 101,
96, 92, 87, 83, 78, 73, 69, 64, 59, 54, 49, 44, 38, 32, 26, 1 };
/**
* It encodes how many i-bit primes there are in the table for
* {@code i=2,...,10}. For example {@code offsetPrimes[6]} says that from
* index {@code 11} exists {@code 7} consecutive {@code 6}-bit prime numbers
* in the array.
*/
private static final int[][] offsetPrimes = { null, null, { 0, 2 }, { 2, 2 }, { 4, 2 }, { 6, 5 }, { 11, 7 },
{ 18, 13 }, { 31, 23 }, { 54, 43 }, { 97, 75 } };
static {// To initialize the dual table of BigInteger primes
for (int i = 0; i < primes.length; i++) {
BIprimes[i] = TBigInteger.valueOf(primes[i]);
}
}
/**
* It uses the sieve of Eratosthenes to discard several composite numbers in
* some appropriate range (at the moment {@code [this, this + 1024]}). After
* this process it applies the Miller-Rabin test to the numbers that were
* not discarded in the sieve.
*
* @see TBigInteger#nextProbablePrime()
* @see #millerRabin(TBigInteger, int)
*/
static TBigInteger nextProbablePrime(TBigInteger n) {
// PRE: n >= 0
int i, j;
int certainty;
int gapSize = 1024; // for searching of the next probable prime number
int modules[] = new int[primes.length];
boolean isDivisible[] = new boolean[gapSize];
TBigInteger startPoint;
TBigInteger probPrime;
// If n < "last prime of table" searches next prime in the table
if ((n.numberLength == 1) && (n.digits[0] >= 0) && (n.digits[0] < primes[primes.length - 1])) {
for (i = 0; n.digits[0] >= primes[i]; i++) {
// do nothing
}
return BIprimes[i];
}
/*
* Creates a "N" enough big to hold the next probable prime Note that: N
* < "next prime" < 2*N
*/
startPoint = new TBigInteger(1, n.numberLength, new int[n.numberLength + 1]);
System.arraycopy(n.digits, 0, startPoint.digits, 0, n.numberLength);
// To fix N to the "next odd number"
if (n.testBit(0)) {
TElementary.inplaceAdd(startPoint, 2);
} else {
startPoint.digits[0] |= 1;
}
// To set the improved certainly of Miller-Rabin
j = startPoint.bitLength();
for (certainty = 2; j < BITS[certainty]; certainty++) {
// do nothing
}
// To calculate modules: N mod p1, N mod p2, ... for first primes.
for (i = 0; i < primes.length; i++) {
modules[i] = TDivision.remainder(startPoint, primes[i]) - gapSize;
}
while (true) {
// At this point, all numbers in the gap are initialized as
// probably primes
Arrays.fill(isDivisible, false);
// To discard multiples of first primes
for (i = 0; i < primes.length; i++) {
modules[i] = (modules[i] + gapSize) % primes[i];
j = (modules[i] == 0) ? 0 : (primes[i] - modules[i]);
for (; j < gapSize; j += primes[i]) {
isDivisible[j] = true;
}
}
// To execute Miller-Rabin for non-divisible numbers by all first
// primes
for (j = 0; j < gapSize; j++) {
if (!isDivisible[j]) {
probPrime = startPoint.copy();
TElementary.inplaceAdd(probPrime, j);
if (millerRabin(probPrime, certainty)) {
return probPrime;
}
}
}
TElementary.inplaceAdd(startPoint, gapSize);
}
}
/**
* A random number is generated until a probable prime number is found.
*
* @see TBigInteger#BigInteger(int,int,Random)
* @see TBigInteger#probablePrime(int,Random)
* @see #isProbablePrime(TBigInteger, int)
*/
static TBigInteger consBigInteger(int bitLength, int certainty, Random rnd) {
// PRE: bitLength >= 2;
// For small numbers get a random prime from the prime table
if (bitLength <= 10) {
int rp[] = offsetPrimes[bitLength];
return BIprimes[rp[0] + rnd.nextInt(rp[1])];
}
int shiftCount = (-bitLength) & 31;
int last = (bitLength + 31) >> 5;
TBigInteger n = new TBigInteger(1, last, new int[last]);
last--;
do {// To fill the array with random integers
for (int i = 0; i < n.numberLength; i++) {
n.digits[i] = rnd.nextInt();
}
// To fix to the correct bitLength
n.digits[last] |= 0x80000000;
n.digits[last] >>>= shiftCount;
// To create an odd number
n.digits[0] |= 1;
} while (!isProbablePrime(n, certainty));
return n;
}
/**
* @see TBigInteger#isProbablePrime(int)
* @see #millerRabin(TBigInteger, int)
* @ar.org.fitc.ref Optimizations: "A. Menezes - Handbook of applied
* Cryptography, Chapter 4".
*/
static boolean isProbablePrime(TBigInteger n, int certainty) {
// PRE: n >= 0;
if ((certainty <= 0) || ((n.numberLength == 1) && (n.digits[0] == 2))) {
return true;
}
// To discard all even numbers
if (!n.testBit(0)) {
return false;
}
// To check if 'n' exists in the table (it fit in 10 bits)
if ((n.numberLength == 1) && ((n.digits[0] & 0XFFFFFC00) == 0)) {
return (Arrays.binarySearch(primes, n.digits[0]) >= 0);
}
// To check if 'n' is divisible by some prime of the table
for (int i = 1; i < primes.length; i++) {
if (TDivision.remainderArrayByInt(n.digits, n.numberLength, primes[i]) == 0) {
return false;
}
}
// To set the number of iterations necessary for Miller-Rabin test
int i;
int bitLength = n.bitLength();
for (i = 2; bitLength < BITS[i]; i++) {
// do nothing
}
certainty = Math.min(i, 1 + ((certainty - 1) >> 1));
return millerRabin(n, certainty);
}
/**
* The Miller-Rabin primality test.
*
* @param n
* the input number to be tested.
* @param t
* the number of trials.
* @return {@code false} if the number is definitely compose, otherwise
* {@code true} with probability {@code 1 - 4<sup>(-t)</sup>}.
* @ar.org.fitc.ref "D. Knuth, The Art of Computer Programming Vo.2, Section
* 4.5.4., Algorithm P"
*/
private static boolean millerRabin(TBigInteger n, int t) {
// PRE: n >= 0, t >= 0
TBigInteger x; // x := UNIFORM{2...n-1}
TBigInteger y; // y := x^(q * 2^j) mod n
TBigInteger n_minus_1 = n.subtract(TBigInteger.ONE); // n-1
int bitLength = n_minus_1.bitLength(); // ~ log2(n-1)
// (q,k) such that: n-1 = q * 2^k and q is odd
int k = n_minus_1.getLowestSetBit();
TBigInteger q = n_minus_1.shiftRight(k);
Random rnd = new Random();
for (int i = 0; i < t; i++) {
// To generate a witness 'x', first it use the primes of table
if (i < primes.length) {
x = BIprimes[i];
} else {/*
* It generates random witness only if it's necesssary. Note
* that all methods would call Miller-Rabin with t <= 50 so
* this part is only to do more robust the algorithm
*/
do {
x = new TBigInteger(bitLength, rnd);
} while ((x.compareTo(n) >= TBigInteger.EQUALS) || (x.sign == 0) || x.isOne());
}
y = x.modPow(q, n);
if (y.isOne() || y.equals(n_minus_1)) {
continue;
}
for (int j = 1; j < k; j++) {
if (y.equals(n_minus_1)) {
continue;
}
y = y.multiply(y).mod(n);
if (y.isOne()) {
return false;
}
}
if (!y.equals(n_minus_1)) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,123 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.math;
/**
* Specifies the rounding behavior for operations whose results cannot be
* represented exactly.
*/
public enum TRoundingMode {
/**
* Rounding mode where positive values are rounded towards positive infinity
* and negative values towards negative infinity.
* <br>
* Rule: {@code x.round().abs() >= x.abs()}
*/
UP(TBigDecimal.ROUND_UP),
/**
* Rounding mode where the values are rounded towards zero.
* <br>
* Rule: {@code x.round().abs() <= x.abs()}
*/
DOWN(TBigDecimal.ROUND_DOWN),
/**
* Rounding mode to round towards positive infinity. For positive values
* this rounding mode behaves as {@link #UP}, for negative values as
* {@link #DOWN}.
* <br>
* Rule: {@code x.round() >= x}
*/
CEILING(TBigDecimal.ROUND_CEILING),
/**
* Rounding mode to round towards negative infinity. For positive values
* this rounding mode behaves as {@link #DOWN}, for negative values as
* {@link #UP}.
* <br>
* Rule: {@code x.round() <= x}
*/
FLOOR(TBigDecimal.ROUND_FLOOR),
/**
* Rounding mode where values are rounded towards the nearest neighbor. Ties
* are broken by rounding up.
*/
HALF_UP(TBigDecimal.ROUND_HALF_UP),
/**
* Rounding mode where values are rounded towards the nearest neighbor. Ties
* are broken by rounding down.
*/
HALF_DOWN(TBigDecimal.ROUND_HALF_DOWN),
/**
* Rounding mode where values are rounded towards the nearest neighbor. Ties
* are broken by rounding to the even neighbor.
*/
HALF_EVEN(TBigDecimal.ROUND_HALF_EVEN),
/**
* Rounding mode where the rounding operations throws an ArithmeticException
* for the case that rounding is necessary, i.e. for the case that the value
* cannot be represented exactly.
*/
UNNECESSARY(TBigDecimal.ROUND_UNNECESSARY);
/** The old constant of <code>BigDecimal</code>. */
@SuppressWarnings("unused")
private final int bigDecimalRM;
/** It sets the old constant. */
TRoundingMode(int rm) {
bigDecimalRM = rm;
}
/**
* Converts rounding mode constants from class {@code BigDecimal} into
* {@code RoundingMode} values.
*
* @param mode
* rounding mode constant as defined in class {@code BigDecimal}
* @return corresponding rounding mode object
*/
public static TRoundingMode valueOf(int mode) {
switch (mode) {
case TBigDecimal.ROUND_CEILING:
return CEILING;
case TBigDecimal.ROUND_DOWN:
return DOWN;
case TBigDecimal.ROUND_FLOOR:
return FLOOR;
case TBigDecimal.ROUND_HALF_DOWN:
return HALF_DOWN;
case TBigDecimal.ROUND_HALF_EVEN:
return HALF_EVEN;
case TBigDecimal.ROUND_HALF_UP:
return HALF_UP;
case TBigDecimal.ROUND_UNNECESSARY:
return UNNECESSARY;
case TBigDecimal.ROUND_UP:
return UP;
default:
throw new IllegalArgumentException("Invalid rounding mode");
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.text;
public class TAnnotation {
private Object value;
public TAnnotation(Object attribute) {
value = attribute;
}
public Object getValue() {
return value;
}
@Override
public String toString() {
return getClass().getName() + "[value=" + value + ']';
}
}

View File

@ -0,0 +1,144 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.text;
import org.teavm.classlib.java.io.TSerializable;
import org.teavm.classlib.java.util.TMap;
import org.teavm.classlib.java.util.TSet;
public interface TAttributedCharacterIterator extends TCharacterIterator {
public static class Attribute implements TSerializable {
public static final Attribute INPUT_METHOD_SEGMENT = new Attribute(
"input_method_segment");
public static final Attribute LANGUAGE = new Attribute("language");
public static final Attribute READING = new Attribute("reading");
private String name;
protected Attribute(String name) {
this.name = name;
}
@Override
public final boolean equals(Object object) {
return this == object;
}
protected String getName() {
return name;
}
@Override
public final int hashCode() {
return super.hashCode();
}
@Override
public String toString() {
return getClass().getName() + '(' + getName() + ')';
}
}
/**
* Returns a set of attributes present in the {@code
* AttributedCharacterIterator}. An empty set is returned if no attributes
* were defined.
*
* @return a set of attribute keys; may be empty.
*/
public TSet<Attribute> getAllAttributeKeys();
/**
* Returns the value stored in the attribute for the current character. If
* the attribute was not defined then {@code null} is returned.
*
* @param attribute the attribute for which the value should be returned.
* @return the value of the requested attribute for the current character or
* {@code null} if it was not defined.
*/
public Object getAttribute(Attribute attribute);
/**
* Returns a map of all attributes of the current character. If no
* attributes were defined for the current character then an empty map is
* returned.
*
* @return a map of all attributes for the current character or an empty
* map.
*/
public TMap<Attribute, Object> getAttributes();
/**
* Returns the index of the last character in the run having the same
* attributes as the current character.
*
* @return the index of the last character of the current run.
*/
public int getRunLimit();
/**
* Returns the index of the last character in the run that has the same
* attribute value for the given attribute as the current character.
*
* @param attribute
* the attribute which the run is based on.
* @return the index of the last character of the current run.
*/
public int getRunLimit(Attribute attribute);
/**
* Returns the index of the last character in the run that has the same
* attribute values for the attributes in the set as the current character.
*
* @param attributes
* the set of attributes which the run is based on.
* @return the index of the last character of the current run.
*/
public int getRunLimit(TSet<? extends Attribute> attributes);
/**
* Returns the index of the first character in the run that has the same
* attributes as the current character.
*
* @return the index of the last character of the current run.
*/
public int getRunStart();
/**
* Returns the index of the first character in the run that has the same
* attribute value for the given attribute as the current character.
*
* @param attribute
* the attribute which the run is based on.
* @return the index of the last character of the current run.
*/
public int getRunStart(Attribute attribute);
/**
* Returns the index of the first character in the run that has the same
* attribute values for the attributes in the set as the current character.
*
* @param attributes
* the set of attributes which the run is based on.
* @return the index of the last character of the current run.
*/
public int getRunStart(TSet<? extends Attribute> attributes);
}

View File

@ -0,0 +1,630 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.text;
import org.teavm.classlib.java.text.TAttributedCharacterIterator.Attribute;
import org.teavm.classlib.java.util.*;
public class TAttributedString {
String text;
TMap<TAttributedCharacterIterator.Attribute, TList<Range>> attributeMap;
static class Range {
int start;
int end;
Object value;
Range(int s, int e, Object v) {
start = s;
end = e;
value = v;
}
}
static class AttributedIterator implements TAttributedCharacterIterator {
private int begin, end, offset;
private TAttributedString attrString;
private THashSet<Attribute> attributesAllowed;
AttributedIterator(TAttributedString attrString) {
this.attrString = attrString;
begin = 0;
end = attrString.text.length();
offset = 0;
}
AttributedIterator(TAttributedString attrString, TAttributedCharacterIterator.Attribute[] attributes, int begin,
int end) {
if (begin < 0 || end > attrString.text.length() || begin > end) {
throw new IllegalArgumentException();
}
this.begin = begin;
this.end = end;
offset = begin;
this.attrString = attrString;
if (attributes != null) {
THashSet<Attribute> set = new THashSet<>((attributes.length * 4 / 3) + 1);
for (int i = attributes.length; --i >= 0;) {
set.add(attributes[i]);
}
attributesAllowed = set;
}
}
@Override
@SuppressWarnings("unchecked")
public Object clone() {
try {
AttributedIterator clone = (AttributedIterator) super.clone();
if (attributesAllowed != null) {
clone.attributesAllowed = (THashSet<Attribute>) attributesAllowed.clone();
}
return clone;
} catch (CloneNotSupportedException e) {
return null;
}
}
@Override
public char current() {
if (offset == end) {
return DONE;
}
return attrString.text.charAt(offset);
}
@Override
public char first() {
if (begin == end) {
return DONE;
}
offset = begin;
return attrString.text.charAt(offset);
}
@Override
public int getBeginIndex() {
return begin;
}
@Override
public int getEndIndex() {
return end;
}
@Override
public int getIndex() {
return offset;
}
private boolean inRange(Range range) {
if (!(range.value instanceof TAnnotation)) {
return true;
}
return range.start >= begin && range.start < end && range.end > begin && range.end <= end;
}
private boolean inRange(TList<Range> ranges) {
TIterator<Range> it = ranges.iterator();
while (it.hasNext()) {
Range range = it.next();
if (range.start >= begin && range.start < end) {
return !(range.value instanceof TAnnotation) || (range.end > begin && range.end <= end);
} else if (range.end > begin && range.end <= end) {
return !(range.value instanceof TAnnotation) || (range.start >= begin && range.start < end);
}
}
return false;
}
@Override
public TSet<AttributedIterator.Attribute> getAllAttributeKeys() {
if (begin == 0 && end == attrString.text.length() && attributesAllowed == null) {
return attrString.attributeMap.keySet();
}
TSet<AttributedIterator.Attribute> result = new THashSet<>((attrString.attributeMap.size() * 4 / 3) + 1);
TIterator<TMap.Entry<Attribute, TList<Range>>> it = attrString.attributeMap.entrySet().iterator();
while (it.hasNext()) {
TMap.Entry<Attribute, TList<Range>> entry = it.next();
if (attributesAllowed == null || attributesAllowed.contains(entry.getKey())) {
TList<Range> ranges = entry.getValue();
if (inRange(ranges)) {
result.add(entry.getKey());
}
}
}
return result;
}
private Object currentValue(TList<Range> ranges) {
TIterator<Range> it = ranges.iterator();
while (it.hasNext()) {
Range range = it.next();
if (offset >= range.start && offset < range.end) {
return inRange(range) ? range.value : null;
}
}
return null;
}
@Override
public Object getAttribute(TAttributedCharacterIterator.Attribute attribute) {
if (attributesAllowed != null && !attributesAllowed.contains(attribute)) {
return null;
}
TArrayList<Range> ranges = (TArrayList<Range>) attrString.attributeMap.get(attribute);
if (ranges == null) {
return null;
}
return currentValue(ranges);
}
@Override
public TMap<Attribute, Object> getAttributes() {
TMap<Attribute, Object> result = new THashMap<>((attrString.attributeMap.size() * 4 / 3) + 1);
TIterator<TMap.Entry<Attribute, TList<Range>>> it = attrString.attributeMap.entrySet().iterator();
while (it.hasNext()) {
TMap.Entry<Attribute, TList<Range>> entry = it.next();
if (attributesAllowed == null || attributesAllowed.contains(entry.getKey())) {
Object value = currentValue(entry.getValue());
if (value != null) {
result.put(entry.getKey(), value);
}
}
}
return result;
}
@Override
public int getRunLimit() {
return getRunLimit(getAllAttributeKeys());
}
private int runLimit(TList<Range> ranges) {
int result = end;
TListIterator<Range> it = ranges.listIterator(ranges.size());
while (it.hasPrevious()) {
Range range = it.previous();
if (range.end <= begin) {
break;
}
if (offset >= range.start && offset < range.end) {
return inRange(range) ? range.end : result;
} else if (offset >= range.end) {
break;
}
result = range.start;
}
return result;
}
@Override
public int getRunLimit(TAttributedCharacterIterator.Attribute attribute) {
if (attributesAllowed != null && !attributesAllowed.contains(attribute)) {
return end;
}
TArrayList<Range> ranges = (TArrayList<Range>) attrString.attributeMap.get(attribute);
if (ranges == null) {
return end;
}
return runLimit(ranges);
}
@Override
public int getRunLimit(TSet<? extends Attribute> attributes) {
int limit = end;
TIterator<? extends Attribute> it = attributes.iterator();
while (it.hasNext()) {
TAttributedCharacterIterator.Attribute attribute = it.next();
int newLimit = getRunLimit(attribute);
if (newLimit < limit) {
limit = newLimit;
}
}
return limit;
}
@Override
public int getRunStart() {
return getRunStart(getAllAttributeKeys());
}
private int runStart(TList<Range> ranges) {
int result = begin;
TIterator<Range> it = ranges.iterator();
while (it.hasNext()) {
Range range = it.next();
if (range.start >= end) {
break;
}
if (offset >= range.start && offset < range.end) {
return inRange(range) ? range.start : result;
} else if (offset < range.start) {
break;
}
result = range.end;
}
return result;
}
@Override
public int getRunStart(TAttributedCharacterIterator.Attribute attribute) {
if (attributesAllowed != null && !attributesAllowed.contains(attribute)) {
return begin;
}
TArrayList<Range> ranges = (TArrayList<Range>) attrString.attributeMap.get(attribute);
if (ranges == null) {
return begin;
}
return runStart(ranges);
}
@Override
public int getRunStart(TSet<? extends Attribute> attributes) {
int start = begin;
TIterator<? extends Attribute> it = attributes.iterator();
while (it.hasNext()) {
TAttributedCharacterIterator.Attribute attribute = it.next();
int newStart = getRunStart(attribute);
if (newStart > start) {
start = newStart;
}
}
return start;
}
@Override
public char last() {
if (begin == end) {
return DONE;
}
offset = end - 1;
return attrString.text.charAt(offset);
}
@Override
public char next() {
if (offset >= (end - 1)) {
offset = end;
return DONE;
}
return attrString.text.charAt(++offset);
}
@Override
public char previous() {
if (offset == begin) {
return DONE;
}
return attrString.text.charAt(--offset);
}
@Override
public char setIndex(int location) {
if (location < begin || location > end) {
throw new IllegalArgumentException();
}
offset = location;
if (offset == end) {
return DONE;
}
return attrString.text.charAt(offset);
}
}
public TAttributedString(TAttributedCharacterIterator iterator) {
if (iterator.getBeginIndex() > iterator.getEndIndex()) {
throw new IllegalArgumentException("Invalid substring range");
}
StringBuilder buffer = new StringBuilder();
for (int i = iterator.getBeginIndex(); i < iterator.getEndIndex(); i++) {
buffer.append(iterator.current());
iterator.next();
}
text = buffer.toString();
TSet<TAttributedCharacterIterator.Attribute> attributes = iterator.getAllAttributeKeys();
if (attributes == null) {
return;
}
attributeMap = new THashMap<>((attributes.size() * 4 / 3) + 1);
TIterator<Attribute> it = attributes.iterator();
while (it.hasNext()) {
TAttributedCharacterIterator.Attribute attribute = it.next();
iterator.setIndex(0);
while (iterator.current() != TCharacterIterator.DONE) {
int start = iterator.getRunStart(attribute);
int limit = iterator.getRunLimit(attribute);
Object value = iterator.getAttribute(attribute);
if (value != null) {
addAttribute(attribute, value, start, limit);
}
iterator.setIndex(limit);
}
}
}
private TAttributedString(TAttributedCharacterIterator iterator, int start, int end, TSet<Attribute> attributes) {
if (start < iterator.getBeginIndex() || end > iterator.getEndIndex() || start > end) {
throw new IllegalArgumentException();
}
if (attributes == null) {
return;
}
StringBuilder buffer = new StringBuilder();
iterator.setIndex(start);
while (iterator.getIndex() < end) {
buffer.append(iterator.current());
iterator.next();
}
text = buffer.toString();
attributeMap = new THashMap<>((attributes.size() * 4 / 3) + 1);
TIterator<Attribute> it = attributes.iterator();
while (it.hasNext()) {
TAttributedCharacterIterator.Attribute attribute = it.next();
iterator.setIndex(start);
while (iterator.getIndex() < end) {
Object value = iterator.getAttribute(attribute);
int runStart = iterator.getRunStart(attribute);
int limit = iterator.getRunLimit(attribute);
if ((value instanceof TAnnotation && runStart >= start && limit <= end) ||
(value != null && !(value instanceof TAnnotation))) {
addAttribute(attribute, value, (runStart < start ? start : runStart) - start, (limit > end ? end
: limit) - start);
}
iterator.setIndex(limit);
}
}
}
public TAttributedString(TAttributedCharacterIterator iterator, int start, int end) {
this(iterator, start, end, iterator.getAllAttributeKeys());
}
public TAttributedString(TAttributedCharacterIterator iterator, int start, int end,
TAttributedCharacterIterator.Attribute[] attributes) {
this(iterator, start, end, new THashSet<>(TArrays.asList(attributes)));
}
public TAttributedString(String value) {
if (value == null) {
throw new NullPointerException();
}
text = value;
attributeMap = new THashMap<>(11);
}
public TAttributedString(String value, TMap<? extends TAttributedCharacterIterator.Attribute, ?> attributes) {
if (value == null) {
throw new NullPointerException();
}
if (value.length() == 0 && !attributes.isEmpty()) {
throw new IllegalArgumentException("Cannot add attributes to empty string");
}
text = value;
attributeMap = new THashMap<>((attributes.size() * 4 / 3) + 1);
TIterator<?> it = attributes.entrySet().iterator();
while (it.hasNext()) {
TMap.Entry<?, ?> entry = (TMap.Entry<?, ?>) it.next();
TArrayList<Range> ranges = new TArrayList<>(1);
ranges.add(new Range(0, text.length(), entry.getValue()));
attributeMap.put((TAttributedCharacterIterator.Attribute) entry.getKey(), ranges);
}
}
/**
* Applies a given attribute to this string.
*
* @param attribute
* the attribute that will be applied to this string.
* @param value
* the value of the attribute that will be applied to this
* string.
* @throws IllegalArgumentException
* if the length of this attributed string is 0.
* @throws NullPointerException
* if {@code attribute} is {@code null}.
*/
public void addAttribute(TAttributedCharacterIterator.Attribute attribute, Object value) {
if (null == attribute) {
throw new NullPointerException();
}
if (text.length() == 0) {
throw new IllegalArgumentException();
}
TList<Range> ranges = attributeMap.get(attribute);
if (ranges == null) {
ranges = new TArrayList<>(1);
attributeMap.put(attribute, ranges);
} else {
ranges.clear();
}
ranges.add(new Range(0, text.length(), value));
}
/**
* Applies a given attribute to the given range of this string.
*
* @param attribute
* the attribute that will be applied to this string.
* @param value
* the value of the attribute that will be applied to this
* string.
* @param start
* the start of the range where the attribute will be applied.
* @param end
* the end of the range where the attribute will be applied.
* @throws IllegalArgumentException
* if {@code start < 0}, {@code end} is greater than the length
* of this string, or if {@code start >= end}.
* @throws NullPointerException
* if {@code attribute} is {@code null}.
*/
public void addAttribute(TAttributedCharacterIterator.Attribute attribute, Object value, int start, int end) {
if (null == attribute) {
throw new NullPointerException();
}
if (start < 0 || end > text.length() || start >= end) {
throw new IllegalArgumentException();
}
if (value == null) {
return;
}
TList<Range> ranges = attributeMap.get(attribute);
if (ranges == null) {
ranges = new TArrayList<>(1);
ranges.add(new Range(start, end, value));
attributeMap.put(attribute, ranges);
return;
}
TListIterator<Range> it = ranges.listIterator();
while (it.hasNext()) {
Range range = it.next();
if (end <= range.start) {
it.previous();
break;
} else if (start < range.end || (start == range.end && value.equals(range.value))) {
Range r1 = null, r3;
it.remove();
r1 = new Range(range.start, start, range.value);
r3 = new Range(end, range.end, range.value);
while (end > range.end && it.hasNext()) {
range = it.next();
if (end <= range.end) {
if (end > range.start || (end == range.start && value.equals(range.value))) {
it.remove();
r3 = new Range(end, range.end, range.value);
break;
}
} else {
it.remove();
}
}
if (value.equals(r1.value)) {
if (value.equals(r3.value)) {
it.add(new Range(r1.start < start ? r1.start : start, r3.end > end ? r3.end : end, r1.value));
} else {
it.add(new Range(r1.start < start ? r1.start : start, end, r1.value));
if (r3.start < r3.end) {
it.add(r3);
}
}
} else {
if (value.equals(r3.value)) {
if (r1.start < r1.end) {
it.add(r1);
}
it.add(new Range(start, r3.end > end ? r3.end : end, r3.value));
} else {
if (r1.start < r1.end) {
it.add(r1);
}
it.add(new Range(start, end, value));
if (r3.start < r3.end) {
it.add(r3);
}
}
}
return;
}
}
it.add(new Range(start, end, value));
}
/**
* Applies a given set of attributes to the given range of the string.
*
* @param attributes
* the set of attributes that will be applied to this string.
* @param start
* the start of the range where the attribute will be applied.
* @param end
* the end of the range where the attribute will be applied.
* @throws IllegalArgumentException
* if {@code start < 0}, {@code end} is greater than the length
* of this string, or if {@code start >= end}.
*/
public void addAttributes(TMap<? extends TAttributedCharacterIterator.Attribute, ?> attributes, int start, int end) {
TIterator<?> it = attributes.entrySet().iterator();
while (it.hasNext()) {
TMap.Entry<?, ?> entry = (TMap.Entry<?, ?>) it.next();
addAttribute((TAttributedCharacterIterator.Attribute) entry.getKey(), entry.getValue(), start, end);
}
}
/**
* Returns an {@code AttributedCharacterIterator} that gives access to the
* complete content of this attributed string.
*
* @return the newly created {@code AttributedCharacterIterator}.
*/
public TAttributedCharacterIterator getIterator() {
return new AttributedIterator(this);
}
/**
* Returns an {@code AttributedCharacterIterator} that gives access to the
* complete content of this attributed string. Only attributes contained in
* {@code attributes} are available from this iterator if they are defined
* for this text.
*
* @param attributes
* the array containing attributes that will be in the new
* iterator if they are defined for this text.
* @return the newly created {@code AttributedCharacterIterator}.
*/
public TAttributedCharacterIterator getIterator(TAttributedCharacterIterator.Attribute[] attributes) {
return new AttributedIterator(this, attributes, 0, text.length());
}
/**
* Returns an {@code AttributedCharacterIterator} that gives access to the
* contents of this attributed string starting at index {@code start} up to
* index {@code end}. Only attributes contained in {@code attributes} are
* available from this iterator if they are defined for this text.
*
* @param attributes
* the array containing attributes that will be in the new
* iterator if they are defined for this text.
* @param start
* the start index of the iterator on the underlying text.
* @param end
* the end index of the iterator on the underlying text.
* @return the newly created {@code AttributedCharacterIterator}.
*/
public TAttributedCharacterIterator getIterator(TAttributedCharacterIterator.Attribute[] attributes, int start,
int end) {
return new AttributedIterator(this, attributes, start, end);
}
}

View File

@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.text;
public interface TCharacterIterator extends Cloneable {
public static final char DONE = '\uffff';
public Object clone();
public char current();
public char first();
public int getBeginIndex();
public int getEndIndex();
public int getIndex();
public char last();
public char next();
public char previous();
public char setIndex(int location);
}

View File

@ -0,0 +1,288 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.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.
*/
package org.teavm.classlib.java.text;
import org.teavm.classlib.impl.unicode.CLDRHelper;
import org.teavm.classlib.impl.unicode.DateFormatCollection;
import org.teavm.classlib.java.util.*;
public abstract class TDateFormat extends TFormat {
protected TCalendar calendar;
public final static int DEFAULT = 2;
public final static int FULL = 0;
public final static int LONG = 1;
public final static int MEDIUM = 2;
public final static int SHORT = 3;
public final static int ERA_FIELD = 0;
public final static int YEAR_FIELD = 1;
public final static int MONTH_FIELD = 2;
public final static int DATE_FIELD = 3;
public final static int HOUR_OF_DAY1_FIELD = 4;
public final static int HOUR_OF_DAY0_FIELD = 5;
public final static int MINUTE_FIELD = 6;
public final static int SECOND_FIELD = 7;
public final static int MILLISECOND_FIELD = 8;
public final static int DAY_OF_WEEK_FIELD = 9;
public final static int DAY_OF_YEAR_FIELD = 10;
public final static int DAY_OF_WEEK_IN_MONTH_FIELD = 11;
public final static int WEEK_OF_YEAR_FIELD = 12;
public final static int WEEK_OF_MONTH_FIELD = 13;
public final static int AM_PM_FIELD = 14;
public final static int HOUR1_FIELD = 15;
public final static int HOUR0_FIELD = 16;
public final static int TIMEZONE_FIELD = 17;
protected TDateFormat() {
}
@Override
public Object clone() {
TDateFormat clone = (TDateFormat) super.clone();
clone.calendar = (TCalendar) calendar.clone();
return clone;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof TDateFormat)) {
return false;
}
TDateFormat dateFormat = (TDateFormat) object;
return calendar.getFirstDayOfWeek() == dateFormat.calendar.getFirstDayOfWeek() &&
calendar.getMinimalDaysInFirstWeek() == dateFormat.calendar.getMinimalDaysInFirstWeek() &&
calendar.isLenient() == dateFormat.calendar.isLenient();
}
@Override
public final StringBuffer format(Object object, StringBuffer buffer, TFieldPosition field) {
if (object instanceof TDate) {
return format((TDate) object, buffer, field);
}
if (object instanceof Number) {
return format(new TDate(((Number) object).longValue()), buffer, field);
}
throw new IllegalArgumentException();
}
public final String format(TDate date) {
return format(date, new StringBuffer(), new TFieldPosition(0)).toString();
}
public abstract StringBuffer format(TDate date, StringBuffer buffer, TFieldPosition field);
public static TLocale[] getAvailableLocales() {
return TLocale.getAvailableLocales();
}
public TCalendar getCalendar() {
return calendar;
}
public static TDateFormat getDateInstance() {
return getDateInstance(DEFAULT);
}
public static TDateFormat getDateInstance(int style) {
return getDateInstance(style, TLocale.getDefault());
}
public static TDateFormat getDateInstance(int style, TLocale locale) {
return new TSimpleDateFormat(getDateFormatString(style, locale), locale);
}
private static String getDateFormatString(int style, TLocale locale) {
DateFormatCollection formats = CLDRHelper.resolveDateFormats(locale.getLanguage(), locale.getCountry());
switch (style) {
case SHORT:
return formats.getShortFormat();
case MEDIUM:
return formats.getMediumFormat();
case LONG:
return formats.getLongFormat();
case FULL:
return formats.getFullFormat();
default:
throw new IllegalArgumentException("Unknown style: " + style);
}
}
public static TDateFormat getDateTimeInstance() {
return getDateTimeInstance(DEFAULT, DEFAULT);
}
public static TDateFormat getDateTimeInstance(int dateStyle, int timeStyle) {
return getDateTimeInstance(dateStyle, timeStyle, TLocale.getDefault());
}
public static TDateFormat getDateTimeInstance(int dateStyle, int timeStyle, TLocale locale) {
String pattern = getDateTimeFormatString(Math.max(dateStyle, timeStyle), locale);
pattern = pattern.replace("{0}", getTimeFormatString(dateStyle, locale))
.replace("{1}", getDateFormatString(timeStyle, locale));
return new TSimpleDateFormat(pattern, locale);
}
public static String getDateTimeFormatString(int style, TLocale locale) {
DateFormatCollection formats = CLDRHelper.resolveDateTimeFormats(locale.getLanguage(), locale.getCountry());
switch (style) {
case SHORT:
return formats.getShortFormat();
case MEDIUM:
return formats.getMediumFormat();
case LONG:
return formats.getLongFormat();
case FULL:
return formats.getFullFormat();
default:
throw new IllegalArgumentException("Unknown style: " + style);
}
}
public final static TDateFormat getInstance() {
return getDateTimeInstance(SHORT, SHORT);
}
static String getStyleName(int style) {
String styleName;
switch (style) {
case SHORT:
styleName = "SHORT";
break;
case MEDIUM:
styleName = "MEDIUM";
break;
case LONG:
styleName = "LONG";
break;
case FULL:
styleName = "FULL";
break;
default:
styleName = "";
}
return styleName;
}
public final static TDateFormat getTimeInstance() {
return getTimeInstance(DEFAULT);
}
public static TDateFormat getTimeInstance(int style) {
return getTimeInstance(style, TLocale.getDefault());
}
public static TDateFormat getTimeInstance(int style, TLocale locale) {
return new TSimpleDateFormat(getTimeFormatString(style, locale), locale);
}
private static String getTimeFormatString(int style, TLocale locale) {
DateFormatCollection formats = CLDRHelper.resolveTimeFormats(locale.getLanguage(), locale.getCountry());
switch (style) {
case SHORT:
return formats.getShortFormat();
case MEDIUM:
return formats.getMediumFormat();
case LONG:
return formats.getLongFormat();
case FULL:
return formats.getFullFormat();
default:
throw new IllegalArgumentException("Unknown style: " + style);
}
}
@Override
public int hashCode() {
return calendar.getFirstDayOfWeek() + calendar.getMinimalDaysInFirstWeek() +
(calendar.isLenient() ? 1231 : 1237);
}
public boolean isLenient() {
return calendar.isLenient();
}
public TDate parse(String string) throws TParseException {
TParsePosition position = new TParsePosition(0);
TDate date = parse(string, position);
if (position.getIndex() == 0) {
throw new TParseException("Unparseable date" + string, position.getErrorIndex());
}
return date;
}
public abstract TDate parse(String string, TParsePosition position);
@Override
public Object parseObject(String string, TParsePosition position) {
return parse(string, position);
}
public void setCalendar(TCalendar cal) {
calendar = cal;
}
public void setLenient(boolean value) {
calendar.setLenient(value);
}
public static class Field extends TFormat.Field {
private static THashMap<Integer, Field> table = new THashMap<>();
public final static Field ERA = new Field("era", TCalendar.ERA);
public final static Field YEAR = new Field("year", TCalendar.YEAR);
public final static Field MONTH = new Field("month", TCalendar.MONTH);
public final static Field HOUR_OF_DAY0 = new Field("hour of day", TCalendar.HOUR_OF_DAY);
public final static Field HOUR_OF_DAY1 = new Field("hour of day 1", -1);
public final static Field MINUTE = new Field("minute", TCalendar.MINUTE);
public final static Field SECOND = new Field("second", TCalendar.SECOND);
public final static Field MILLISECOND = new Field("millisecond", TCalendar.MILLISECOND);
public final static Field DAY_OF_WEEK = new Field("day of week", TCalendar.DAY_OF_WEEK);
public final static Field DAY_OF_MONTH = new Field("day of month", TCalendar.DAY_OF_MONTH);
public final static Field DAY_OF_YEAR = new Field("day of year", TCalendar.DAY_OF_YEAR);
public final static Field DAY_OF_WEEK_IN_MONTH = new Field("day of week in month",
TCalendar.DAY_OF_WEEK_IN_MONTH);
public final static Field WEEK_OF_YEAR = new Field("week of year", TCalendar.WEEK_OF_YEAR);
public final static Field WEEK_OF_MONTH = new Field("week of month", TCalendar.WEEK_OF_MONTH);
public final static Field AM_PM = new Field("am pm", TCalendar.AM_PM);
public final static Field HOUR0 = new Field("hour", TCalendar.HOUR);
public final static Field HOUR1 = new Field("hour 1", -1);
public final static Field TIME_ZONE = new Field("time zone", -1);
private int calendarField = -1;
protected Field(String fieldName, int calendarField) {
super(fieldName);
this.calendarField = calendarField;
if (calendarField != -1 && table.get(new Integer(calendarField)) == null) {
table.put(new Integer(calendarField), this);
}
}
public int getCalendarField() {
return calendarField;
}
public static Field ofCalendarField(int calendarField) {
if (calendarField < 0 || calendarField >= TCalendar.FIELD_COUNT) {
throw new IllegalArgumentException();
}
return table.get(new Integer(calendarField));
}
}
}

Some files were not shown because too many files have changed in this diff Show More