This article is published by the Cloud + community

Author: Yin Yuan, focusing on mobile client development, special prize winner of Microsoft Imagine Cup China region

JavaScript is becoming more and more visible in our client-side development, and from ReactNative to JSpatch, the combination of JavaScript and client-side technology is becoming increasingly attractive. This article focuses on the JavaScriptCore framework in iOS, which provides iOS with the ability to execute JavaScript code. The technology of the future is changing fast, and JavaScript and iOS are colliding with new passions.

JavaScriptCore is a JavaScript virtual machine that provides underlying resources for JavaScript execution.

A, JavaScript,

Before we can discuss JavaScriptCore, we must first understand JavaScript.

1. What does JavaScript do?

  • Put it on a grand scale: a prototype-based, function-first high-level programming language that is, by interpretation, a literal translation of dynamic typing. Is a multiparadigm language that supports object-oriented, imperative, and functional programming.
  • In layman’s terms: mainly used for web pages to provide dynamic interaction capabilities. Embed dynamic text in HTML pages, respond to browser events, read and write HTML elements, control cookies, etc.
  • More colloquial: Grab mooncakes, button.click(). (PS: Please use the while loop carefully)

2. Origin and history of JavaScript

  • At the end of 1990, Tim Berners-Lee, a scientist at the European Nuclear Energy Research Organization (CERN), created the World Wide Web, based on the Internet, allowing people to browse Web files.
  • In December 1994, Netscape released a new version of its browser, Navigator 1.0, aimed at ordinary users, and it took over 90% of the market.
  • In 1995, Netscape hired programmer Brendan Eich to develop the scripting language for embedding web pages. Originally called Mocha, it was changed to LiveScript in September 1995.
  • In December 1995, Netscape struck a deal with Sun, which allowed the language to be called JavaScript.

3. The JavaScript and ECMAScript

  • “JavaScript” is a registered trademark used by Sun to customize netscape’s (now Mozilla) implementation of the language. Netscape submitted the language as a standard to ECMA, the European Computer Manufacturers Association. Due to trademark conflicts, the standard version of the language was given the ugly name “ECMAScript”. Also due to trademark conflicts, Microsoft gave the implementation version of the language the widely known name “Jscript”.
  • As a JavaScript standard, ECMAScript is generally considered to be an implementation of the former.

4. Java and JavaScript

Lei Feng and Leifeng Pagoda

Java and JavaScript are two different programming languages. It’s generally believed that Netscape named LiveScript JavaScript because Java was the most popular programming language at the time, The “Java” name helped spread the nascent language.

Second, the JavaScriptCore

1. Browser evolution

  • Evolutionary complete diagram

Upload.wikimedia.org/wikipedia/c…

  • Its branches

The two main WebKit browsers now use Sfari and Chromium (Chorme’s open source project). WebKit originated as an offshoot of KDE’s open source project Konqueror and was used by Apple for the Sfari browser. One branch developed into the Kernel of Chorme, and Google developed a new Blink kernel based on this in 2013.

2. WebKit layout engine

Webkit is a typesetting engine for browsers such as Sfari and Chrome

  • Webkit Embedding API is the API interface for webpage interaction with browser UI.
  • PlatformAPI provides interaction with underlying drivers such as the network, font rendering, audio file decoding, rendering engine, etc.
  • WebCore it realizes the model of the document, including CSS, DOM, Render and so on;
  • JSCore is a specialized engine for JavaScript scripting;

3. The JavaScript engine

  • JavaScript engines are virtual machines that specialize in JavaScript scripts and are typically shipped with web browsers. The first JavaScript engine was developed by Brandon Eck at Netscape for use in the Netscape Navigator web browser. JavaScriptCore is a JavaScript engine.
  • Here are the main JavaScript engines currently under development

4. JavaScriptCore

JavaScriptCore consists of the following modules:

  • Lexer lexical analyzer that breaks the script source code into a series of tokens
  • Parser, which processes tokens and generates the corresponding syntax tree
  • LLInt low-level interpreter that executes binary code generated by Parser
  • JIT (just in time)
  • DFG low latency optimized JIT
  • JIT for FTL high throughput optimization

For more JavaScriptCore implementation details, reference trac.webkit.org/wiki/JavaSc…

5. JavaScriptCore

JavaScriptCore is an open source project implemented by C++. With the JavaScriptCore framework provided by Apple, you can execute Javascript code in objective-C or C-based programs and insert custom objects into the Javascript environment. JavaScriptCore is available directly from iOS 7.0.

In javascriptCore.h, we can see this

#ifndef JavaScriptCore_h #define JavaScriptCore_h #include <JavaScriptCore/JavaScript.h> #include <JavaScriptCore/JSStringRefCF.h> #if defined(__OBJC__) && JSC_OBJC_API_ENABLED #import "JSContext.h" #import "JSValue.h"  #import "JSManagedValue.h" #import "JSVirtualMachine.h" #import "JSExport.h" #endif #endif /* JavaScriptCore_h */Copy the code

The main JavaScriptCore classes are clearly listed here:

  • JSContext
  • JSValue
  • JSManagedValue
  • JSVirtualMachine
  • JSExport

We’ll look at each of these classes in turn.

6. Hello World!

This code shows you how to execute a piece of JavaScript code in Objective-C and get the return value and convert it to OC data printing. Okay

// Create a VM
JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];

// Create context
JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];

// Execute the JavaScript code and get the return value
JSValue *value = [context evaluateScript:@"1 + 2 * 3"];

// Convert to OC data and print
NSLog(@"value = %d", [value toInt32]);

Output

value = 7
Copy the code

Third, JSVirtualMachine

An instance of JSVirtualMachine is a complete and independent JavaScript execution environment, providing underlying resources for JavaScript execution.

This class does two things:

  1. Implement concurrent JavaScript execution
  2. Memory management for JavaScript and Objective-C bridge objects

Take a look at what’s in the svirtualmachine. h header:

NS_CLASS_AVAILABLE(10_9, 7_0)
@interface JSVirtualMachine : NSObject

/* Create a new fully independent virtual machine */
(instancetype)init;

/* Memory management for bridge objects */
- (void)addManagedReference:(id)object withOwner:(id)owner;

/* Cancel memory management for the bridge object */
- (void)removeManagedReference:(id)object withOwner:(id)owner;

@end
Copy the code

Each JavaScript context (JSContext object) belongs to a virtual machine (JSVirtualMachine). Each virtual machine can contain multiple different contexts and allow values (JSValue objects) to be passed between these different contexts.

However, each virtual machine is complete and independent, with its own heap space and garbage collector. GC cannot process objects in another virtual machine’s heap, so you cannot pass values created in one virtual machine to another.

Concurrent execution of threads and JavaScript

JavaScriptCore apis are thread-safe. You can create a JSValue or execute JS code in any thread, however, all other threads that want to use the virtual machine will have to wait.

  • If you want to execute JS concurrently, you need to use multiple different virtual machines to do so.
  • JS code can be executed in child threads.

Use the following demo to understand this concurrency mechanism

JSContext *context = [[CustomJSContext alloc] init];
JSContext *context1 = [[CustomJSContext alloc] init];
JSContext *context2 = [[CustomJSContext alloc] initWithVirtualMachine:[context virtualMachine]];
NSLog(@"start");
dispatch_async(queue, ^{
    while (true) {
        sleep(1);
        [context evaluateScript:@"log('tick')"]; }}); dispatch_async(queue1, ^{while (true) {
        sleep(1);
        [context1 evaluateScript:@"log('tick_1')"]; }}); dispatch_async(queue2, ^{while (true) {
        sleep(1);
        [context2 evaluateScript:@"log('tick_2')"]; }}); [context evaluateScript:@"sleep(5)"];
NSLog(@"end");
Copy the code

Context and Context2 belong to the same VM.

Context1 belongs to another VM.

Three threads each asynchronously execute js logs once per second, first sleeping for 1 second.

Execute a JS function on context that sleeps for 5 seconds.

The JS function should be executed first, which sleeps for 5 seconds, during which time all other calls to the virtual machine where the context is located will be in a waiting state, so tick and tick_2 will not be executed for the first 5 seconds.

However, tick_1 can still be executed on the VM where Context1 is located.

Tick and Tick_2 will be executed only after the 5-second hibernation is complete (the sequence is not guaranteed).

The log output from the actual run is:

start
tick_1
tick_1
tick_1
tick_1
end
tick
tick_2
Copy the code

Four, JSContext

A JSContext object represents a JavaScript execution environment. In native code, JSContext is used to execute JS code, access values defined or calculated in JS, and enable JavaScript to access native objects, methods and functions.

1. JSContext executes JS code

  • Call the evaluateScript function to execute a sectiontop-levelAnd can add functions and object definitions to the global object
  • The return value is the last value generated in the JavaScript code

API Reference

NS_CLASS_AVAILABLE(10_9, 7_0)
@interface JSContext : NSObject

/* Create a JSContext and create a new JSVirtualMachine */
(instancetype)init;

/* Create a JSContext */ on the specified virtual machine
(instancetype)initWithVirtualMachine:
        (JSVirtualMachine*)virtualMachine;

/* Executes a piece of JS code that returns the last generated value */
(JSValue *)evaluateScript:(NSString *)script;

/* executes a piece of JS code and identifies the sourceURL as its sourceURL (for markup only) */
- (JSValue *)evaluateScript:(NSString *)script withSourceURL:(NSURL*)sourceURL     NS_AVAILABLE(10_10, 8_0);

/* Get the context of the currently executing JavaScript code */
+ (JSContext *)currentContext;

/* Get the currently executing JavaScript function*/
+ (JSValue *)currentCallee NS_AVAILABLE(10_10, 8_0);

/* Get this */ of the currently executing JavaScript code
+ (JSValue *)currentThis;

/* Returns the arguments to the current native callback from JavaScript code.*/
+ (NSArray *)currentArguments;

/* Get the global object of the current context. The context in WebKit returns the WindowProxy object */
@property (readonly, strong) JSValue *globalObject;

@property (strong) JSValue *exception;
@property (copy) void(^exceptionHandler)(JSContext *context, JSValue
    *exception);

@property (readonly, strong) JSVirtualMachine *virtualMachine;

@property (copy) NSString *name NS_AVAILABLE(10_10, 8_0);


@end
Copy the code

2. JSContext accesses the JS object

A JSContext object corresponds to a global object. For example, JSContext in a Web browser, whose global object is the Window object. In other environments, global objects play a similar role in distinguishing the scope of different JavaScript contexts. Global variables are properties of global objects and can be accessed either by JSValue objects or context subscripts.

A word against the code:

JSValue *value = [context evaluateScript:@"var a = 1+2*3;"] ; NSLog(@"a = %@", [context objectForKeyedSubscript:@"a"]); NSLog(@"a = %@", [context.globalObject objectForKeyedSubscript:@"a"]); NSLog(@"a = %@", context[@"a"]); / Output: a = 7 a = 7 a = 7Copy the code

Here are three ways to access JavaScript objects

  • ObjectForKeyedSubscript through the context instance method
  • ObjectForKeyedSubscript instance method in Context.GlobalObject
  • By subscript

Setting properties also corresponds.

API Reference

/* Provides subscript access to elements for JSContext */
@interface JSContext (SubscriptSupport)

/* First converts the key into a JSValue object, and then uses that value to look for the name's properties in the global object of the JavaScript context and return */
(JSValue *)objectForKeyedSubscript:(id)key;

/* First turn the key into a JSValue object, and then use that value to set the property in the global object of the JavaScript context. You can use this method to bridge objects or methods in native to JavaScript calls */
(void)setObject:(id)object forKeyedSubscript:(NSObject <NSCopying>*)key;

@end



/* For example: the following code creates an Objective-C block function */ in JavaScript
context[@"makeNSColor"] = ^(NSDictionary *rgb){
    float r = [rgb[@"red"] floatValue];
    float g = [rgb[@"green"] floatValue];
    float b = [rgb[@"blue"] floatValue];
    return [NSColor colorWithRed:(r / 255.f) green:(g / 255.f) blue:(b / 255.f)         alpha:1.0];
};
JSValue *value = [context evaluateScript:@"makeNSColor({red:12, green:23, blue:67})"];
Copy the code

Fifth, JSValue

A JSValue instance is a reference to a JavaScript value. Use the JSValue class to convert some basic types of data (such as values and strings) between JavaScript and native code. You can also use this class to create JavaScript objects that wrap native objects of your custom classes, or to create JavaScript functions implemented by native methods or blocks.

Each JSValue instance comes from a JSContext object that represents the JavaScript execution environment, which contains the corresponding value of the JSValue. Each JSValue object holds a strong reference to its JSContext object, and as long as any JSValue associated with a particular JSContext is retained, the JSContext will survive. Any other JSValue object returned by calling the JSValue instance method belongs to the same JSContext as the original JSValue.

Each JSValue is indirectly associated with a specific JSVirtualMachine object that represents the execution resource base through its JSContext. You can only pass one JSValue object to a JSValue or JSContext instance method that is managed by the same vm (host). If you try to pass JSValue from one virtual machine to another, an Objective-C exception is raised.

1. JSValue type conversion

JSValue provides a series of methods to convert native and JavaScript data types to and from each other:

NSDictionary and JS objects

The NSDictionary object and the keys it contains convert to and from properties in JavaScript that correspond to names. The value of the key is also copied and transformed recursively.

[context evaluateScript:@"var color = {red:230, green:90, blue:100}"];

//js->native to show you my colors
JSValue *colorValue = context[@"color"];
NSLog(@"r=%@, g=%@, b=%@", colorValue[@"red"], colorValue[@"green"], colorValue[@"blue"]);
NSDictionary *colorDic = [colorValue toDictionary];
NSLog(@"r=%@, g=%@, b=%@", colorDic[@"red"], colorDic[@"green"], colorDic[@"blue"]);

//native->js
context[@"color"] = @ {@"red": @ (0), @"green": @ (0), @"blue": @ (0)};
[context evaluateScript:@"log('r:'+color.red+'g:'+color.green+' b:'+color.blue)"];
Output:

r=230, g=90, b=100
r=230, g=90, b=100
r:0 g:0 b:0
Copy the code

As you can see, JS objects can be directly converted to Objective-C NSDictionary, and NSDictionary can also be used as objects when passed into JavaScript.

3. NSArray and JS arrays

The NSArray object rotates with the array in JavaScript. The child elements are also copied and transformed recursively.

[context evaluateScript:@ "var Friends = ['Alice','Jenny','XiaoMing']"]; Js ->native which do you say is true love? JSValue *friendsValue = context[@"friends"]; NSLog(@"%@, %@, %@", friendsValue[0], friendsValue[1], friendsValue[2]); NSArray *friendsArray = [friendsValue toArray]; NSLog(@"%@, %@, %@", friendsArray[0], friendsArray[1], friendsArray[2]); [@" singledom "] = @[friendsArray[2], @" singledom "]; [context evaluateScript:@"log('girlFriends :'+girlFriends[0]+' '+girlFriends[1])"];Copy the code

Output:

Alice, Jenny, XiaoMing
Alice, Jenny, XiaoMing
girlFriends : XiaoMing Jimmy
Copy the code

4. Block/ function and JS function

Blocks in Objective-C are converted to function objects in JavaScript. Parameters and return types are converted using the same rules.

Converting a JavaScript function that represents a native block or method will result in that block or method.

The other JavaScript functions will be converted to an empty dictionary. Because JavaScript functions are also objects.

5. OC and JS objects

For all other Native object types, JavaScriptCore creates a Wrapper object with a Constructor prototype chain that reflects the inheritance of native types. By default, properties and methods of native objects are not exported to their corresponding JavaScript Wrapper objects. You can optionally export properties and methods through the JSExport protocol.

More on object type conversions later.

This article has been published by Tencent Cloud + community in various channels

For more fresh technology dry goods, you can follow usTencent Cloud technology community – Cloud Plus community official number and Zhihu organization number